diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 15f949544c..d227113e2c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @lambdaclass/zk_research_and_development @schouhy @ajgara +* @lambdaclass/zk_research_and_development diff --git a/Cargo.toml b/Cargo.toml index d97f54b1eb..8ae4e92f74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,18 +4,29 @@ exclude = ["ensure-no_std"] resolver = "2" [workspace.package] -version = "0.2.0" +version = "0.4.0" edition = "2021" license = "Apache-2.0" repository = "https://github.com/lambdaclass/lambdaworks" [workspace.dependencies] iai-callgrind = "0.3.1" -lambdaworks-crypto = { path = "./crypto", version = "0.2.0" } -lambdaworks-gpu = { path = "./gpu", version = "0.2.0" } -lambdaworks-math = { path = "./math", version = "0.2.0" } -stark-platinum-prover = { path = "./provers/stark", version = "0.2.0" } -cairo-platinum-prover = { path = "./provers/cairo", version = "0.2.0" } +lambdaworks-crypto = { path = "./crypto", version = "0.4.0" } +lambdaworks-gpu = { path = "./gpu", version = "0.4.0" } +lambdaworks-math = { path = "./math", version = "0.4.0" } +stark-platinum-prover = { path = "./provers/stark", version = "0.4.0" } +cairo-platinum-prover = { path = "./provers/cairo", version = "0.4.0" } + +[patch.crates-io] +winter-air = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +winter-prover = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +winter-math = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +winter-utils = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +winter-crypto = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4"} +miden-air = { git = "https://github.com/lambdaclass/miden-vm" } +miden-core = { git = "https://github.com/lambdaclass/miden-vm" } +miden-assembly = { git = "https://github.com/lambdaclass/miden-vm" } +miden-processor = { git = "https://github.com/lambdaclass/miden-vm" } [profile.bench] lto = true diff --git a/README.md b/README.md index 981fabc6f5..3ca089854c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,33 @@ # LambdaWorks -From the heights of these towers of fields, forty centuries of mathematics look down on us. The library for kids who wanna learn how to do STARKs, SNARKs and learn other cryptographic stuff too. -
+The library for kids who wanna learn how to do STARKs, SNARKs and learn other cryptographic stuff too. + +> From the heights of these towers of fields, forty centuries of mathematics look down on us. + +This library provides efficient implementation of cryptographic primitives used to build proving systems. Along with it, many backends for proving systems are shipped, and compatibility with different frontends is supported. + +- [Our vision on ZKP](https://blog.lambdaclass.com/transforming-the-future-with-zero-knowledge-proofs-fully-homomorphic-encryption-and-new-distributed-systems-algorithms/) +- [Lambda Crypto Doctrine](https://blog.lambdaclass.com/lambda-crypto-doctrine/) +## Table of contents +
+ + +- [LambdaWorks](#lambdaworks) + - [Documentation](#documentation) + - [List of features](#list-of-features) + - [Main crates](#main-crates) + - [Crypto](#crypto) + - [Examples - mini apps](#examples---mini-apps) + - [Exercises and Challenges](#exercises-and-challenges) + - [Why did we build lambdaworks](#why-did-we-build-lambdaworks) + - [Additional tooling usage](#additional-tooling-usage) + - [Fuzzers](#fuzzers) + - [Documentation building](#documentation-building) + - [๐Ÿ“Š Benchmarks](#-benchmarks) + - [๐Ÿ“š References](#-references) + + [![Telegram Chat][tg-badge]][tg-url] [![codecov](https://img.shields.io/codecov/c/github/lambdaclass/lambdaworks)](https://codecov.io/gh/lambdaclass/lambdaworks) @@ -14,6 +39,7 @@ From the heights of these towers of fields, forty centuries of mathematics look ## [Documentation](https://lambdaclass.github.io/lambdaworks) ## List of features + Disclaimer: This list contains cryptographic primitives and mathematical structures that we want to support in Lambdaworks. It can be expanded later to include new primitives. If you find there is a mistake or there has been an update in another library, please let us know. List of symbols: @@ -35,8 +61,8 @@ List of symbols: | BLS12-381 | :heavy_check_mark: | :heavy_check_mark: | | | | | BLS12-377 | ๐Ÿ—๏ธ | :heavy_check_mark: | | :heavy_check_mark: | | | BN-254 | :x: | :heavy_check_mark: | | | | -| Pallas | :x: | :heavy_check_mark: | | | | -| Vesta | :x: | :heavy_check_mark: | | | | +| Pallas | :heavy_check_mark: | :heavy_check_mark: | | | | +| Vesta | :heavy_check_mark: | :heavy_check_mark: | | | | | Bandersnatch | ๐Ÿ—๏ธ | :heavy_check_mark: | | | | | **STARKs** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | | STARK Prover | :heavy_check_mark: | :x: | | :x: | | @@ -58,16 +84,26 @@ List of symbols: | Protostar | :x: | | | | | | Protogalaxy | :x: | | | | | +Additionally, provers are compatible with the following frontends and VMs: -Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask. - -So, we decided to build our library, focusing on performance, with clear documentation and developer-focused. Our core team is a group of passionate people from different backgrounds and different strengths; we think that the whole is greater than just the addition of the parts. We don't want to be a compilation of every research result in the ZK space. We want this to be a library that can be used in production, not just in academic research. We want to offer developers the main building blocks and proof systems so that they can build their applications on top of this library. +| Backend | Frontend | Status | +|---------|----------|--------| +| Groth16 | Arkworks | :heavy_check_mark: | +| Groth16 | Gnark | :x: | +| Groth16 | Circom | ๐Ÿ—๏ธ | +| Plonk | Gnark | ๐Ÿ—๏ธ | +| Plonk | Noir | :x: | +| Stark | Winterfell | :heavy_check_mark: | +| Stark | Miden | :heavy_check_mark: | +| Stark | Cairo | :heavy_check_mark: | +This can be used in a multi prover setting for extra security, or as a standalone to be used with Rust. ## Main crates - [Math](https://github.com/lambdaclass/lambdaworks/tree/main/math) - [Crypto primitives](https://github.com/lambdaclass/lambdaworks/tree/main/crypto) +- [STARK Prover](https://github.com/lambdaclass/lambdaworks/tree/main/provers/stark) - [Plonk Prover](https://github.com/lambdaclass/lambdaworks/tree/main/provers/plonk) - [Cairo Prover](https://github.com/lambdaclass/lambdaworks/tree/main/provers/cairo) - [Groth 16](https://github.com/lambdaclass/lambdaworks/tree/main/provers/groth16) @@ -77,6 +113,7 @@ If you are interested in proving Cairo programs, use the Cairo Prover CLI. ### Crypto - [Elliptic curves](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/elliptic_curve) - [Multiscalar multiplication](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/msm) +- [Hashes](https://github.com/lambdaclass/lambdaworks/tree/main/crypto/src/hash) Finite Field crate fully supports no-std with `no-default-features` @@ -87,6 +124,9 @@ Both Math and Crypto support wasm with target `wasm32-unknown-unknown` by defaul ## Exercises and Challenges - [Lambdaworks exercises and challenges](https://github.com/lambdaclass/lambdaworks_exercises/tree/main) +- [Roadmap for Sparkling Water Bootcamp](https://github.com/lambdaclass/sparkling_water_bootcamp/blob/main/README.md) + +## Citing Lambdaworks If you use ```Lambdaworks``` libraries in your research projects, please cite them using the following template: @@ -99,9 +139,15 @@ If you use ```Lambdaworks``` libraries in your research projects, please cite th } ``` -### Gadgets +## Why we built Lambdaworks + +Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask. + +So, we decided to build our library, focusing on performance, with clear documentation and developer-focused. Our core team is a group of passionate people from different backgrounds and different strengths; we think that the whole is greater than just the addition of the parts. We don't want to be a compilation of every research result in the ZK space. We want this to be a library that can be used in production, not just in academic research. We want to offer developers the main building blocks and proof systems so that they can build their applications on top of this library. + +## Additional tooling usage -## Fuzzers +### Fuzzers Fuzzers are divided between the ones that use only the CPU, the ones that use Metal, and the ones that use CUDA. @@ -124,7 +170,7 @@ Run a specific fuzzer from the ones contained in **fuzz/fuzz_targets/** folder w make run-fuzzer FUZZER=field_from_hex ``` -## Documentation +### Documentation building To serve the documentation locally, first install both [mdbook](https://rust-lang.github.io/mdBook/guide/installation.html) and the [Katex preprocessor](https://github.com/lzanini/mdbook-katex#getting-started) to render LaTeX, then run diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 8261ee1966..adb02143a9 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -13,6 +13,7 @@ sha3 = "0.10" sha2 = "0.10" thiserror = "1.0.38" serde = { version = "1.0", features = ["derive"] } +rayon = { version = "1.8.0", optional = true } [dev-dependencies] criterion = "0.4" @@ -21,6 +22,7 @@ rand = "0.8.5" [features] test_fiat_shamir = [] +parallel = ["dep:rayon"] [[bench]] name = "criterion_merkle" diff --git a/crypto/src/commitments/kzg.rs b/crypto/src/commitments/kzg.rs index 7abc65efba..fb33cb0ffd 100644 --- a/crypto/src/commitments/kzg.rs +++ b/crypto/src/commitments/kzg.rs @@ -200,7 +200,7 @@ impl>, P &(alpha_g2.operate_with(&(g2.operate_with_self(x.representative())).neg())), ), ]); - e == FieldElement::one() + e == Ok(FieldElement::one()) } fn open_batch( diff --git a/crypto/src/hash/poseidon/bls12381/t2/mds_matrix.csv b/crypto/src/hash/poseidon/bls12381/t2/mds_matrix.csv deleted file mode 100644 index ae5dd836bc..0000000000 --- a/crypto/src/hash/poseidon/bls12381/t2/mds_matrix.csv +++ /dev/null @@ -1,3 +0,0 @@ -1054aa308cbc55d67054b451780938637384c4516c1efde724244827b4a3f517da7dafc71176d4d0d8a389ad443d87bc,193dd99b435bdca5e54d9628f98cf64b4ef15b4e2932f4aaced0306790038fe2a24a129e873b775603e9bcb62d2eb572,197d34c530289f1ee0c78c2549e7b588c17a92540bc14097515c84ca3a1cc5c11c0f28d8dc5a1c45023c79a753fdfe7f -028eb098a4742d219569a8e6a5aa351492f8f10167c20ecb7d772463dcf7235320bcd6c3f573b4886354bd6dc6d142bd,02acacfd528c4a6581b88f99d319114aa570e09304469f24c922e11d53a233b91ede1db5d56bcf351b0c7273d236dc2a,09739ef37c6cd264756f56cc854698c909e36260dab6ba99c0f9f966e6e620c0f3af8c9d9d39259a4772c0ddb846caf0 -0e04c931c0ca356d46e3ead73b18c96b3d0b18d22ed1f65c6083be79a489c4bd37304725f8ee11007cb37f2eaf1ccee1,17f594a2ad8bf7a10bd730eaf750db78dc067b8d79cdf61d0218a9c2dc72986795f6dab8f3bf4512d3cf419146b31266,09605fc485a538747637ede019ed67c220d6e6773430033ca6ab7d7e2df4587ebce624cf90da513633af537ef47fdffa diff --git a/crypto/src/hash/poseidon/bls12381/t2/round_constants.csv b/crypto/src/hash/poseidon/bls12381/t2/round_constants.csv deleted file mode 100644 index b609c7420a..0000000000 --- a/crypto/src/hash/poseidon/bls12381/t2/round_constants.csv +++ /dev/null @@ -1 +0,0 @@ -154f2457ef0af5d8e80201b00fb2e7651c26e75c552c2bc71f25de7436423909b6431e4c87276409028bdb84cfea977b,17330c2341e58e3255d9f0bbb7d3fee3bb30b80ebebae1f456f6e8b4229c09fffd7052dbc85863015d27a43217f11e3f,05d4688ba8f620e38eeb1f3a1461c89611aadfbff11be1bfa469f461b3c10831d1ca2c237f8a93b0adc90ec14c6df22b,0664a69aa7c2864176cf758dcd71fb255eb5320e93cdabfb32cbcc78d61e913e93ce180bc31041da2930b27c6c91be78,080035cc109d5aab327d8177ed36b620f6e62b598df5c326f87a6bddb9fc301bbfc6f63330037e1a3933b2c68b8d742c,0e46a18007cff8a758623662dfaa6a5907508ca3fdc262b16de7f1021af3471f18073fffa8b62e4d2d65f88d6de82d08,087aa16a5b69dcf5e5dade5483b470ac1fa205c9859c0e7c73fe330cf1f253e3a079470ebd8235cb7945a7f4dd4bda87,0eaa1a2c10012c78d3cfa5449b77b16ad30a2c86b17b95fd7ec0a16041148d3a4404e242dec8ffe1545b0e51ad5aa7ba,0e3b6a0616900c3af65bb6ea9a8b2561b1593ba66d8c1df6dcbb5ff61ddc7a6901e813835804ad93f620f85244186bde,055502e89fb98360ba746aa8f1848539984677f7a92ef1dc69eb9aec78632d2fdffbef65a16e48cb141bfcfeb68b8ff9,1823c8032ea6d122aabb6907adfa011f369b3fe2101022edbc139efbd8dc393c1fd72b4956e3749bd4a05cee0e1bf2e3,16094945fa5310014f8f536673456948fae2965ad3f44cd66680f9c619cb0ab42a46e335ea39f9fd5d0e5434b22d0bae,02c5cce0e00a04d0af53f30c459e60e752f0edee8141702b402d0b04111926d8e8048c9d6d19a4178f5f39d276b6636d,0a0e9eb9c29ab145709137b90d7b784406adcf03be4d5e5cb22168d665c704eae0c59c33d7b39870d7d41ebdae3dba63,14810d3625b41d7d2915009d70f6ff79a360aeaeb4309e3a8c15c31869bfe9281778e98894dfdd3ae3676791fef00d8f,0ac64e116b047832b2d4dde1d59ca7abebd7e165729a9f92da3764c796a3722851a526ff4dd730dbd89aa024d6b90c4f,082810320ae521f905c70d58ab9a9d116025906953aa1ad2b33670e6aee784e97306dff4469aff5030238be5300c4125,06fdddeca6d088b675e48c8edbbe3c668df3486eda775851bfa7d3b30482d9996fb2558589ed56fd81881ef8aa35e463,0d2c62d0f1a1cc18cd3c0b4695da388c5fe4a5f6fa23b7e0f1adad95aa17178b64010ce2ad27952e9de13bdd0f2238f1,05980e4285f89623b4923e10bae8de36eb0790d517a1a13ee2d1e60dd7af4cf77df27202ace93f3cc2d2cd790a359f2f,0651a7a9ef748bab7fb382380615fbec324ec63b8841a762490feae653a22368255845324133fa4d712b908cf23862fe,16aa775e2376ab82ea54406cd3e86b2ae9f3f1940d09419828a47d8792872af6d319f571f8ac52b690d0edb47385adb9,0d2bf7f7d1a3ef3a28f4fd7fb0799b711f0aa0b569e0e39b35f0682041c6bf4313571fc268126803c1cd455926b9f31c,133665f56a0462062926c8758395d53e5f55c8e49b4be0ba504847a9d92da919125eae37ddda7e4f9e54e6030d37f2e6,057d3388086600f182601a3aaf874ad6c9af2e1b6e31fd907735c2a0d2f36d280fe720f7ab68f1619919a59f8034750d,06b8202bd67f29635882c73a2075d20f523f8ca3ef3e5031b749766ac28aadd0e9b42c515f539b6543972950e641c7b7,100f28254f5a526902976d9a0e04aa7c8281ef241b65fa7753609f99e2a65d4ddcf9a6f203f52eca9d8a67eaceab0368,025f8e66fdde4c9b366efb32ac27e8e6929dca3b9221ae63ec3dcc8c1bc0d6fb3faece3d63ff4ae05d93b7d58f2275cd,0e60c2cc4aefb5f9795bbddfa437c39984e7c24c85405c3bd10930fa6cbac400aa3a07de6b3eab999a706e0a7417e7f8,10149037150d9ecb855842788053b978936f376f3faf2688433c69a9bb9a59679e669c510da19b4d7835e9556b128e6c,02e1fe43ed9084dcc74865ae47d59ab7577361ef9d858e183a58081c213a21f36680d051b496ef253ffd90bf93d0b1ef,0b2ffe1b4c9498b46728e35820023a2981f0173ec0c94eb8662a9d93a74ee971a4da683d3e0333e6f0331c299eaf49ae,0365ef61b7d375f28463a1fef5a0fb6ced29471c52245569853fa806b863a0f07ec392377ac459a3554759971da01f80,0828bd548085ad65df11653813e3c7270ab11dc6605071fd1c52905842296b6eac7174c1a9efb80d7829ba6517769acf,01bc698937294df70dc04c1f0c13a191bf2d7f41edbd29a6b6198de6b128066a24663d67e6ad23525d2586602cb0aa2e,18daf786f64570af1e44d387c1819f318e31bd02b1b09eb0eef36e4273bd3346cd12f43ceeb3aa9e6e3fb7104a1acc3a,016fa665700b72fe0d9d4b588f87553cf289e0fc98f3b12aa1429c2c4f5a0f18c46f54186f2b89524e858eec1263cb66,0b67c11fc4788bb2f1517678f71e21e58a0af7cdf9cf821eb36e7fcd7fb6bba9bb57f91bcade43b11865b7147dd4a03f,144aebfa271a685a9e05ba4b98e3fba70493eda456f45c748f3cab0e671caf407e86d1ac129ffe2a7413ce794db30ed2,1247ae67946b588d139e52dcf673c67a41f7be84c2368d6e5e748f9acef42407c7f537abdc0f33d8c4e675f41d38b910,0780000fa29beb94fd07312400d57c40e55fe346c2c68400a1ed5c4c7108859150a7af8f7000d21199cb7fc292c95521,0b2b79069d11eb45b83fd1407ae8e33593844ded02dc62a79117eee92eeae7687ea9ca08eadc8778b24ae6c14582d6ed,099fc7451e0211a1d92b82fbe5b290b370b4c8627cbe9a8ff2e1480d2c520caef7ddc494ca93952d8ba6a57768214b3b,15d38e4a3eb3beec5d21c94b90c98c0c15db24b3910f26286346de7a0a59a262d75436f8b4f45d5759d3bc6284313da7,0df6e591d48b8fe773f1aa940868fede16b46339e656d462a266ab23d75874d55845751942769870ad193ce692d7e122,0733a678e5e6916419c2bd5e8b539ead4187b2af724003182b45de8dcd2e29e888d6af424c8c6ccd520fcc3d19b47c2e,12c6e68e20d7245cf6f95224cc86c1b2eba4ae9a158602d588e73adab495b48b1177798a7eac56179a72dcdb9b948ef4,0e2bf51d224aaff9b09f19e2618dd7a3483af0b1f3593b59fec160c81e51a372f38d3c1fa3668f8e031b30f50ffc6e1a,0d189eafc13611b4d52ed0fece5a1121cc71261c898bfbd0944683ea2fbf93179d6e19fee858c88532a7c0ca0fe48ce0,106ca751523f7746402b0047c4e3c22e6078d5fc846a625d1cdd6f95ab4d75d6f6effd9dfb37508e5a56127e4e5f440e,0e3d7b0b5b18a2d9757a5c537fa1bfc07ebb64e73b3fafac804bdf775030d6b3552d522210538572522021f12204436b,0ca015d9a41111738830bfc8acaec1afbf12a26da30130824d6e30d72934f2bc741324db5308ec300118f6e777d51540,091c3d10ae2deff9699870018f4475adfb1c4bb9a4760487093fef27ac91c691f3062f79593e6758004028144c55cf90,085a8ff1fb23ad3a534df006ce209df11841c0388ead4f874e30ec77c518f57f3fd80c76f91e0d5364d34c0d2c91a226,03b260e0a1428a0afb0880cb00686ea7e7774c419babd0131485b33a19419e9628b2436dc9604a5aa9a48dec14149b37,06e17d6cc8341743f331e8030b5e35acf0f31454d6c46be1834f2db3b2fb5b9d0177d3f437ddbd8d92b60c32fa41ae9f,13458b0422d3a90c0c48c7f8be74c5f6bf43c65eef8a55ed3e4a3150dd14d6e1370a13c63a7bcd9e1c9f5c89d3febb40,009307ba2dea6e150d33935a81b632d36b8a0b45cf638e2c081acf367c4f8316b1c1b5ea9057479f68f17e82c466f954,096d8122739e2ffd19ddbe7c1a7a4584bb7abd8caebc25e476068ee4e757287c4d759c50c62f380add701bb8ab10c20f,19bb476b8629da8924327afd809588d75f7800c6ef6cd7fc8709cee65ab2683ba9cbd2746a20e56af82d89d9b1ed66c5,0c4a08deb88627f5db30d42b5ed83a10180a62cf991258b9bccd142cda9c0168e3b1d5cd848d2a6a7adab709159a5133,0a107ad615c80dc81e38024de9298d60f1dc709cc8b1acecd5e0aa09dc6e48ace7ec3f3327a860ad38f1d8901933849f,146cb92f3254d4fb6ac2f1545e25a295970a0b3d05f8842fcec6f943787b9939e30eb33cf6d754511893a81e94adad7b,0f65ba85a62513668ab9e9ab5b2c5d4f0989d4e2599b5252a60dfc56105987600eb3a64bc10c75b63f3c4f9dae55810f,01efd65006fb91a94748f46dc2297d1e19519a41c75695160e6ca5e71e580486c504907b5161d3d6c61652134f7bcdca,0d43ffd4f33bde32c7630ef4674b07ed12c85aba1229f431cff5d78d68d817b5cc976d69b469ac6cb93177d1486ab199,0f707eaddc3ff387b1457fb20531d5fffe1a25875d3c5e4edbda1343b9e5758ae3114209b14f55d2abfd23a73aa6333b,05db174b4fba7e68d28a642eac6e4909cb8dfb658e4408147dfb70c09243fe5c32129792fd6cd336f1824524a8e37999,0e8334f7b12ba167b0782d1dd7dfa28d51cc83dda20e2bd44548d0c2fc4940689cda542ad609906c4e295d8a1e9f64f6,0e569b7f4fda9c27239da626a37aae1f1f393940f7ffa0238f07e367d0b040cbb3bfd06f86261adde52d52f142711232,0d917e17cbe2994a9f48c754dfcb43824dfc196c4eeaad1ec2c902978d6670b895ca25016db9a720f450fb055b150a70,0211d95a855cc02131117c08419c8a8ea906a00cbc8fdb79c755acd6ec0aa4bd450fca6953fd4b6b0b0009d982c85b5e,026e137be92785d890a8d8d8a4bde4cfa07f29e2b6b148be5f8014ed13034a60801bb003a36971467c9bf91e7a97142e,190d39ddb4050295544700a76b23d3f109c092a80a804e13b66ea795db6fc156986d556f206026a49344e0e5951d98d7,0f1d859aba9fa151727cac9a8567ee9c032c2d8ccf5d8f2724464828b1f673212809e9fa096b2ecdf95b2b2e4fa38a75,091c654a512b8ea08bbab7b6b9fdd78a49b19423345a1465528a0b584e85d23c765bb9f3f1985e6f89ac3009ebf3a99e,0f57fec764855ea7d1624c91f16c4d099166506f304a79e1e0c40bfc57f41c8f361ab15c74ef76eac14edd5c6d5bbceb,0acb488fd42ad53b12c9703361fa00eef33606e804e7a91a2d6c96a5774df2a6bf94aab5973c3aa0b88221aa1a0ecb8f,0a9ba0419b6cc23aeafe149f6d737ddd8aa00e75834dfc1e73901bb9ae25010d85977dcdbf417f82396aa061273f3a5f,016b0bf68515efd0574f287d03010c8fcf8d523102e0654ee51c4132d750ef64ca1b1093503e1c671f382fc1b140a6be,0819566899cba98f5f43cb9dafb062e635ec4193abfcc1aaa8aa34d53efb392b48cca39665d4960e2ca61b8e0df68a42,04c13c6ada21527399abc94e4fdbdb8ffb14b5bb3f774880ff33cb38aa8981319bbfbf6e4941ae7504ab7aeabb0bac34,180ded2033afdbd8f88963e1d1ad4b640875e17a7e720a056a0e61ea1a6f5990cfdd9e8572934743a6cc8e999e784985,091896bbb40a69fc15c9d75556fa08c59450fedcd480cc9e8def435dfb68041ce38b5a800da26e82e67573283fcca177,03f59112fb3048a6a468feb79664872b58f898d9ad36c922858111f42e204aec44997f5a6da233fec053f33b45f4c835,195a78a6b417d24382b40185a29901b956d54c95c9764097420e66e6daa4eff3fba9155c85fea9479611e322f49c9522,06648f2749a55fefb4492e8a4453bea878c65f740665d01084a62deb11bcbdac48e357c9a6f68076cce028faf6865146,07b49d61c947fdcdc7961f981adbcb55924d3e7b68214068b87da133060e7f95ea00e99d70036cd4bbd6749c6932169f,0a3881cd9dd7d8f1163ac6fa761f75df4e0847c9f5da5197dbd3336a995a1dce7106d0b1434a356e248ccd2f105d1a4f,0af02e7b4ba459116765fddd8f761db3cd909446c5afa49e46d4a4bb83ad181a99569e418a5ceccaf00ad29f2d445135,19b5191fdcd2b9d9a7d2fffc1c4187f14b70d6838c0dad3c9b2da8415c19ec97ec3798eb25f32a7226003d8333a422ae,0b388939fcca198ec39c0bd0288b768816f842abc6985e2af35b43bf0db1659b699942873c843072f6d0fdac5b83117e,162dfb8b05e1bdaf3cde4f7cc80dbafa48489c290bc09ec187f98f7cddf220c85ab0885d7cccbfcbae38000c3c870e00,00c2ac6722de06149432bb0c2e5012f1831ea313fa29ed5c93519e8506a1ed32adce2b4a85f07474781b5bbe802a3898,189f182cb112caece3612aef52bde9aba388f7bc4bf4de158ef6540c3923b01eb64671f60cfb8396a56b63c78ee1b081,0434613a56a2a3546baf0459bf1b5cea2edf822bfc5c89ae7cbdb317d5f8d38eeea9dce5c65c49b38aff83206ffe35d5,0dc34d1d79b41e0df0005ff90665357a7314e72b8c89e6c67cd98ac1fd01cd1c0bb57aadeaf2b77b75fdf92f9530caa5,14f38ec06a49dd2a850cf5cc0378f087001ffeb12f12ce075d3d2c7d84a77fc115e7c218c110cdf25d45593b6cdec378,001e794021eb02572e0ace832ea4c863038433ddf439a3d26d2d39bae07e80c84652e23a652858a647698a43737cc253,0cc12818fcec8de2ff6688b8eed64a4e03e3b0d3d1ed5da397ae9cd398329c17579ac9cfe9301e2c119dbc52c286e5b8,08882c9c79b23d992c50156933658218e44a7f27be637947600cf6d132212dacae30c2f42902fbf4e04f21321a10d4bf,0dba2c8dd75397e5094d6cabdf0f430428f0ca20c05a0ae19438fb0cdb3d7475ca278a64262631a2bc2dbf35ffcdf216,1803e2c96c87d7c0fe49bdfc811b32ae4c9ebfcd7d2f02542a7185197c2c3e0bb0744f76bcfea488499654d6a1aa1afa,0eb92031745ae4b6b1e096a9f2a142c40f4fe5995a574d968a5adf8c66bd987297ea7d2e2129aaaf8756ec9ce364e3b3,128bac41ff7a71d26308e5190ed5ea80e80b64d994a7e7c57037007ba178a9983e9d3fdd5e5d59e9abdf1e524187ac6a,00bdf9f6a53be1828b46689987f20baf7a2757853dd80b9da616c749c35e54fe683debd0df062dfbe9c2f1d68365f91c,08a70deb1baad04bf134642ff43baed6b5a0c86e6cec7d4e5f969b85beed1e93bb8d0266c4b23463b7ef72bc26375dca,18fd4a63c62018804a2375a24c4f381ce3f94abf2ebf7340da75f759c78ae91646dbe79bcf8409ee537205765f585582,0398cbf405c66b09bc3bb2df8809a82ad432b66d544ef466fa04d6f2dcc48f12571b1fab34a0286699d923ecbf65676f,15bf34bd747a732510bc10cf2bdd98804e3e5a30bd6884d1f3195dc4398aa0f7fc35e777b20ab8219ec11b4c77f4eddd,0b5f02b361dec84aa3dd7240e410d8b4b2c0768ab9335f71f7f3fedc3e08a9c27c35aa4cf7416af83ef5e2c5f03f4547,1344510bddd17c7b873df49848d445bc8dcf49b03be49dbdc68495f79388b33f521c6f48f137762be2eca8f5dfc7151a,01525dcbfbb85405e0af83b391560fd504652d369ebafd64f1ed4a44e8ff7c4260b561d58a180b5c127faeed06ca48e5,0c2d15aa7b437da7472473a34620850d346a4e52257db90acd823499eeb7008016e63e0f51752f14b336e50dbd9dfee1,0855aad17f483c75a542bbea0e8a9273fe815d1ba3d20b3706dfb4c894d0c7c0c78813dc20f1988bbd2bf1273919f7f2,0406f4ac57671b676e9c2119aad5cc4310b1a07ffb82b11ac494d51c09e41e501547deb1a3f1daf4f51af237ba337c4c,12eb56e33d4f24c74682853c235445de6d2b8a67d8cb9bfd1231ddfc9c1ad30bf09bac2ca348a1b88b997acab6c18404,18dc440193684f025c3633850bf0193f484da7d6d7d967094b51202dff171e18d868e1ac94421e722c166a82c7d62d41,188dedb851df0d743e693a2cc09b2fc3786ca2def266b49b4c95dc266209c47a59121d20afe2fecd6edf3be9cc164028,0579a1e5aaae3f6e02ac1deb913ca82477cbde72b7e6d58b7cc6a91a9319bc9834d550126d18931884ac769c7d821b42,14a9d80e9010b4676e3ced9da437370385daa0874e4636ea63983e766fe96c1a062a715ce03064319c1fb5e2f37d48b4,19f24af0d7dfab02a118e0c873422626c76fd70370eb41af469e0e82b78bcd5255c814f2e7c320418ea8cc55183c51e9,19cef3bcc7428c678d54d3083e6b57e897c273b4472a5301d2498fde11f948f6d9c9f17c8feb4836cdec0a897222719e,0dd4cb77abccccf182405a74ade52dc9efcce0e170e720d510ffd10239621a4502850e3bef113a81bb2252bfb5652268,06d9f3e1dab3438a43e493a7b665b81186bbbd733ab812cf10c563d6b07dbfc62efe2f3c3ffddebc31ec54e5406493d1,13e1fff7de97f3b1df8217108215b8eb41afd06c0f8763025a7ce928afcab6c965a0ebb47a4d572c333f5b4487efa39c,12d303e32afc0b1a430b36e79e1d2ce1a4b41b65e9614b5c73464c77ab705d8162ef31efb15c2363be099c8d8a6938f4,02f89adefb33ef694bf89c794977155c1248662d9493c1bdaef8929e2d9ce97af464ef782a1c55d21f8a419efc9d9bcf diff --git a/crypto/src/hash/poseidon/bls12381/t3/mds_matrix.csv b/crypto/src/hash/poseidon/bls12381/t3/mds_matrix.csv deleted file mode 100644 index ae5dd836bc..0000000000 --- a/crypto/src/hash/poseidon/bls12381/t3/mds_matrix.csv +++ /dev/null @@ -1,3 +0,0 @@ -1054aa308cbc55d67054b451780938637384c4516c1efde724244827b4a3f517da7dafc71176d4d0d8a389ad443d87bc,193dd99b435bdca5e54d9628f98cf64b4ef15b4e2932f4aaced0306790038fe2a24a129e873b775603e9bcb62d2eb572,197d34c530289f1ee0c78c2549e7b588c17a92540bc14097515c84ca3a1cc5c11c0f28d8dc5a1c45023c79a753fdfe7f -028eb098a4742d219569a8e6a5aa351492f8f10167c20ecb7d772463dcf7235320bcd6c3f573b4886354bd6dc6d142bd,02acacfd528c4a6581b88f99d319114aa570e09304469f24c922e11d53a233b91ede1db5d56bcf351b0c7273d236dc2a,09739ef37c6cd264756f56cc854698c909e36260dab6ba99c0f9f966e6e620c0f3af8c9d9d39259a4772c0ddb846caf0 -0e04c931c0ca356d46e3ead73b18c96b3d0b18d22ed1f65c6083be79a489c4bd37304725f8ee11007cb37f2eaf1ccee1,17f594a2ad8bf7a10bd730eaf750db78dc067b8d79cdf61d0218a9c2dc72986795f6dab8f3bf4512d3cf419146b31266,09605fc485a538747637ede019ed67c220d6e6773430033ca6ab7d7e2df4587ebce624cf90da513633af537ef47fdffa diff --git a/crypto/src/hash/poseidon/bls12381/t3/round_constants.csv b/crypto/src/hash/poseidon/bls12381/t3/round_constants.csv deleted file mode 100644 index 3349235495..0000000000 --- a/crypto/src/hash/poseidon/bls12381/t3/round_constants.csv +++ /dev/null @@ -1 +0,0 @@ -05ce359a7f1c1482b1f51709cadd64f5a26af15a23f514ddd3f99c1cb19d70b5e3c89c25e6c66c03b24fb5cf098171dd,0cbe7632efbacf28e284698e45f9564427171e953365ce0cea873a09cd5858899109272afc22d0f71632040fef6066e5,06ec4522f086f379aedd419486800c2331968b246ea4fe3c505621f4e8bc4e43126c217b178a2ce037c5d3c62c83cfc1,044d247198740ea72421cdfc31a8cc2b75bb91103d8c8c962bc21374299a2971bc8cc83a8047fbd8ef7f5e9d09a258ff,0420e80c9b9e0bf1732967ee0fd10235ce6095f420b91ec9e05331a563290ed9bade74ad84576e86436f63bb8188327a,0ab070a0f6fb9da6ecb8fc33528002a8460a0ece92b8e43849ebfc6bd1066283f6a3c2b01eef74c36df368a1bff4425a,0acaf97590428902e3c0b935ef0f502b62c7813e1abad1f70bbe0418ee59d97d0038450669d0b8f990d9dbb890bab9d6,02f9455e08ec7eb0a6e298a8d7920dffa93b7c70456e620af26bd2fc3f73f78e7e0d43e94e38a3719d312f10a30ca727,0380838a13c281081d6eb090e6a22e81a80b6ea6999e9cee02b47c466eed788da3f98ed9e4bc766aa8a74510ca933267,0ff56d99a2ac8b53fcb7a73a3b6c3027b3aded2ca607a84d9b21762890a60c0506b3e9674644207aae65dafe97831db9,142c240946c27c075ca9c8185658a20b62b50af30464654b940171e5dc3e80ef890899261fb806fe20eeeaae0295f3de,025afb38bbad1053670447d397a1bcc6f6206eb88b57a992673ef3505523575840119427396ab82fe2bae7188efd12e5,15dcad4ebb0f84e3ddb75c5b26b7777b64c80a00ceedaf69b8aabcea714c4050e589bc68ff17a334b025ae7ff7b565ae,19e9ced254a572f210a23d4bdc969893e21e66dfcea0295a3a9e4932e6aaf10e687d85af3fd1f8018f8f6ee3377398cf,140335a3c89cfe2a895eb0a1f0976f0084d4e7f9742d29359efa35dab5d53b9ca846d09b8ce14744e62456d98fa2b255,03a27061337bf8a6137a3c431114fe68fd10dd67a94c185f227cf3c83169c93a96cc6c2f68e9cedfffc1f7e9b4153ce5,0e9157746dfc2fa80982e8e0a532d96f107780b5531113f9a3bff986bb056d6537ef35ee58260b204df0309cfb09f725,18456e050b97e7bf6e4e5faba129cd0e219148f04a6bf424a98b8526760171217193cfb79cfbbfd0787275ce7f494c25,0f47ea2a57120fec3ba8ff68de76eb961c75678d90b3e167556c0ff90fc924fcf1954c2e6e825deeb7b44634e26e2a5a,0529c46e1812c17588bdf6bb2808723ab13f044ac3611c4904815f4deca65d5de22e58bf27d0a2973df3f9a24d6d7f04,06f7d85299cef8bf7cc0b8400cfaa53da9e2441b688b617953226e484b5b51aa68ecc953a422dbee26bb5628b2dd22a6,02c98c03b1f86a41b940307f49294856fcbeb8adcd203f346846c3ccc804ba9b53a9baf8c7c1139cfe836146f91fde5d,1436ec6bd04bfac09b195dc71c6ec105b7430586d6959ddb2b484f6769b27095651bc56c296cbc7e0b66dec3b347e37b,18714cf905f3f453ddeee24d7458d234ac2db60c25eb32604a7635a373f83d44b10001ef8d554d7ef40db37a68afb6c3,090b7696b7e0fa8d23841f04274971fecb81b57f56bd169f9b0becaec57013dae47948d45ce2800029de77e029dabccb,04cdb2625703e7155cca895075ef92ed738e1c7dd162c91da5584ae970a575a1eb377a4dea47c0bf72f3b2ab0ac4834c,1144cfac1dc4c2d16681d3459db3eb289fe3248677b519b429e813857c6e807e9f7ee2c55fbf66667950f2b78448e21a,09568d718e369742310b1a23e14a5a162c71c0e32e3eaacf6e2e12aa5cef9530cd28fd760ff4d313d44d7afc40a3c7f1,0688822f3bfd78ad70d0e84609e56f5058f8660a3411e9fa73c5d6b8c82dc25cedb79bc1bbe7a68a17c95aa67974e018,15e7b1b72818c77a9ca26eaac3fedf48b57948f8628b1a9516c6d7ae34e61799021ca4891a4a5797c23a2a97b1b3e100,0137553bf56f0b217a6a5ccd16abdc96f1ace6fc82c2fbde179ca3c13f4f633f7f6bb54581ccc5159228950d9d00a781,1781f5f50a747bb7b48c50ff7476a4bf32cb65457ea0af6cc0ef3773dc610acea687048b5d55e7842471e2058331c0a8,03f4c4558ceecffce8f234cf001ff89dca987ea20833751b4ed3753f5f0004c7eb3d1b3cc9982c009a1e47a69060ccc6,18a8dbbd87cb89704250d42245e586055fd978ea444e70fcc9101f341dbdcd88ead49b151225840f2ca3e7e1feba4b4d,0724e331e71ca7b17b4e7d633349acd5d285a582a52013092692d5b3e48395441f2483207b032edcf89b42a419eeb1ac,16adb125d97e06622b91f650a7a7183878e7dfab2a2a123186e5cbb41f07b1f9c8eff6b15491f7db7a910fd8d0cfaf0c,12d84fe1d5fca468d5816a0f2008346252aa2f6cee3eaf5d030a321fa5219889e5ea5a5072a231cd0cba25a7ce6ac48a,098a829e2f358afc1034ede5157c3056acbfbdfb60638335d2fda25c3f26c5341a704cf142579bb1b213595dd59d4428,16489677f6a6bbfba47ac7d04a88d6236e337fac0a73e9cf2d1e5dfc6eae10a91406b157c0c71b6bd1cdd551b9ea800c,07a424aae1b55e5b5f8e3f9a1db819fa5e266e391c7eb3a6c68dad0dea7fda692f075ebeea39c1022c4c113ff7cd14fe,1331a382d571cca0e791eabb10370f5981efbef3db48115cfcd9337bf8eb1ea77851dd705972d4428fcdb80adc3ef993,168a3aece4331b8ac5ba9eb50ff558f94633f2e027dde1f6d4e28bbd5a5e336ba5224c8f2a03ccfe5ed155911e9509e9,01e24ea996889992c1f60510e602c2346a216df585319e01561ab40eadcb83f9dded6138cccfb50bfec6618f6e0678aa,0a913b3c9398f07d7ae4ebfd371be9529874c239ac28a9526b2fcb7aaa5a806af217f239a444c7fd7afc80bcd4125db4,18d58a9abd4588144055b222f75b19a4d04d144056d31a66035e7e039b92dfd38b01dc29a1124481e5a9645ba20eecbb,14a998b9fc42b4c16d84ac55754c45884502854ac8c28fd57d652fab0a9e19316a164d6f741272dfcd77c146cd8b999e,0ef45e044f060a44e83391157a17e0272dcc4b6ba4bcb844b0a554229d18faa4f2dd9526356acb2d6480de3df6e41d20,047ff51bc66d5bdbe62c6d0dbc0b605d8de234acff2976df33d05e66b3d922694caddb59fe72f21a24652853ef46dd00,011bc23a609888f723178e35d7a13b2f21950ee6213890d5f40a6b8926de9b835dd9acf73a8b1d1c3669862fd8c7c590,01dce326b98812b2855e819c34a7c4f16ba9db156e3a83e513cd61556a3ce608e9146abd2a228b104be6c2c6bfb90bd2,1150ef5003c9a54b1b128d7725eba4d7b1436526f834eb073eba8fccd845e107b3c996746443f93165ebf0ec4a8222e1,0cf349aec12bbc5268063e99754741e2d344f31bd0e943893b19efa98fa289674d1d7ac5cdf9626515c264e84b9ddde2,027822348cbff16ec1e397418d3ba95b0204dcd7b22696785581f4b6fbc9ae693133ccbcd022b2499408296559d76d72,1098a88df50b338300498ad27ab9815e63522af879eceda54cedcaad069cc0c206db7d18acb7da340e279569077ae1bf,0540cbf7b242a46c8b2133c287697bc2c67c13699ec4949a789cfa883d0d2c3e792f5757bdc079473ced142f5e158d35,0b2e85a5c568235613b8a3d0f454b6a3371b535aa3a470887c32bb0dcbac7aa8287c625c267da78bf44e54ffedee67ec,0976b0e5e3c2707762844ce0cc4ceb7efbcb7f5d7dbce76b99bb9ea5c4f181ec5cc4b5fb9a0ff4725e775bb6ce2b92ed,0af4715edc994b4c751e1620a7263e0d459addabd7a71f54180abd713a727e507b2653d77c6d8249fb8bdc4e15a2d3c8,0b750e94e19e26998795fb67a4984bb98ddf8f4016eb65f7b1b7856301ef05d030c5e247262241bf96e0e985907a9477,16472b1e2c7b5919d87a39831ef730eacb015b97bfc0e3b7752f4b56c53504187f6b8705a3180b0868d82571260eadf0,037e1a36cedbfc2a72baeee7e012b7176647c23c0422bdf00fcb0cca4f3f8c1af95f65513cc330371b291cf6b2f2bb9c,0729d0cf85334fca0a6714b9a38b34c4969ce5247c6bffde1637cb7d922dd07c16ff430deb7b8de025f39845f74eb64a,030b67b9c66743215b1e15caa3750694de366b6663296fd307d142da9b9027a2e4b334503a26dac8e9173e24f79cbb48,01189e869a4fbabfb8daa5fc56a6da604e775307e22ce093abe44966986e2a0241f35c23114cc44a961b8bd6a1deb32e,049793cdd584aec84b560124989879f6157629a1c717c4873bf45130c6889c7deb77ac9495ec69664a487efed216e3e6,0e38794bed871a15c84833c3e5d42b6d82d70c15da0101c2e7ede9fddc7eae9d7bc979706d07e72aea52005d3226f7ba,10ddf61a15f49cd2b8046cf887da2b6fcac37be7c082c967a8f2fec04b84fa6ba86c8b561ae281b6ed638b445b408df4,09aebc19964ee60b7aefb2c251a4a094196f7034dd9d0ed187da0533a7e1f1ab97b28307346e35072f69b67350f0ee1a,0f320be802ad88035a89faf94d270437014388134a75436d8b3af5e25c5d12d1cdd0edcb8aea0712857246bf834a4502,105cd93cb9fd0be6bcbdc07d961d2cdb349ef76b1e16a9d44d7bfadcf05b47f4d31d7d15bf666d8a6a8087b0b2ab29cc,0df20a9ae9e01da18a21b35680972b90910a3e304db3db50f8cf83d4d6922afa2e55682c1dc3be3a860f0200d4b4cf4e,04e24a87315876e95619ac8322319953b7f84323bad52509ffe05eb4a5667b06a166f18b365a8f4cdfd858674c12c40a,0b8875acdb187fa263f2dac90dea8dff052a325450a4367c219cd731a577b92428107a53f5bc4a2b154624556bb5949f,0b4635d1a1cd2f1b96d32bdb651f15dcbce50502464f7a3c7311a20822a909c46ae3a3ec92a31ea13d68a9f973681bdb,0f19b257050abf57d718f651451eea67f97a84f05272411cc2afc97410c12d0989cafa47937b4153c6129e1564312e57,077a54e3d0e43947a52707ab3221dffc0de858cd5413b5c8f750ce02bd79ee754b32f40bd88f9bd62e9a13fe79b1d5b4,184664091abd54bf15ffb03364be946daf37e354a2a9962570f79b64d8670470b84240761e558f343ea7649254cbd013,145d1651d59327f204514a21a711183710f1686f1a990ce2d44b4a65ed7a3285985cbc6de91a56b691612e5e2ee02a57,0007d55524a36b12d267e95a962fde3bd5ba34314fa37810ded7a8626cf98672d7bd4b48f3218ce5e86edf0289088ea3,10b0097cfef9ce88b164db30ac2ac77f597c9fc1822cdae7014a6789a4716e25f595c2ed0516fcadd4ef20e29bdf31c6,066ad87f0efb7c7a307bba84459d5316a11920642c42d992b592c3d517d29095a2cd07600f676d750cbbc4b04edfbc16,00fb9b5f9e70c20e78a020d555465fb232e696dd9f38d82d968f164bd2d98d092dc1d5320dfa9a5b3d490066c85dba45,024953f8badbf9211686c44a673d69f85f6158ac34eca9ce2dba1455d837a2de9642d36d8a2f8854a0b364aa3bd79d01,18e11bcc334b279a3ce4ba62a7f2980a7064fdc67f77429412440ca13c0ff0312f5a08eac1310dce3a42ab1c73833533,04e5a1e6d15868d4e4f0c6c442e9c52d9fbf96f565242b66d8e66e152d3607566259ba63ede6d6fa8d6973e022835d27,147bd4798b157ed4d3e736743faa1d103ceb6989447e9248d452f91b2043de17160383ea97c135009c47cc15624d671a,118befecbd3b48fb946cc1a4dfc8f52929b98f395676095daf690ef9386a6fdefe3c8a4aa3f8eaa9e7585f00aeebe697,09a73b5eb2ace9b5f62b8affe9a79143bb265d07b95142263a5fc980c1146d235f6a89f8af94911741d891e4cd721d27,171c08e7e2709c99f886df082e1e0cd4118719239dbbdb18d228dc988eb85926b2c6adb9472e7122aed118ef5135dfd0,09f272c7d746ef7f598dbdd118b9b06d5361d4dcfa8086f928018e531219571a370843bd577c634cf5de6faebcd3126e,0d128a38bc3d48a31e11195332b8c5a8a204e3e59034b9681a4c3d9514f27af90e92004b37c530101298f34a6466bffb,18382300aee2678a4519f4b17a7092a7f76b4b2af42b13523245579b0aa20e0653efe994864e38412e82fb487a663ebb,0d68d7d180ddf70c7fa2469f4e7851c493d48af1ffedc7f4a6d4a02b63ce2949fc878ea9758f74943b101e9e794d5448,078c2d00f1bbbabf2b58e4d8a49709cc5996d0a176789f9f92754706950facb57c0cff1dd784d2ce887d9f2e3262114b,0909ccd7e72c7952bd434a11d4246535cb3aae2fadb6cff08afed4a32d973bdb32e418ba754861de12f7c3636792522e,0b6ec5064386034726d53e63d3a1d9bff848d4e008c265205e2815af9e2ed50deda6fe04f64c388f94876eb7fd3445c9,185f4aa3b9050e21671ca2960f025ec50d4bc1fe8d3ad4f569deb98fe99c5cd3319cc066d9093674428126c6750a6cec,00e7e3595109c6563e01fd54867800943b3e38152e6d0a32115d3422e5d80ba612a5fe3d800a273531d6274578516c13,166530b6115e4d43c758ee5a687e39c358426f8159a41e92e748b80a2d8cd9c0d351b05dd98f84122f4ef20d94171749,07eb14319e034abda996289dea0c891d64cc3fdde9e9e2b6676d2fe96f00ffba9668c5d856ef07530567fb3aef17aaa5,0b95d8d4de0bbdad8b7f458f9da28b830910c5624e29aa83460ffc8498f7830d0b255e5e1525d5564bed8ae17d583afd,03a47d2d2ec8d2fa7837147495c4a6143be8e58f91c91e6412748a32dd1a33bda48b5c9ed69216c57effa187b0c8aa9f,077f7bc717b2560b8773d5c3fc03525a01a941ff72a929b286e317b7495de2ce43d653d40ce31e994b080cd8183a0515,0be1b0962fe93fe7946bf1df923fd12cf9cdc7b07fa99d0553ffbe1ce32b9de9144ff6b4cc7c62a3d64679ec32dc9f0c,01dd6422670a60d2e916294d5dea985d46ea96a63b27737121410e322e1847d4e7b9e3dd6aa8e04ee245d7e1a7f13241,0f21590653671d78043e9c6d372c41865f679415b366554879241718bc95a2c7a2e04b1b61c0e9c112985c6e10f4fdf1,0422fd1be4147d4ec040770fd1aa4e70ba92943e10659272d771e4fb37d9536e1dc3996cdb45bc4fa523d34b58ef08d2,00b1467dbb2645c2ef8e16e8b5cbd179b1580b475f1d32bfd400a4e99f23c172c5676cfc8e4f9adad1232f489a340c1b,08ec6ea940732f09b5e8de3a6fd9958fddef903d8a3ff2dece0d264bc1ea5ec6472e17951eab3103023ab50b4f52194a,160aa6c14a31c79c1b1915ff1addebe23dfcdacbd95117bce04ed9d99dea33e9de2d9e97c1cc1a5f1fb91ffd5aa0023c,164a82fd9ed676818d029d5fe2596d384b85f3fa5086f23007dcd0f023b16dcf2c0b3ae43e32f1d91334363c2629ec96,0f0efea1d3bffbe1cc4ea82b15f41b2f28eb7b53e7ce7e944b456f4fe1669a2c938c7eec5d7ceeaa53c03dcd81ce767c,149354064804e1cea886906f2b3fe96ee00e0fc0c2603f82a56649a9c864b3c41c81b0f2c49a07f5a3c852e447b9c2fb,10d1564cb621de7d0604671236207c9ac39cbb193f9a725d22cb4c963968aa00d8174bdab906e03a1d5740be06f0fb89,128a6d9c3bc0f94309faefed95bb0d6b52b0ca9f9bce23eecb6774a732df16aca5e670d9d7e565c3cb152cbff1606e83,094776371d549361ec26a30cbf335bcce648ea666b2ac471608be1d1beaca9f07c0839cbb7aa8a00a24c5259f76d610b,0fff2c4159ba172de6f30cf0de74a485a38a0cc96a62e11a26fc82d99d401247ee8e1e3c7e907c633d28589f35231861,057efd21c274e1a15400b8f10a12948a3a8bf9a65a23446774ece8e8c8c7618e8032e4ea03ad3fa333ce1405b8394478,0e04cfe3451c08b9dc5238d2bf6c762711ccc09b40eb02a875f04882af5f4e48a7ba43cd54b22c5a740cf5e902fe5364,19a96116dde9c7eb3a84604bb5bd793e469c4415792a968f40d92bf41c7b477a8f3c9629054f150acad0cd65148daca5,0bd81f5e316b869464fe73c0a45202be3ba6e1b4254c7679c6789b4a0dc25c84dbbf3b05db21923fd83109479f888a23,00c464080153a13a83bea0c2a695a3c6467e2a03e2c0cc0bf16bd959db9804b2e42ccc517bce1c157771aa780161e7ac,178c7d0319260818f645a76b4815fcaf51f6531bdccd2ba552b68b30d79cbab01b52a0613f21972fecd9c4785c011c48,011393ba528a7067b330eaa8207770fc08bd34ffd26a112f74000f33b2e006d870407df2a625fc36be7e23a658a105d0,136877999cdcb25ae6d76b70b66557d5bbc74fe30668dec3cceb6dcb5b70ca9536baa618319e917c082dbf073fe35021,185ba3019fb2636c7beedf173badc49671b4b4e65b8baf141d16a49c74644050623e3fd2749a64a2721fc216284288c9,0c615df42da5777100ac4ab6075e634177426a7a2e406def9a879c63a170bbb84228df9e54ee4925f56bd40a20c5def6,0c47d568e12d8bbe0b34017d377ad40a89e2718a41176c0f1a99a6b6519c3a1780ea0c3574aba2b10547866f39c72c40,03c69a7ccbc473c3d2b8e35733c2567b04528c05a71ef19096e2e7cc9084b10f3aade165ef193232f4d1ac785bfdcddd,163f55581d9f16a5207bb93fb636877a260ff5c45acca10c0fb7a0f61c977a8637ffdc404e3630ea8475bcf8ddee3e3d,0d28e185da6408d9f3dcc88854bed1543de7d0dd3089a835b93a79b022dc9b2f51a97b3b0611d8a0bcb6236b0015567a,00303e9dcbb98689b6e887d6845a5fcefcb5adc23e5407a877bdd41a365e2e81030479e5ab1bf47203c279745539d5e7,07cac622a80947ec9ce438dd8436ded7f877bdd395e6ee37ed0fa680e6c72fc36bfece086fd67d5b30707e760b7e15c0,16e80d030d026fd9f22bb0d590386ee5cd0694d0bb359c646006d60a6ed27f1b2a164f669a3fc92d7b5b160216baa5fd,167b0dede70310c5c65cd2974413dabf46359d51b9d5ed5ed30d4ef96b4618a0b36cb972cd4711854549e87e8ad9da68,08c0723287967b2a67079556ca108d9b9eba5799509f4f1a4141d272487399bc25d0c8ba3cffedc223f68a89e7405cce,164e1e59ad8df72a45c656818e54571e710755f0501d7a07842df612e959228e0fb0343f6c41eefd290c14530e51902e,01d21d2438240516072aef2dc5a41f8e0a8fcf10d8a5e99c5b73983a9c7ed8e6b8b4268c9f083713d73b8d2bd08a55c4,07f18232bc05c1e17761f47e32ddf4aa7c1668f4450477e96620f5fd9466a022b670d778da0fff772501d2f94646c1bb,051d52281e61ce46473de7d0b81c0730a65201aab9162a37f8828b693c7ae9b68cc0a3331810792b6be004852bcbbf3d,15d888836f26821caecc48881cead8ecaf161772cbccaa7660f38a73abe81c3f1090522a8948d42237883904bfce2841,119f6aa29426a19848984997573acbf1cded87be680ecce8ccfc39e7e58bad54942151f00a3370c64e5a7bfcd79a7585,00ee43e73d460496b140ddea7c269d9e5f9c7667c097bf294cc12029e48adad5a951f3db1ec99f5536a825fb8e4ae18a,1744de6f243aad17f7956e73f10f05e311a5073ddb7e086d795a09d648d9967d1ac149edb7d9b894f2b809ce281597f8,0964c43fde732ba6d2e7e504c30d1e5bca4b7fae88154c7714b500cc9ec2515c79d18c2f1841b5679ce4024b1dfb8120,0a0e278a3a14f3a4abf74a50df67d94259997461a3f04456bec24bcf8f5e766eedf4c5cec98bb3e5c8aae70d677f915b,0a519f934edf38e363f63d5cb7d7ef42d79b0c40bedf5ee476ba59c2982a448f4ff5b2e85155344ce537c4068fe20eae,0d7e48d6a4d0b760fd348b80522bc44099ffd4c113c9dfae7dd1965254fd6349cfe367a13a03e9c59b845a6a182be3f9,14a92ff29e69681828ab2349f07b282efeaf9041617ef528446da683632f543aef7c1adf6ef3a86762ef5207f2167566,0b402bda56cfbacc5df30977f2abe5f2c124707517a87e1be1581daced1dc76656dc7dd7c1444f30d6d231152304f458,0859044cfcbd98680091b4273c8bad57680ad0d8ec94d15d47e56b3a4d0556fbc1e9c202528e3d1eb381073154c75466,0d936bb51d78851f14c7f59aa40c1393513739175654a237289487f0c7769a920cf95d7f7b7f33f01db8c9c5a7152808,083eefdf26479e4ab22eb29f63c8e7a0396f8d68cf2490727869fcfef4a28c0cc60a10af6f3ebe94e36b3f27c9445f15,192fa334e28c89f0497f0efe3852710679846219f31350aab9878a9564fab8ec6b0f80e7059185eb1e21c95fde7acc0d,1284b780a63069963fa52dcad439221b7a989275191e5ccd51ac6f7cdd5afea693d8b80d667ba1faa0ff153ff8945907,120a4ceca1b79d90bc87937ce162bf5afb526c933af83c002897bb714ec76e104d50bf5f075435fe9e4d1df7768ec976,14f969981137edcae8c51ec84fecd9854d38b2d7ab36b90d9bfd2b1939f0d8e1711189b7bc0a0786d3a1d7f5a82808c1,1574e558dd57b7bb88e8947b2873e9a0b1e2ac91ffa36a43c2a7ad447a473d090e6c31562916ec75f9ae6e7f23e1d183,055bbd9217e7c4219eb23ef4f41ea00988c536238747574ff6130c852f8abfec73af0429dcea5cd9a3309179186f829f,00c9bdf50f0d1d4aad99c034a62221ae790c9c3d8d9e6393accf2dbf7191561e8fa8a03d0eba83b1fe84d4e72183215b,0748f478ef452544d34388175c0e37969515bd3c2f198028aa4d3a6b294f5cb11e5d6c4442379475eed8a44eba5ce779,1891fd758f4122852f4856e9c4177570fe8205218402aaf5398ff255852b8438af1d07d8d5f0472a4827291ff32ca9d0,10a71032a784fc7e89734b3f66f5bb640c5377cfa762b8d71a418739f2a6f7e6dbf750f2177138f099bbf866dd2527d5,04c19b663c45ff653d582e6694c1332b4feada37b28910714a834f2f6c2bc6ac238d0a80e18905e9c5e8a069cb79e202,01addecfb6ae315271349cbc54eec9788caea8e42b25ab5f5a9dc3e7001fc01184be5daac997fea2757cfe618d30a766,17e29412f7863adf73ed5e02424d84f6d8b50e2e3e4cbc67d82c40fec6cdcaa1f4e6dcab2b7e353f672b99e1a066587c,06a4526cbd242edfb340bbba5ca7c834343237d9a3558f7d8d7a5aa515f9ff209239178f9c7dc5dcf487300ec3a2ae0c,0029bc8185dbd1843263e0f4eba5c30b6faec72895f62d2f8365ab446fe001b4f826bfa1640ae82029e0563f0eda59b8,0ee70e93a57b6644d75fe29187c2599beb930f9ca9c69d6474f04d0a1fcda71a02ae9e8916655fa042c515aa4ceb6655,00500e8c34d680ae0d43a3e84a2a4d6a1dad05d7fc7a6db8fceb2febe2f9d899937ee3d7b5dfa2af7248dc6920809e37,05e2496094e498bdfa05aa676973b7a7fcd9f70cfd859ad342e1ac3480998e150c5ffba8d750b0ca51f88b1d1c34706c,1196e116c4f6ae092a3308122ccd09e8fee95277a6d5cd4c33ed084895d4b1e40dd650fb7db009b69a86a33b725cc39a,0b8745eee6cec3513b8e142d7d122813c4ef51a856d533093bb6d74ed6073264dc9f4bcd889b7dc034ed0b578d3658c0,110815b3ed5cfcccc993eda5e6272573b7b73a62bb27a85b3d5edf6f41a186cdd3e0bbd52b92c1ecba182cc120eabd54,080d6fd19269b333a206819d21bac42bafca7acb748363112098d357bd863eac44d694aa20bbfc88dc4430e292e4a01b,02d355830be701ae5dc0e04149e258f16208c5e5a771ca5a3827baf837d8ebdb6497db7fe13e14b2bf368380119b66c4,0057973cff03522b3031fda563891aa942125320be89b1226bc646e69e219a16274ae48396aa8b45be38e3c691247361,04c15f4475385a4c8da5e854ee22047d557dc0e5a902d9cf1b7a15f675e494a5279ceca134a291fa0c90bdd669269a4f,0adef7e30f15446e0d0308b19760dfa878fada2bbbf847a360b8333a7f488a49d1f61b11209139d9dfd18238b3f2e7bb,03e9674ca3e94c1493106228dd36dc08e3f1054b045cc8ce0c2b441fded4d512e81ebb082a83810d9d5a97ea213a158b,00a840ddebd2809998cd943ecf635e944116135576cf781014bc7ff02e0cd13e6a88bbb9082cd0f70eb0d1f58afa4cb3,172e5019a2315b706dbe6c391f0d652b7f8dad9bca83e08adf20e20577c6d6fa67c3939d809337bb29f5ae1796e959e4,0436f10695b5d8484e67eb3260c5a4b721c9ece0a72239cf081d1c6fce80c3940b1e3f65791529bbe71b55ba94b4de1f,05cc3e4936c915ff2c4c28b2de8097719af68b3d5eae1f7e991ec722e98b3a6c9b0acd1fb5f6ce4a3267c01bdbb419ca,16e6b6a08e681348604162bc3ae75a17a0305f3707672375380b70d19d0af7c06425887492f8ffc486c437da6de36104,101fc082e1e4eb3b500a781ef2de5fc54e349d643e9bed315a22c169a11d0df3deee571312839966147e28395e287cc4,07052111a043a47127fb3e71018fbb62e4b0fdde669f7e0fbe28533121c7123bdb201b506a3d8a20c0b0f5623a2af612,044f2c9171fb093f42a2a5cf2764e88aa245d6327e6294ad41d72971a86f00e83af2d03d9887b855cc5bddb5182352b0,023bc737af1675138e33c7d70651a78e49c5d639c2ec98e5c96144edb4298f9f9ddc16e949f187daa802334cc467f777,1656d48494cc233d331bebf1f7e6896573b298c6fe1ffb215a7b071e6a776951d1f6461b7efe9a2d9324234a1f0e47c4,1031229bccd53f2b33fa7567acb6f4e192fb04847c20ae8ed0502a72177857178ef9a71533e8a1e782978bebf35daa7b,0cb412e1f0533ef873afde5ff9ea56a03ec8d9316400a299532d54af94f71245a8df168682cb0219db57d6eeaca403d4 diff --git a/crypto/src/hash/poseidon/mod.rs b/crypto/src/hash/poseidon/mod.rs index d628fea3e7..5954a142f0 100644 --- a/crypto/src/hash/poseidon/mod.rs +++ b/crypto/src/hash/poseidon/mod.rs @@ -1,310 +1,235 @@ -use crate::merkle_tree::traits::IsMerkleTreeBackend; +use lambdaworks_math::field::element::FieldElement as FE; -/// Poseidon implementation for curve BLS12381 -use self::parameters::Parameters; - -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::BLS12381PrimeField, - field::{element::FieldElement, traits::IsField}, -}; -use std::ops::{Add, Mul}; -mod parameters; +pub mod parameters; pub mod starknet; -pub struct Poseidon { - params: Parameters, -} +use parameters::PermutationParameters; -impl Poseidon { - pub fn new() -> Self { - Self { - params: Parameters::with_t2() - .expect("Error loading parameters for Posedon BLS12381 hasher"), - } - } -} +mod private { + use super::*; -impl Default for Poseidon { - fn default() -> Self { - Self::new() - } -} + pub trait Sealed {} -impl IsMerkleTreeBackend for Poseidon { - type Node = FieldElement; - type Data = Self::Node; - - fn hash_data( - &self, - input: &FieldElement, - ) -> FieldElement { - // return first element of the state (unwraps to be removed after trait changes to return Result<>) - // This clone could be removed - self.hash(&[input.clone()]) - .unwrap() - .first() - .unwrap() - .clone() - } + impl Sealed for P {} +} - fn hash_new_parent( - &self, - left: &FieldElement, - right: &FieldElement, - ) -> FieldElement { - // return first element of the state (unwraps to be removed after trait changes to return Result<>) - self.hash(&[left.clone(), right.clone()]) - .unwrap() - .first() - .unwrap() - .clone() - } +pub trait Poseidon: PermutationParameters + self::private::Sealed { + fn hades_permutation(state: &mut [FE]); + fn full_round(state: &mut [FE], round_number: usize); + fn partial_round(state: &mut [FE], round_number: usize); + fn mix(state: &mut [FE]); + fn hash(x: &FE, y: &FE) -> FE; + fn hash_single(x: &FE) -> FE; + fn hash_many(inputs: &[FE]) -> FE; } -impl Poseidon -where - F: IsField, -{ - pub fn new_with_params(params: Parameters) -> Self { - Poseidon { params } +impl Poseidon for P { + fn hades_permutation(state: &mut [FE]) { + let mut round_number = 0; + for _ in 0..P::N_FULL_ROUNDS / 2 { + Self::full_round(state, round_number); + round_number += 1; + } + for _ in 0..P::N_PARTIAL_ROUNDS { + Self::partial_round(state, round_number); + round_number += 1; + } + for _ in 0..P::N_FULL_ROUNDS / 2 { + Self::full_round(state, round_number); + round_number += 1; + } } - pub fn ark(&self, state: &mut [FieldElement], round_number: usize) { - let state_size = state.len(); - for (i, state) in state.iter_mut().enumerate() { - *state += self.params.round_constants[round_number * state_size + i].clone(); + fn full_round(state: &mut [FE], round_number: usize) { + for (i, value) in state.iter_mut().enumerate() { + *value = &(*value) + &P::ROUND_CONSTANTS[round_number * P::N_ROUND_CONSTANTS_COLS + i]; + *value = value.pow(P::ALPHA); } + Self::mix(state); } - pub fn sbox(&self, state: &mut [FieldElement], round_number: usize) { - let is_full_round = round_number < self.params.n_full_rounds / 2 - || round_number >= self.params.n_full_rounds / 2 + self.params.n_partial_rounds; - - if is_full_round { - // full s-box - for current_state in state.iter_mut() { - *current_state = current_state.pow(self.params.alpha); - } - } else { - // partial s-box - let last_state_index = state.len() - 1; - state[last_state_index] = state[last_state_index].pow(self.params.alpha); + fn partial_round(state: &mut [FE], round_number: usize) { + for (i, value) in state.iter_mut().enumerate() { + *value = &(*value) + &P::ROUND_CONSTANTS[round_number * P::N_ROUND_CONSTANTS_COLS + i]; } + + state[P::STATE_SIZE - 1] = state[P::STATE_SIZE - 1].pow(P::ALPHA); + + Self::mix(state); } - pub fn mix(&self, state: &mut [FieldElement]) { - let mut new_state: Vec> = Vec::with_capacity(state.len()); - for i in 0..state.len() { - new_state.push(FieldElement::zero()); + fn mix(state: &mut [FE]) { + let mut new_state: Vec> = Vec::with_capacity(P::STATE_SIZE); + for i in 0..P::STATE_SIZE { + let mut new_e = FE::zero(); for (j, current_state) in state.iter().enumerate() { - let mut mij = self.params.mds_matrix[i][j].clone(); - mij = mij.mul(current_state); - new_state[i] = new_state[i].clone().add(&mij); + let mut mij = P::MDS_MATRIX[i * P::N_MDS_MATRIX_COLS + j].clone(); + mij = mij * current_state; + new_e += mij; } + new_state.push(new_e); } - state.clone_from_slice(&new_state[0..state.len()]); + state.clone_from_slice(&new_state[0..P::STATE_SIZE]); } - fn permute(&self, state: &mut [FieldElement]) { - for i in 0..(self.params.n_full_rounds + self.params.n_partial_rounds) { - self.ark(state, i); - self.sbox(state, i); - self.mix(state); - } + fn hash(x: &FE, y: &FE) -> FE { + let mut state: Vec> = vec![x.clone(), y.clone(), FE::from(2)]; + Self::hades_permutation(&mut state); + let x = &state[0]; + x.clone() } - fn ensure_permuted(&self, state: &mut [FieldElement], offset: &mut usize) { - // offset should be <= rate, so really testing for equality - if *offset >= self.params.rate { - self.permute(state); - *offset = 0; - } + fn hash_single(x: &FE) -> FE { + let mut state: Vec> = vec![x.clone(), FE::zero(), FE::from(1)]; + Self::hades_permutation(&mut state); + let x = &state[0]; + x.clone() } - pub fn hash(&self, inputs: &[FieldElement]) -> Result>, String> - where - F: IsField, - { - let t = self.params.rate + self.params.capacity; - if inputs.is_empty() || inputs.len() >= self.params.n_partial_rounds - 1 { - return Err("Wrong input length".to_string()); - } + fn hash_many(inputs: &[FE]) -> FE { + let r = P::RATE; // chunk size + let m = P::STATE_SIZE; // state size - let mut state = vec![FieldElement::zero(); t]; - let mut offset: usize = 0; + // Pad input with 1 followed by 0's (if necessary). + let mut values = inputs.to_owned(); + values.push(FE::from(1)); + values.resize(((values.len() + r - 1) / r) * r, FE::zero()); - let n_remaining = inputs.len() % self.params.rate; - if n_remaining != 0 { - return Err(format!( - "Input length {} must be a multiple of the hash rate {}", - inputs.len(), - self.params.rate - )); - } + assert!(values.len() % r == 0); + let mut state: Vec> = vec![FE::zero(); m]; - // absorb - for input in inputs { - self.ensure_permuted(&mut state, &mut offset); - state[offset] += input.clone(); - offset += 1; - } + // Process each block + for block in values.chunks(r) { + let mut block_state: Vec> = + state[0..r].iter().zip(block).map(|(s, b)| s + b).collect(); + block_state.extend_from_slice(&state[r..]); - // squeeze - let mut result = vec![FieldElement::zero(); self.params.rate]; - for result_element in result.iter_mut().take(self.params.rate) { - self.ensure_permuted(&mut state, &mut offset); - *result_element = state[offset].clone(); - offset += 1; + Self::hades_permutation(&mut block_state); + state = block_state; } - Ok(result) + state[0].clone() } } -// Test values and parameters are taken from https://github.com/keep-starknet-strange/poseidon-rs/blob/f01ff35ab4dca63a9d6feb7ff3f46c9b04b28b04/src/permutation.rs#L136 -// (values are parsed from decimals and have been converted to hex in our mod) -// The field that these tests use is defined below, and parameters are stored under /s128b #[cfg(test)] mod tests { - use lambdaworks_math::{ - field::fields::montgomery_backed_prime_fields::{IsModulus, U384PrimeField}, - unsigned_integer::element::U384, - }; - use super::*; - - #[derive(Clone, Debug)] - pub struct TestFieldModulus; - impl IsModulus for TestFieldModulus { - const MODULUS: U384 = U384::from_hex_unchecked( - "2000000000000080000000000000000000000000000000000000000000000001", - ); - } - - pub type PoseidonTestField = U384PrimeField; - type TestFieldElement = FieldElement; - - pub fn load_test_parameters() -> Result, String> { - let round_constants_csv = include_str!("s128b/round_constants.csv"); - let mds_constants_csv = include_str!("s128b/mds_matrix.csv"); - - let round_constants = round_constants_csv - .split(',') - .map(|c| TestFieldElement::new(U384::from_hex_unchecked(c.trim()))) - .collect(); - - let mut mds_matrix = vec![]; - - for line in mds_constants_csv.lines() { - let matrix_line = line - .split(',') - .map(|c| TestFieldElement::new(U384::from_hex_unchecked(c.trim()))) - .collect(); - - mds_matrix.push(matrix_line); - } - - Ok(Parameters { - rate: 2, - capacity: 1, - alpha: 3, - n_full_rounds: 8, - n_partial_rounds: 83, - round_constants, - mds_matrix, - }) - } + use crate::hash::poseidon::starknet::PoseidonCairoStark252; + use lambdaworks_math::field::{ + element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, + }; #[test] - fn test_poseidon_s128b_t() { - let mut state = [ - TestFieldElement::new(U384::from_u64(7)), - TestFieldElement::new(U384::from_u64(98)), - TestFieldElement::new(U384::from_u64(0)), - ]; - let poseidon = Poseidon::new_with_params(load_test_parameters().unwrap()); - - poseidon.ark(&mut state, 0); - let expected = [ - TestFieldElement::new(U384::from_hex_unchecked( - "16861759ea5568dd39dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe8a", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "13827681995D5ADFFFC8397A3D00425A3DA43F76ABF28A64E4AB1A22F275092B", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "BA3956D2FAD4469E7F760A2277DC7CB2CAC75DC279B2D687A0DBE17704A8310", - )), + fn test_hades_permutation() { + // Initialize a state to test. The exact contents will depend on your specific use case. + let mut state: Vec> = vec![ + FieldElement::::from_hex("0x9").unwrap(), + FieldElement::::from_hex("0xb").unwrap(), + FieldElement::::from_hex("0x2").unwrap(), ]; - assert_eq!(state, expected); - } + PoseidonCairoStark252::hades_permutation(&mut state); + + // Compare the result to the expected output. You will need to know the expected output for your specific test case. + let expected_state0 = FieldElement::::from_hex( + "0x510f3a3faf4084e3b1e95fd44c30746271b48723f7ea9c8be6a9b6b5408e7e6", + ) + .unwrap(); + let expected_state1 = FieldElement::::from_hex( + "0x4f511749bd4101266904288021211333fb0a514cb15381af087462fa46e6bd9", + ) + .unwrap(); + let expected_state2 = FieldElement::::from_hex( + "0x186f6dd1a6e79cb1b66d505574c349272cd35c07c223351a0990410798bb9d8", + ) + .unwrap(); + + assert_eq!(state[0], expected_state0); + assert_eq!(state[1], expected_state1); + assert_eq!(state[2], expected_state2); + } #[test] - fn test_mix() { - let mut state = [ - TestFieldElement::new(U384::from_hex_unchecked( - "13f891b043b3b740cc3e1b3051127d335f08e488322f360a776b3810b7dc690a", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "1bd24b7cb99acf0dbea719ff4007bd60105bcefef21ec509d2f8d4f9bb6a3a1a", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "110853eb2ebee0d940454fe420229a2a0974e666d16c92bab9f36cbd1a0eded", - )), - ]; + fn test_hash() { + let x = FieldElement::::from_hex("0x123456").unwrap(); + let y = FieldElement::::from_hex("0x789101").unwrap(); - let poseidon = Poseidon::new_with_params(load_test_parameters().unwrap()); + let z = PoseidonCairoStark252::hash(&x, &y); - poseidon.mix(&mut state); + // Compare the result to the expected output. You will need to know the expected output for your specific test case. + let expected_state0 = FieldElement::::from_hex( + "0x2fb6e1e8838d4b850877944f0a13340dd5810f01f5d4361c54b22b4abda3248", + ) + .unwrap(); - let expected = [ - TestFieldElement::new(U384::from_hex_unchecked( - "1d30b34b465f8cddc8dc468f137891659c7e32b510cf41cec3aac0b26741681d", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "c445fa4dd2af583994272bede589b06b98fe9cd6d868bf718f6748ba6165620", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "1ed95ae0ea03bb892691f5200fb5902957ac17b3466afa62be808682801f97f9", - )), - ]; - assert_eq!(state, expected); + assert_eq!(z, expected_state0); } #[test] - fn test_hash() { - let poseidon: Poseidon = Poseidon::new(); + fn test_hash_single() { + let x = FieldElement::::from_hex("0x9").unwrap(); - let a = FieldElement::one(); - let b = FieldElement::zero(); + let z = PoseidonCairoStark252::hash_single(&x); - poseidon.hash_new_parent(&a, &b); + // Compare the result to the expected output. You will need to know the expected output for your specific test case. + let expected_state0 = FieldElement::::from_hex( + "0x3bb3b91c714cb47003947f36dadc98326176963c434cd0a10320b8146c948b3", + ) + .unwrap(); + + assert_eq!(z, expected_state0); } #[test] - fn test_permutation() { - let poseidon = Poseidon::new_with_params(load_test_parameters().unwrap()); - - let mut state = [ - TestFieldElement::new(U384::from_u64(7)), - TestFieldElement::new(U384::from_u64(98)), - TestFieldElement::new(U384::from_u64(0)), - ]; - - poseidon.permute(&mut state); - - let expected = [ - TestFieldElement::new(U384::from_hex_unchecked( - "18700783647721BB9AD092B176BBEB5348401C21132CCF83C30134DFAB5A2DEB", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "1CC8856652601B3C81139AD5EC13E4A3A8F4A5DB242555521A09E002E7A10B2B", - )), - TestFieldElement::new(U384::from_hex_unchecked( - "3DCB1CEC811FC2D7401CA7B9B084D167F33B6983D4428C8E0534C9C3CECF46D", - )), - ]; - - assert_eq!(state, expected); + fn test_hash_many() { + let a = FieldElement::::from_hex("0x1").unwrap(); + let b = FieldElement::::from_hex("0x2").unwrap(); + let c = FieldElement::::from_hex("0x3").unwrap(); + let d = FieldElement::::from_hex("0x4").unwrap(); + let e = FieldElement::::from_hex("0x5").unwrap(); + let f = FieldElement::::from_hex("0x6").unwrap(); + + let ins = vec![a, b, c, d, e, f]; + let z = PoseidonCairoStark252::hash_many(&ins); + + // Compare the result to the expected output. You will need to know the expected output for your specific test case. + let expected_state0 = FieldElement::::from_hex( + "0xf50993f0797e4cc05734a47daeb214fde2d444ef6619a7c1f7c8e0924feb0b", + ) + .unwrap(); + assert_eq!(z, expected_state0); + + let ins = vec![a]; + let z = PoseidonCairoStark252::hash_many(&ins); + let expected_state0 = FieldElement::::from_hex( + "0x579e8877c7755365d5ec1ec7d3a94a457eff5d1f40482bbe9729c064cdead2", + ) + .unwrap(); + assert_eq!(z, expected_state0); + + let ins = vec![a, b]; + let z = PoseidonCairoStark252::hash_many(&ins); + let expected_state0 = FieldElement::::from_hex( + "0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7", + ) + .unwrap(); + assert_eq!(z, expected_state0); + + let ins = vec![a, b, c]; + let z = PoseidonCairoStark252::hash_many(&ins); + let expected_state0 = FieldElement::::from_hex( + "0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082", + ) + .unwrap(); + assert_eq!(z, expected_state0); + + let ins = vec![a, b, c, d]; + let z = PoseidonCairoStark252::hash_many(&ins); + let expected_state0 = FieldElement::::from_hex( + "0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d", + ) + .unwrap(); + + assert_eq!(z, expected_state0); } } diff --git a/crypto/src/hash/poseidon/parameters.rs b/crypto/src/hash/poseidon/parameters.rs index a09a150e4d..e2eb064588 100644 --- a/crypto/src/hash/poseidon/parameters.rs +++ b/crypto/src/hash/poseidon/parameters.rs @@ -1,82 +1,27 @@ -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::BLS12381PrimeField, - field::{element::FieldElement, traits::IsField}, -}; - -type PoseidonConstants = (Vec>, Vec>>); - -pub struct Parameters { - pub rate: usize, - pub capacity: usize, - pub alpha: u32, - pub n_full_rounds: usize, - pub n_partial_rounds: usize, - pub round_constants: Vec>, - pub mds_matrix: Vec>>, -} - -/// Implements hashing for BLS 12381's field. -/// Alpha = 5 and parameters are predefined for secure implementations -impl Parameters { - // t = 3 means width of input is 2 - // sage generate_params_poseidon.sage 1 0 381 3 5 128 - // Params: n=381, t=3, alpha=5, M=128, R_F=8, R_P=56 - pub fn with_t3() -> Result { - let round_constants_csv = include_str!("bls12381/t3/round_constants.csv"); - let mds_constants_csv = include_str!("bls12381/t3/mds_matrix.csv"); - - let (round_constants, mds_matrix) = Self::parse(round_constants_csv, mds_constants_csv)?; - Ok(Parameters { - rate: 2, - capacity: 1, - alpha: 5, - n_full_rounds: 8, - n_partial_rounds: 56, - round_constants, - mds_matrix, - }) - } - - // t = 2 means width of input size is 1 - // sage generate_params_poseidon.sage 1 0 381 2 5 128 - // Params: n=381, t=2, alpha=5, M=128, R_F=8, R_P=56 - pub fn with_t2() -> Result, String> { - let round_constants_csv = include_str!("bls12381/t2/round_constants.csv"); - let mds_constants_csv = include_str!("bls12381/t2/mds_matrix.csv"); - - let (round_constants, mds_matrix) = Self::parse(round_constants_csv, mds_constants_csv)?; - - Ok(Parameters { - rate: 1, - capacity: 1, - alpha: 5, - n_full_rounds: 8, - n_partial_rounds: 56, - round_constants, - mds_matrix, - }) - } - - pub fn parse( - round_constants_csv: &str, - mds_constants_csv: &str, - ) -> Result, String> { - let round_constants = round_constants_csv - .split(',') - .map(|c| FieldElement::::new_base(c.trim())) - .collect(); - - let mut mds_matrix = vec![]; - - for line in mds_constants_csv.lines() { - let matrix_line = line - .split(',') - .map(|c| FieldElement::::new_base(c.trim())) - .collect(); - - mds_matrix.push(matrix_line); - } - - Ok((round_constants, mds_matrix)) - } +use lambdaworks_math::field::{element::FieldElement as FE, traits::IsPrimeField}; + +/// Parameters for Poseidon +/// MDS constants and rounds constants are stored as references to slices +/// representing matrices of `N_MDS_MATRIX_ROWS * N_MDS_MATRIX_COLS` and +/// `N_ROUND_CONSTANTS_ROWS * N_ROUND_CONSTANTS_COLS` respectively. +/// We use this representation rather than an array because we can't use the +/// associated constants for dimension, requiring many generic parameters +/// otherwise. +pub trait PermutationParameters { + type F: IsPrimeField + 'static; + + const RATE: usize; + const CAPACITY: usize; + const ALPHA: u32; + const N_FULL_ROUNDS: usize; + const N_PARTIAL_ROUNDS: usize; + const STATE_SIZE: usize = Self::RATE + Self::CAPACITY; + + const MDS_MATRIX: &'static [FE]; + const N_MDS_MATRIX_ROWS: usize; + const N_MDS_MATRIX_COLS: usize; + + const ROUND_CONSTANTS: &'static [FE]; + const N_ROUND_CONSTANTS_ROWS: usize; + const N_ROUND_CONSTANTS_COLS: usize; } diff --git a/crypto/src/hash/poseidon/s128b/mds_matrix.csv b/crypto/src/hash/poseidon/s128b/mds_matrix.csv deleted file mode 100644 index d3c6af6e3d..0000000000 --- a/crypto/src/hash/poseidon/s128b/mds_matrix.csv +++ /dev/null @@ -1,3 +0,0 @@ -cf637e74647c1797112d9a892ab349f752a41d0786fe3ccfe8aa8b19100e8c2,1126899aecd525518a149bd72873b63a2e63ddce2beeb008b174602d5627e37a,12c89bb904f498c2b93b27b25de1ecb92fceed11ac0edc9e4fa579ab59dbbb68, -5755e4439812879f5de90df763ea97b39ec65d17d51029fa43766692a018caf,e40fb0bb50b31f195cd880c9fa733623e9ade8ba440998a89648cfa4986d1fe,58516197c95d98f783f2e5f2cfb295278c521f10e79022aed0a4e95234400f5, -4d1d1386e0cf786cbe76072195538fde6ad46f5d2a56130a3eebb204210c046,173b460d8dcb542a860598f41981a1be0a33473a94dc67168c5a4ab66d5d29eb,13c5fbb65e8c090e2395fb03b71da4ad138e18486882a863f9a090fbe53c063d, diff --git a/crypto/src/hash/poseidon/s128b/round_constants.csv b/crypto/src/hash/poseidon/s128b/round_constants.csv deleted file mode 100644 index ffa5cbc400..0000000000 --- a/crypto/src/hash/poseidon/s128b/round_constants.csv +++ /dev/null @@ -1 +0,0 @@ -16861759ea5568dd39dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe83,13827681995d5adfffc8397a3d00425a3da43f76abf28a64e4ab1a22f27508c9,ba3956d2fad4469e7f760a2277dc7cb2cac75dc279b2d687a0dbe17704a8310,1e26c47a7d421f24f13c4282214aa759291c78f926a2d1c6882031afe67ef4dc,f8985f8e16505145bd6df5518cfd41f2d327fcc948d772cadfe17baca05d6a7,1d427f108675136e204c659875341243c6e26a68b456dc1d142dcf3434169714,15af083f36e4c70f454361733f0883c5847cd2c5d9d4cb8b0465e60edce699dc,fd71701bde3cf8e54fa3f74f7b352a52d3975f92ff84b1ac77e709bfd38888f,603da06882017d49c26f8a6320a1c5eac1b64f699ffea44e39584467a6b1d4d,1c332a6f6bde2f1f8e79ce13f47ad1cdeebd8870fd13a36b613b9721f6453a63,1d3d0ebf61664c9b5310a04c4dec2e7e4b9a813aaeff60d6c9e8caeb5cba78ea,d346a688948442c5ae5ebcb88028d2a6c82f99f928494ee1bfc2d15eaabfecf,1d50a9e24176501da7631ccaecb7a4ab8694ab61f238797098147e69dd91e5af,1a19dcccb783b05aaa62773fedd3570e0f48ad3ed77c8b262b5794daa2687018,1cb085eb1df424933453cc97445954bf3433b6ab9dd5a99592864c00f54a3fac,d3e8a8e8a404bad3af2bf3c03e420ea5a465939d04b6c72e2da084e5aabb797,15ca045c1312c00b1bd14d2537fe5c19fb4049cb137faf5df4f9ada962be8cb3,7c74922a4567ec444997e959f27a5b06820b1ed97596a969939c46c16251806,8c0bba6880d2e3d6bf5088614b9684ff2526a20f91670435dc6f519bb7ab843,4526bcaec43e837d708dd07234c1b2dc1a6203741decd72843849cd0f879353,9cc9a17b00d3564d81efaea5a75a434bef44d92edc6d5b0bfe1ec7f01d613f7,828b1e269b84bd912aa8cdbead0bc1ce1eb7284e2b28ed90bc7b4a4fde8f026,1e2af2f41d76c3f01d9a2482fbdaf6590c19656bcb945b58bb724dc7a994499c,1dcfd7e44946dad9b2618213b0d1bf4a2269bed2dc0d4dbf59e285eee627df1d,7ff2afb40f32e6456fdd1b94da8d3bbcf0312ab9f16ac9bc31955dc8386a75c,1dcd236bdc15b4fc83e90bab8ae37f8aab40efae6fa9cd919b3248ee326e92a5,5463841390e22d60c946418bf0e5822bd999084e30688e741a90bbd53a698a,1a4c940fff3fe757b2021f13eb4d71747efd44a4e51890ae8226e7406144f81d,4e50cb07b3872728dc88f05393d9d03153ca4c02172dd1d7fc77d45e1b0455e,e2ca053e4da0f257b430e53238d2bab1d9b499c35f375d7d0b32e1189b6dcbf,f19f20ac59d1da1aaf37fe0b851bc2419cd89100adff965951bff3d3d7e11a1,17645ca5e87a9f776a82fe5bb90807f44050ac92ca52f5c798935cf47d55a902,95b8aeaca96aab0200eed38d248ecda23d4b71d17133438015391ca63663771,1853d94dbbca7bf5aa8252f106292ac3b98799e908f928c196c1b658bf10b2f1,1a8f90b403e24034c6f4c0a3b70edbb3942b447c615c0f033913831c34de2d2d,2485167dc233a02e1161c4d0bf025159699dd2feb36e3e5b70ae6e770e22096,19c8b08a90d6ed89ff7de548541dd26988f7fdaacdd58698e938607a5feca6f7,105c3bf5cba23fc66b75e79d146f9880c7c4df5ecdad643ce05b16901c48830,a38019787f4cc1c627a65a21bef2106d5015b85dfbd77b2965418b02dbc6bd8,15e624d7698fd09b73dce29a5f24c465c15b52dec8172923a6ebc99a6ddc5ed,5d3688ba56f34fdf56bc056ad8bf740ca0c2efef23b04a479f612fde5800a0a,a29abdef3fef7839e67ed336e82dc6c2e26d872d98b3cce811c69ae363b4451,1be8096ecfcbce15ee400801a56f236db2c43d1e33c92b57ac58daf2d3fc44de,1bad5fec670d6ec8108d605aae834c7ce6a7cd4e1b47bf6a02265352c57db9d5,fcf4598c0cf131d75877afdbb4df6794ef597fff1f98557adca32046aeaef1a,58aecc0081b545f4a4d1c4c8f27932e4170c37841fef49aca0ec7a123c00adf,f57b4b7ee98dffe5460b71995790396e4ef3c859db5b714ec09308d65d2ca6b,16b82800937f87fff3cd974f43322169963d2b54fd2b7ed348dc6cc226718b74,103a915b1814709473427e34ab8fbb7ca044f14088fedae9606b34a60b1e9c66,d4afbf1bd98ff28f9bc01028ff44195c0bb609d367b76269a627689547bfbff,1de1ceb846fe12b1b9524c7d014931072c3852df2d991470b08375edf6e762d3,ff751f98968213fbe5dff3ce06e8cb916709e0c48e3020c6b2b01c1bec0814c,36f6b64463f7afdfc3180616e340536bea7f01d226b68b6d45cd6dfbff811f3,161135c9846fadf3b4511d74fe8de8b48dd4d0e469d6703d7ed4fe4fe8e0dbc0,8b58921a3fbdb4e59b78f6acfca9a21a4ba83cc6e0ae3527fbaad907fc912bf,22a4f8a5cdc7438b9d16b61c2973847211d84eb2fb27b816e52821c2e2b1b21,c1cf6db5d6145fefeccbbc9a50b2ceedeb1765c61516ffcb112f810ad670370,10be44689973d9e51cfc05fa8f4aec6fac6a0ff2fdfab744ade9de11416b6845,1b9bf209c4e117606489cda45128096d6d148a237142dc4951df0b8239be1497,1a09cf541e5f74f32b93310b8ce37b092a58282643860b5707c7eb980ea03a0c,6b562e6005f334a0bdc218ba681b6ba7232e122287036d18c22dd5afa953282,180e8103a23902b55dc6d5f59253a627a2a39c8aca11a914670e7a35dea38c95,6a3725548c6648506bdc1b4d5f9bed83ef8ca7468d68f4fbbf345de2d552f78,e7fcd6997472d37605d0f01a8eccc5f11a45c0aa21eb4ebb447b4af006a4a4a,a6144c95c8de1d04075784d28c06c162a44366f77792d4064c95db6ecb5d006,15b173c8b0eb7ebe4b3a874eb6307cda6fd875e3725061df895dc1466f35023b,17e1c2d6fde8ac8587bae06ad491d391c448f877e53298b6370f2165c3d54de0,1cdb779f3e5b7367996f451b156fe4e28f74d61e7771f9e3fa433b57ca6627b8,bb930d8a6c6493713435ec06b6fed7825c3f71114acb93e240eed6970993e9,c472d73b28304fed708467e9296fb5599d3a08814c31c4189e9579c046e8796,1fba9c303dfee1679e10e3c883ca5ce5614d23739b7cb2052cc23612b11170fa,121c0e3319ede3390425dc9b2c1ed30e6356cb133e97579b822548eb9c4dc4cb,acfd61139e50c4a37b09933816e2a0932e53b7dc4f4947565c1d41e877eb1a7,dabea18941a47e3844544d92ee0eca65bdd10b3f170b0dc2f30acd37e26d8fd,1f7088fdb015c7137a6265e44fef6f724ea28ae28b26e6eee5a751b7ce6bcc2d,babdc9d677230535b3e3c43cfd443076b4ce33cddbc8446120dce84e6122b80,a250f430b7fe72e2e5d00b6b83e52a52ca94879ccfab81a7a602662c2d62c57,1dc92ef479c11a801fb24ef76d57912b12660e7bd156d6cabbb1efb79a258630,1235ec59739163f9510f616fa8b87900fd08fd4208a785cffcf784a63a0fd5d1,1ced4e872eb7e72d207be77e9d11e38f396b5c0ba3376e855523c00b372cc66e,15f9406febca3823b756ef3f6331890b3d46afa705908f68fb7d861c4f275a23,19d9c501d9ff1efd621a9f61b68873c05f17b0384661f06d97edf441abdaa4ac,14b0de22bbd0a43f34982c8e28d2f6e169e37ba694774c4dfa530f41c5359542,9b4d48bd38a3e6b02186aabb291eca0d319f0e3648b2574c49d6fd1b033d913,f558bbea55584d01725d8aa67ddba626b6596bbd2f4e65719702cefcead4bac,11108f1a9500a3e9561ea174600e266a70b157d56ece95b60a44cf7a3eef17d2,8913d96a4f35c12becb92b4b6ae3f8c209fb90caab6668567289b67087bf6c,1e502262c51ad8ed16926346857dec8cca2e99f5742b6bf223f4d8a6f32867ac,fcb5fcdc00891662889280505c915bde962ea034378b343cd3a5931d2ec0e62,12eb919524a898a4f90be9781a1515145baea3bc96b8cd1f01b221c4d2a1ce9e,58efb6272921ad5eada46635e3567dced0662c0161223e3c1c63e8de3ec3d7f,1e2fcd49ca9c7452b436d205ffc2a39594254a1ac34acd46d6955e7844d4f8a3,e3589533083872be62d9acce0b625f885e5941e54bd3a2106fcf837aef5314b,7da445b81e9b31f6d47a5f4d23b92a378a17f119d5e6e70629f8b41fefb12ec,2b22dab62f0817e9fc5737e189d5096a9027882bef1738943b7016256118343,19af01472348f224bacdfed1d27664d0d5bdea769be8fcb8fbef432b790e50ed,76b172dbbeec4b31de313b9390f79ec9284163c8e4986bc5b682e5ac6360315,1070efaeae36f6590f362f6cb423d2009b30ddb4178d46def0bdb2905b3e086a,186cb99b36e5203b0a39872686b84ee1d28c4942b8036a1c25a0e4117ccaeef7,1a9fd44305a5a99e0bbf9674e544bda0fb3d0fe5bb3aa743fd1b8a4fc1dc605b,106b447ded1045663629b184d8c36db3a11a6778d8848142aa6363d6619f977b,642a8b4be4ba722cbfcf55a77339b5d357cceb6946fdc51c14b58f5b8989b65,1c89e0a26f65a0f5cc6cc6aa5b6e775cbc51a73700bd794a7acd79ae1d95883c,bb19d4ef195967cbf78ab5dc2fd1d24816428f45a06293c1b9d57b9a02e920d,1fd2dd994756ea86a576b74790b2194971596f9cd59e55ad2884c52039013dfe,1922810cc08f493f300df869823b9f18b3327e29e9e765002970ef0f2e8c5f9,52f3afaf7c90f3f1d46e1d79a70745b39c04376aafff05771cbd4a88ed418b8,17ccfc88e44a03fda95260f44203086e89552bbe53dcc46b376c5bcab6ea789f,a949125939e69464100228beff83823f5157dd8e067bc8819e40a1ab008ddb2,16cb64e3a0d37a504273ce4ee6929ba372d6811dde135af4078ba6e1912e1019,10d63b53707acf3362f05f688129bf30ad43714257949cd9ded4bf5953837fb6,18bcb1549c9cab70d13bb968b4ea22d0bb7d7460a6965702942092b32ef152dd,13d1c5233657cddbf5ead698fe76f6492792a7205ba0531a0ca25b8d8fe798c9,12240b9755182e57066c2808b1e16ea448e26a83074558d9279f450b79f97521,8cc203d8b0f90400fe8e54f343cef59fe8d70882137de70c9b43ab6615a6476,310c6cc475d91a2e061bacdc175ea9e119e937dea9d2100fa68e03c1f779120,ff84b639f52e45920bc947defced0d8cbdbe033f578699397b83667049106d7,1584ca7f01262b51d89c4562f57139f47e9f038cb32ec35abe4e1da8de3e165b,1135eefaf69b540af7d02f562868be3e02fdc72e01e9510531f9afa78abbbf3,372082b8a6c07100a50a3d33805827ad350c88b56f62c6d36a0d876856a99e8,17c3c12b819a8acf87499bac1a143fc59674f132e33898f0c119e3d12462dfe8,4f1354c51e8f5a05b84157cfeff6822c056ce9e29d602eb46bd9b75a23836db,ada9f26a82714c6075739ba206507a08ac360150e849950ef3973548fbd2fe0,1287173956a2bd2f11b5ec29195e38cc3f6a65ff50801aa75fd78dd55070285a,7273101c190fe38212420095a51c8411c7f3227f6a7a4a64ae6ba7f9201e135,2dbf2a6b56b26d23ebeb61e500687de749b03d3d349169699258ee4c98005fc,885b6cbb29739b7808e67f00ab89b52ab89ef8d92530394e4b910efd706c7fc,bd55b5f1171ef731dacbcbadfd5b910b493fa9589fd937e3e06ce26b08925aa,10aaedaa6ef2f9667d16b3b295410c0e44f7a2f8135c207824f6ae2a9b16e91d,6aca6ebf70b1c006c6331e9f1a5c4cc89b80f8adc5d18915c1cd0d496ccf5ea,1678602af36a84abb010f831d403d94d5e90003e6d37c677e9dd157fb27776,2022036bdf687b441b547fefdf36d4c2cd3f4b0526a88aafe60a0a8f508bad5,87bfc350957c979ca664397414bdfb8f9b8dfe49fb63e32353d4e2e8d1d4af7,12d639cbd418ca95c24ea29ccd1d15ab81f43a499b27a06d3c5e2176f7ad79c0,ecdea7f959a3e488403d5b39687a1fe0dee3369e5fbc0f4779569f64506e18,bf656bdc4fefda3b70658e2f1992ef9f22e5f2d28c490e21d4e34357154b559,10d1b8cb1561ee7d2319638ccab9033dfec47596f8a6f4ce6594e19fddd5925c,1758ffc77c62e312f86ef6ea01545ad76f281ec2941da7222d1e8b4e2ec1f1a0,1a0315ca07956f6e995386e96aeaa1b4596aacd28f83c32f29a591c95e6fcadd,3e55cf341e7c280cb05f3d6ff9c8d9f2cfe76b84a9d1b0f54884b316b740d8d,14d56feb32cde735eede9749739be452e92c029007a06f6e67c81203bf650c6d,1cee807aa678a8ab33b6171eaa6a2544497f7599fb8145d7e8089f465403c8ad,1a5d2bacc8f1ed4048cb5f394de2cb6e1f365e56a1bc579d0f9a8ad2ef2b3836,1df573de597ce1679fc20051f6501268cd4b278811924af1f237d15feb17bd4f,b0297c3c54a4ecc5826a280e053cf7a3c1e84a1dcf8b33c682cf85ddac86e01,1af5e9c47c9a862343c7526a59783f03c6bc79b69b8709fe6a052b93a8339af7,19bf75c7a739da4829f9c23065ff8ccb1da7deec83e130bcd4a27a416c72b854,e0563d5f852ad6c5989017bd5c4cfdc29cd27fc4e91eeabdb8e864df3c3c685,fa4b1d70885a92b0969635468daec94f8156c20e3131bd71005be1cd16ccfb1,1b47bb025695e416f1e201cd62aa4600b8b85cf718cd1d400f39c10e59cc585e,e783ab1e1ef97189e7f9381eb6ab0de2c4c9c2de413691ba8aa666292e9e221,933e0280c6de7b77b3870a07823c081fd9c4cb99d534debd6a7bfb4e5b0dd59,8865d450ce29d212fb5db72460b3560a2f093695573dff94fd0216eb925bef6,11de023f840e053035526dabacf0dee948efba06bcbb414ecd81a6b301664e5c,855fc1e341bfcdc805015a96f724c5ac7cc7b892a292d38190631ab1a5388d4,12df6557bfd4a4cde7b27bf51552d2b5162706a3e624faca01a307ef8d53285d,913a8a66962cdddd92a6bd3e9c1d55ef8f226da95e4d629046d73d0507f6275,1a71577d6ee9f9027f2c889874ba5b44ca1076033db5c2de4f3367b08c008e68,b396b33911218d7b0365c09348a561ef1ccb956fc673bc5291d311866538581,19e1392f2da08450c8a7d89e899189306170baa3c3436e6a5398f69c8f321648,1e6154508103200adf118e1d6e7c61a333e313b1a9a5b6d69c876bd2e7d694d0,6b14294e71cd7bf776edbd432d20eb8f66d00533574e46573516f0cacdeec8b,f252fbbb06c2769338b1c41df31e4e51fe2a18e2406c671915cab6eb1a1d4ff,13ccf71be7cc2abcbcf5a09807c69679430c03645747621b7f5327cb00ff99dc,a9778dc707503a36a9f7c97b4ceef0a9b39001d034441617757cd816dac91ad,1b9473f6f06bb82d33590d34e3bae36e491f7bbf86a26aa55a8f5b27bb98d4dd,7ba7c32f875b62b895caa0215f996fd4ad92bab187e81417063dde91c08c033,1b7c1367e49cbf0703b22aac82abf83b0ed083148a5f4c92839e5d769bdab6c5,1dc9eb899931d1fbb53ffcf833cdfa05c2068375ff933eb37ae34157c0b2d963,15f6054a4d486938c27772fb50a7d2e5c1557ffdc1ffd07331f2ca26c6e3b669,20e6d62a2fe0f35b0fab83e8c7d1e8bfd0fec827960e40a91df64664dcd777d,16290a56a489abd0120c426fe0e409c2ff17adf51f528cafb0d026d14ffd6ac3,1b703f16f99033332267a6f7ece342705a32ca4c101417286279f6fc315edc8e,5194962daf666e79a0c32b5a9a307ba92e2c630f70e439195b680dd296df406,e8eae20a79a7c1242c34617b01340fb5fd4bea2aa58b98d2400d9b515ee5e2,b69058169d62f3aae28bfb28def7cd8d00dd7c2894fae4ffec65242afa5cd58,418c963bc9717f274077503ee472f22cfdff0973190ab189c7b93103fd78179,168d07a3eefc78865b28b3f4dc93167fb8c97112d14a25b4d4db55972015638e,1d17e892228df1dbf15a3c4241c98ba25ba0b5557375003f8748583a61836384,5cc0f0f6cf9bda4a150116e7932f8fe74ac20ad8100c41dc9c99538792e27a7,53d5d7863434afe29bdb1f8a648e4820883543e821f0f5c1668884c0be41eda,18a158126b89e5f3a600bf53f8101707b072218912dd0d9df2528f67de24fe04,1eb53b80726538b1e582069a698323d44c204bed60672b8d8d073bed2fede506,11097fb448406b606de0877efd58c01be53be83bde9601a9acc9e0ca2091fda5,18cbc0ff7239d2f53902396389d67b3049ce1fefde66333ce37ca441f5a31bf8,1f9a3d91dd8a2f2b632eb43d57b5c5d838ceebd64603f68a8141ebef84280e8a,823fb472fe573bc5300f74e8f6de8fe1185078218eceb938900e7598a368dcc,17ac73134016d2caa4c63a6b9494c0bd7a6ba87cc33e8a8e23ebda18bfb67c2c,199a16068c3eab2b03f1b5c5ee2485ccc163d9ab17bb035d5df6e31c3dcf8f2c,1f24b4356a6ba954d4ef9fd1634752820ee86a925725ac392134d90def073fc,803e44e7f7aed13add59b6b4d11c60a528fb70727f35d817305971592333d4c,df93b02f82672bd14535a511ed3eb4fe85987ae57bc9807cbd94cd7513d3961,8f0a0a88db990b4d71c3d51d4197fa3fd1cc76e670607e35ca2d3bada295250,13432226916d3125acac1e211431fd4cd2b6f2e80626af6564bdde3e77608dbe,1d5625941bfea5838175192845a7ad74b0b82940ef5f393ca3830528d59cf931,8ddf48695b204587dfe4f8cb3ef1b39783e9b92f9276b858e2e585e318e20a5,a60730a657ff88c8851a679ab2a1490434ee50d4953e7c5d3194578b08ae8ea,1ccfd231373aa37496283840bdb79ba6d7132775b398d324bcd206842b961abb,1b203843c41cd396f14fa0bc0b2191a27ebc659e74fd48f981e963de57eff26c,802c2f6ae5624a6fb8435d1c86bf76c260f5e77a54b006293705872e647cc4a,f80225456e63770b3e561384ef2e73a85b0e142b69752381535022014765f1c,7f602ec1a80a051fd21b07f8e2960613082fc954b9a9ff641cc432a75c81887,162561b0a0a720f3b60f6aaf7022b7d323fe77cd7c1ab432f0c8c118ca7e6bde,1604fe5a6a22337ca69b05dea16b1cf22450c186d093754cb9b84a8a03b70bd6,11cf9987a40446c0d3dc140bf5f9b76f6eada5995905189f8682eaf88aef2b83,16bc0b2487c1ed883db47a4bdd60cf69debee233e91b50e9ee42ce22cbfbacd3,af5dbb5055eb66aa11403b93e90338b7620c51356d2c6adcbf87ab7ea0792f3,446328f4dddacc129743c43883d59c45f63b8a623a9cf318489e5fc4a550f76,4ba30c5240cde5bca6c4010fb4b481a25817b43d358399958584d2c48f5af25,df5275f76425982c89209117734ae85708351d2cf19af5fe39a32f89c2c8a9f,d76f3b5156f45d0e18c7f98df3b2f7b993cdda4eb8cb92415e1be8e6af2fc2d,191dc3f15cba92bded5a44b55a5b026df84a61719ed5adbb93c0e8e12d35ef40,44c40e6bd52e8a2d9896403ae4f543ae1c1d9ea047d75f8a6442b8feda04dd0,1836d733a540012bd0ccbf4974e80ac1954bf90fe9ea4e2c914ad01166026e7,bc553be9776b585a8159d306ef084727611df8037761f00f84ca02ce731b3b6,186ce94781c1a1fada1c7b87e0436b1b401ae11a6d757843e342f5017076a062,1381ec71fbdef2480253be9f00f4e6b9e107f457812effb7371cc2daa0acd0fb,9844da9cc0ee9856490d847320d9f3cd4fb574aa687bafdfe0ffa7bf2a8f1b4,7a8bf471f902d5abb27fea5b401483dedf97101047459682acfd7f9b65a812f,1633b6fb004de51a41915fb51ac174456f5a9cdff7aecb6e6b0d063839e56338,1979ee5cec4961c7771200382bfc6d17bbe546ba88fed8b17535fd70fbc50ab9,a806c07861857f3ea9891b42d565256b0312446f07435ac2cae194330bf8c58,c38703d9487079390c7a6b8af194b8b603bb2cdfd26bfa356ac9bb6ee0413a3,1a4446628f5602587153bd3a482b7f6e1c56f4e02225c628a585d58a920035b8,1cc2a76e5ce83177b0685cdeeea3a253ae48f6606790d817bd96025e5435e271,1f8a2332352098c492933c079b148aed57d5e4ce1ab122d370983b8caa0e030c,f9ca6c5e102598e51144ea5937dd07cadce1aa691b19e6db87070ba51ec22d6,16b2e4a46e37ae6ef952d9d34f8d6bd84a442ebfd1ac5d17314e48922af79c6b,305d6cd95cc1beb6805d93d3d8d74e1ca7d443f11e34a18e3529e0d03435d1,1e097b4b8b90dacfb39743ed23f8956cabb7aea70cc624a415c7c17b37fbf9b2,1064e1b3f16c25be845bdb98373e77dad3bdcc90865b0f0af96288707c188950,1e49fafe673f21a123384d841221b73421c56014af2ffdf57f1579ae911fd33e,1fd806dccbf1a1346b294404e849722f2baa2f4d19005a49d1ba288a77fefe45,1d951a37da53e3eec0b3e2db1a9a235d7a03f48f443be6d659119c44aafc7525,ed87fa479fb59274d1912c3554ae3d010496a31bdacb542c816a1607a907735,9451cccd4200f06d473ad73466b4e8c0a712a0b12bb6fc9462a3ac892acc9bc,bca1b6400b3e52107642535f1ca9b03832ca0faa15e1c4ed82dd1efdc0763db,1d2c55735b2f0a2060ad1516a8f13592b0dd024ff4162539f993a99c7a1a4d9e,fe04de60aa8008ff0149d1dee29617de750bd5ce3e9fa5e62951d65f6b924d7,18271784e6920a9be47c4c8fab71c8f8303ef29e26f289223edf63291c0a5498,dc7c19061a84d2e60a04b8f0adaa603c8afe93f17b7f0e56b49514af43d0c6d,1972db5affe7823e419da337cb79061e090943c2959dea1b38e4436f5482eb16,1518b7975a6d8c270eac9fe4082916f021a7ecbadf18809746a9e061a2cb9467,120c5539dc45dc10d4bbc2440a9f5061d74b8ae5e37b34e8755a0315f1e196ef,19ea6f5fb309fa7d08bc7d516e80efc3a977b47208283cf35a9d8bc213b90b17,d0ce323c5128d9cfdd8ddd8ba9cfe2efd424b5de167c7257d1f766541e29df1,401e37d0e2763a3695538b41d3c28215b865f5b7d1b497a8919284c613cb7ed,e45a0de30acc2e67f2893056fc5880255daa12cc61261cc0fab9cf57c57397f,69bc3841eb0a25cd9e988d75f09f698d4fdc9d0d69219f676b66ae7fa3d4964,102684bbe315ad124bdd47c38fe72db47cf0ae0c455cda5484baf523f136bdcb,11e0f83c547c96c68202e8d34e5595a88858c2afa664365e4acb821fd8a13fa,1caf4a7635f8c6585966567ceec34315d0f86ac66c1e5a5ecac945f1097b8301,cfba58cf8aaf4223cb7158908ccc18b1dc48894d2bb46225c72b11f4c74b278,397c4c1691159a28cc90da2e664f8c29a7f89be0ead679a38b0f44c8a2a0e35,6563b9ebb643a5bad397fa5dd13c501f326dd7f32be22e20998f59ec7bad11,b76edb238f7b641ea81d307f4c79f9afec48562076dd09c36cd79e9cb817166,60d4208bb50ea9df29ed22addcd50a1b337504039690eb858584cda96e2e067,1ea37d569d2fbb7adbff1019dc3465ec0f30da46918ab020344a52f1df9a9222,d3b174c7290c6bf412083ff35d23821dc512f1df073c1b429130371ac63b1a,a26ed3d7634762a4b46eb2a5c3b814634d974919689fb489fe55e525b980383,15f3997e7dafcb13e0e7a23d33d2fd9ef06f4d79bd7ffa1930e8b0080d218518,17c5eec716d944ee434df335a10bbac504f886f7f9d3c1648348c3fae8fdf161,53cc30d7fe0f84e7e24fd22c0f9ad68a89da85553f871ef63d2f55f57e1a7c,368821ee335d6dc19b95769f47418569474a24f6e83b268fefa4cd58c4ec8fd,85334f75b052b5f35119816883040da72c6d0a61538bdfff46d6a242bfeb7ab,dd0af4fcbd9dfefc1020cca9d871ae68f80ee4af2ec6547cd49d6dca50aa438,1b0131bce2fba4e84114a19c46d24e00b4699dc00f1d53ba5ab99537901b1e71,5646a95a7c1add2b34c0750ed2e641c538f93f13161be3c4957660f2e78896e,14b9f291d7b430ad9fac36230a11f43e78581f5259692b52c90df47b7d4ec01f,5006d393d347fc81a98f19127072dc83e00becf6ceb4d73d890e74abae01a22,e2c9d42199f3a470e7cb8a115143106acf4f702e6b346fd202dc3b26a679d8d,1d1274d092db5018f180b1a8a13b7f2c7606836eabd8af54bf1d9ac2dc5717b1,1e1fc552b8eb75247ad0fb7aaa4ca528f415e14f0d9cdbed861a8db0bfff0c6a diff --git a/crypto/src/hash/poseidon/starknet/mod.rs b/crypto/src/hash/poseidon/starknet/mod.rs index 1b3947acbf..0aba345722 100644 --- a/crypto/src/hash/poseidon/starknet/mod.rs +++ b/crypto/src/hash/poseidon/starknet/mod.rs @@ -1,245 +1,2 @@ pub mod parameters; -pub mod round_constants; -// mod starknet_poseidon; -use self::parameters::PermutationParameters; - -use lambdaworks_math::field::{element::FieldElement, traits::IsPrimeField}; -use std::ops::{Add, Mul}; - -#[derive(Clone)] -pub struct Poseidon { - params: PermutationParameters, - // Suggestion: Add the state here -} - -impl Poseidon { - pub fn new_with_params(params: PermutationParameters) -> Self { - Poseidon { params } - } - - pub fn hades_permutation(&self, state: &mut [FieldElement]) { - let mut round_number = 0; - for _ in 0..self.params.n_full_rounds / 2 { - self.full_round(state, round_number); - round_number += 1; - } - for _ in 0..self.params.n_partial_rounds { - self.partial_round(state, round_number); - round_number += 1; - } - for _ in 0..self.params.n_full_rounds / 2 { - self.full_round(state, round_number); - round_number += 1; - } - } - - pub fn full_round(&self, state: &mut [FieldElement], round_number: usize) { - for (i, value) in state.iter_mut().enumerate() { - *value = &(*value) + &self.params.round_constants[round_number][i]; - *value = value.pow(self.params.alpha); - } - self.mix(state); - } - pub fn partial_round(&self, state: &mut [FieldElement], round_number: usize) { - for (i, value) in state.iter_mut().enumerate() { - *value = &(*value) + &self.params.round_constants[round_number][i]; - } - - state[self.params.state_size - 1] = - state[self.params.state_size - 1].pow(self.params.alpha); - - self.mix(state); - } - - pub fn mix(&self, state: &mut [FieldElement]) { - let mut new_state: Vec> = Vec::with_capacity(self.params.state_size); - for i in 0..self.params.state_size { - new_state.push(FieldElement::zero()); - for (j, current_state) in state.iter().enumerate() { - let mut mij = self.params.mds_matrix[i][j].clone(); - mij = mij.mul(current_state); - new_state[i] = new_state[i].clone().add(&mij); - } - } - state.clone_from_slice(&new_state[0..self.params.state_size]); - } - - pub fn hash(&self, x: &FieldElement, y: &FieldElement) -> FieldElement { - let mut state: Vec> = vec![x.clone(), y.clone(), FieldElement::from(2)]; - self.hades_permutation(&mut state); - let x = &state[0]; - x.clone() - } - - pub fn hash_single(&self, x: &FieldElement) -> FieldElement { - let mut state: Vec> = - vec![x.clone(), FieldElement::zero(), FieldElement::from(1)]; - self.hades_permutation(&mut state); - let x = &state[0]; - x.clone() - } - pub fn hash_many(&self, inputs: &[FieldElement]) -> FieldElement { - let r = self.params.rate; // chunk size - let m = self.params.state_size; // state size - - // Pad input with 1 followed by 0's (if necessary). - let mut values = inputs.to_owned(); - values.push(FieldElement::from(1)); - values.resize(((values.len() + r - 1) / r) * r, FieldElement::zero()); - - assert!(values.len() % r == 0); - let mut state: Vec> = vec![FieldElement::zero(); m]; - - // Process each block - for block in values.chunks(r) { - let mut block_state: Vec> = - state[0..r].iter().zip(block).map(|(s, b)| s + b).collect(); - block_state.extend_from_slice(&state[r..]); - - self.hades_permutation(&mut block_state); - state = block_state; - } - - state[0].clone() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::hash::poseidon::starknet::parameters::{ - DefaultPoseidonParams, PermutationParameters, - }; - use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }; - - #[test] - fn test_hades_permutation() { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - - let poseidon = Poseidon::new_with_params(params); - - // Initialize a state to test. The exact contents will depend on your specific use case. - let mut state: Vec> = vec![ - FieldElement::::from_hex("0x9").unwrap(), - FieldElement::::from_hex("0xb").unwrap(), - FieldElement::::from_hex("0x2").unwrap(), - ]; - - poseidon.hades_permutation(&mut state); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x510f3a3faf4084e3b1e95fd44c30746271b48723f7ea9c8be6a9b6b5408e7e6", - ) - .unwrap(); - let expected_state1 = FieldElement::::from_hex( - "0x4f511749bd4101266904288021211333fb0a514cb15381af087462fa46e6bd9", - ) - .unwrap(); - let expected_state2 = FieldElement::::from_hex( - "0x186f6dd1a6e79cb1b66d505574c349272cd35c07c223351a0990410798bb9d8", - ) - .unwrap(); - - assert_eq!(state[0], expected_state0); - assert_eq!(state[1], expected_state1); - assert_eq!(state[2], expected_state2); - } - #[test] - fn test_hash() { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - - let poseidon = Poseidon::new_with_params(params); - - let x = FieldElement::::from_hex("0x123456").unwrap(); - let y = FieldElement::::from_hex("0x789101").unwrap(); - - let z = poseidon.hash(&x, &y); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x2fb6e1e8838d4b850877944f0a13340dd5810f01f5d4361c54b22b4abda3248", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } - - #[test] - fn test_hash_single() { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - - let poseidon = Poseidon::new_with_params(params); - - let x = FieldElement::::from_hex("0x9").unwrap(); - - let z = poseidon.hash_single(&x); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x3bb3b91c714cb47003947f36dadc98326176963c434cd0a10320b8146c948b3", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } - - #[test] - fn test_hash_many() { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - - let poseidon = Poseidon::new_with_params(params); - - let a = FieldElement::::from_hex("0x1").unwrap(); - let b = FieldElement::::from_hex("0x2").unwrap(); - let c = FieldElement::::from_hex("0x3").unwrap(); - let d = FieldElement::::from_hex("0x4").unwrap(); - let e = FieldElement::::from_hex("0x5").unwrap(); - let f = FieldElement::::from_hex("0x6").unwrap(); - - let ins = vec![a, b, c, d, e, f]; - let z = poseidon.hash_many(&ins); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0xf50993f0797e4cc05734a47daeb214fde2d444ef6619a7c1f7c8e0924feb0b", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a]; - let z = poseidon.hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x579e8877c7755365d5ec1ec7d3a94a457eff5d1f40482bbe9729c064cdead2", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b]; - let z = poseidon.hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b, c]; - let z = poseidon.hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b, c, d]; - let z = poseidon.hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } -} +pub use parameters::PoseidonCairoStark252; diff --git a/crypto/src/hash/poseidon/starknet/parameters.rs b/crypto/src/hash/poseidon/starknet/parameters.rs index 2b18a34713..ff73e7e6ba 100644 --- a/crypto/src/hash/poseidon/starknet/parameters.rs +++ b/crypto/src/hash/poseidon/starknet/parameters.rs @@ -1,71 +1,319 @@ -use lambdaworks_math::field::{element::FieldElement as FE, traits::IsPrimeField}; +use crate::hash::poseidon::PermutationParameters; +use lambdaworks_math::field::{ + element::FieldElement as FE, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, +}; -use crate::hash::poseidon::starknet::round_constants::ROUND_CONSTANTS_HEXSTRINGS; +impl PermutationParameters for PoseidonCairoStark252 { + type F = Stark252PrimeField; + const RATE: usize = 2; + const CAPACITY: usize = 1; + const ALPHA: u32 = 3; + const N_FULL_ROUNDS: usize = 8; + const N_PARTIAL_ROUNDS: usize = 83; -#[derive(Clone)] -pub struct PermutationParameters { - /// Exponent for the S box - pub alpha: u32, - pub n_full_rounds: usize, - pub n_partial_rounds: usize, - pub round_constants: Vec>>, - pub mds_matrix: Vec>>, - pub rate: usize, - pub capacity: usize, - pub state_size: usize, -} -pub enum DefaultPoseidonParams { - /// Poseidon as used by Cairo - /// with three inputs - CairoStark252, -} + const MDS_MATRIX: &'static [FE] = &PoseidonCairoStark252::MDS_MATRIX; + const N_MDS_MATRIX_ROWS: usize = 3; + const N_MDS_MATRIX_COLS: usize = 3; -/// Parameters for Poseidon -/// Mds constants and rounds constants should be used for the shared field, even if it technically can work for any field with the same configuration -impl PermutationParameters -where - F: IsPrimeField, -{ - pub fn new_with(params: DefaultPoseidonParams) -> Self { - match params { - DefaultPoseidonParams::CairoStark252 => Self::cairo_stark_params(), - } - } - - fn cairo_stark_params() -> PermutationParameters { - let round_constants: Vec>> = ROUND_CONSTANTS_HEXSTRINGS - .iter() - .map(|[x0, x1, x2]| { - [ - FE::::from_hex(x0).unwrap(), - FE::::from_hex(x1).unwrap(), - FE::::from_hex(x2).unwrap(), - ] - .to_vec() - }) - .collect(); + const ROUND_CONSTANTS: &'static [FE] = + &PoseidonCairoStark252::ROUND_CONSTANTS; + const N_ROUND_CONSTANTS_ROWS: usize = 91; + const N_ROUND_CONSTANTS_COLS: usize = 3; +} - let mds_matrix = [ - [FE::::from(3), FE::::from(1), FE::::from(1)].to_vec(), - [FE::::from(1), -FE::one(), FE::::from(1)].to_vec(), - [FE::::from(1), FE::::from(1), -FE::::from(2)].to_vec(), - ] - .to_vec(); +#[derive(Clone, Default)] +pub struct PoseidonCairoStark252; - const RATE: usize = 2; - const CAPACITY: usize = 1; - const ALPHA: u32 = 3; - const N_FULL_ROUNDS: usize = 8; - const N_PARTIAL_ROUNDS: usize = 83; - Self { - alpha: ALPHA, - n_full_rounds: N_FULL_ROUNDS, - n_partial_rounds: N_PARTIAL_ROUNDS, - round_constants, - mds_matrix, - rate: RATE, - capacity: CAPACITY, - state_size: RATE + CAPACITY, - } - } +impl PoseidonCairoStark252 { + const MDS_MATRIX: [FE; 3 * 3] = [ + FE::from_hex_unchecked("3"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("800000000000011000000000000000000000000000000000000000000000000"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("1"), + FE::from_hex_unchecked("800000000000010ffffffffffffffffffffffffffffffffffffffffffffffff"), + ]; + // These constants can be found both in Jonathan's implementation + // https://github.com/xJonathanLEI/starknet-rs/blob/35c287e1a06e6ab68447f5f0b9df53f910960f57/starknet-crypto-codegen/src/poseidon/params.rs + // And the round 0 ones matches the one used + // in Cairo Lang + // https://github.com/starkware-libs/cairo-lang/blob/c98fc0b50529185b7018208cb3460191eeb53e0d/src/starkware/cairo/stark_verifier/air/layouts/starknet/autogenerated.cairo#L1574-L1596 + const ROUND_CONSTANTS: [FE; 3 * 91] = [ + FE::from_hex_unchecked("6861759ea556a2339dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe6f"), + FE::from_hex_unchecked("3827681995d5af9ffc8397a3d00425a3da43f76abf28a64e4ab1a22f27508c4"), + FE::from_hex_unchecked("3a3956d2fad44d0e7f760a2277dc7cb2cac75dc279b2d687a0dbe17704a8309"), + FE::from_hex_unchecked("626c47a7d421fe1f13c4282214aa759291c78f926a2d1c6882031afe67ef4cd"), + FE::from_hex_unchecked("78985f8e16505035bd6df5518cfd41f2d327fcc948d772cadfe17baca05d6a6"), + FE::from_hex_unchecked("5427f10867514a3204c659875341243c6e26a68b456dc1d142dcf34341696ff"), + FE::from_hex_unchecked("5af083f36e4c729454361733f0883c5847cd2c5d9d4cb8b0465e60edce699d7"), + FE::from_hex_unchecked("7d71701bde3d06d54fa3f74f7b352a52d3975f92ff84b1ac77e709bfd388882"), + FE::from_hex_unchecked("603da06882019009c26f8a6320a1c5eac1b64f699ffea44e39584467a6b1d3e"), + FE::from_hex_unchecked("4332a6f6bde2f288e79ce13f47ad1cdeebd8870fd13a36b613b9721f6453a5d"), + FE::from_hex_unchecked("53d0ebf61664c685310a04c4dec2e7e4b9a813aaeff60d6c9e8caeb5cba78e7"), + FE::from_hex_unchecked("5346a68894845835ae5ebcb88028d2a6c82f99f928494ee1bfc2d15eaabfebc"), + FE::from_hex_unchecked("550a9e24176509ea7631ccaecb7a4ab8694ab61f238797098147e69dd91e5a3"), + FE::from_hex_unchecked("219dcccb783b1cbaa62773fedd3570e0f48ad3ed77c8b262b5794daa2687000"), + FE::from_hex_unchecked("4b085eb1df4258c3453cc97445954bf3433b6ab9dd5a99592864c00f54a3f9a"), + FE::from_hex_unchecked("53e8a8e8a404c503af2bf3c03e420ea5a465939d04b6c72e2da084e5aabb78d"), + FE::from_hex_unchecked("5ca045c1312c09d1bd14d2537fe5c19fb4049cb137faf5df4f9ada962be8ca8"), + FE::from_hex_unchecked("7c74922a456802c44997e959f27a5b06820b1ed97596a969939c46c162517f4"), + FE::from_hex_unchecked("c0bba6880d2e686bf5088614b9684ff2526a20f91670435dc6f519bb7ab83f"), + FE::from_hex_unchecked("4526bcaec43e8ebd708dd07234c1b2dc1a6203741decd72843849cd0f87934a"), + FE::from_hex_unchecked("1cc9a17b00d3607d81efaea5a75a434bef44d92edc6d5b0bfe1ec7f01d613ed"), + FE::from_hex_unchecked("28b1e269b84c4012aa8cdbead0bc1ce1eb7284e2b28ed90bc7b4a4fde8f01f"), + FE::from_hex_unchecked("62af2f41d76c4ad1d9a2482fbdaf6590c19656bcb945b58bb724dc7a994498d"), + FE::from_hex_unchecked("5cfd7e44946daa6b2618213b0d1bf4a2269bed2dc0d4dbf59e285eee627df1a"), + FE::from_hex_unchecked("7ff2afb40f3300856fdd1b94da8d3bbcf0312ab9f16ac9bc31955dc8386a747"), + FE::from_hex_unchecked("5cd236bdc15b54183e90bab8ae37f8aab40efae6fa9cd919b3248ee326e929c"), + FE::from_hex_unchecked("5463841390e22d60c946418bf0e5822bd999084e30688e741a90bbd53a698a"), + FE::from_hex_unchecked("24c940fff3fe8c8b2021f13eb4d71747efd44a4e51890ae8226e7406144f805"), + FE::from_hex_unchecked("4e50cb07b3873268dc88f05393d9d03153ca4c02172dd1d7fc77d45e1b04555"), + FE::from_hex_unchecked("62ca053e4da0fc87b430e53238d2bab1d9b499c35f375d7d0b32e1189b6dcb5"), + FE::from_hex_unchecked("719f20ac59d1ebcaaf37fe0b851bc2419cd89100adff965951bff3d3d7e1191"), + FE::from_hex_unchecked("7645ca5e87a9f916a82fe5bb90807f44050ac92ca52f5c798935cf47d55a8fd"), + FE::from_hex_unchecked("15b8aeaca96ab53200eed38d248ecda23d4b71d17133438015391ca63663767"), + FE::from_hex_unchecked("53d94dbbca7cb2aa8252f106292ac3b98799e908f928c196c1b658bf10b2e2"), + FE::from_hex_unchecked("28f90b403e240f1c6f4c0a3b70edbb3942b447c615c0f033913831c34de2d1e"), + FE::from_hex_unchecked("2485167dc233ba6e1161c4d0bf025159699dd2feb36e3e5b70ae6e770e22081"), + FE::from_hex_unchecked("1c8b08a90d6ee46ff7de548541dd26988f7fdaacdd58698e938607a5feca6e8"), + FE::from_hex_unchecked("105c3bf5cba256466b75e79d146f9880c7c4df5ecdad643ce05b16901c4881e"), + FE::from_hex_unchecked("238019787f4cc0b627a65a21bef2106d5015b85dfbd77b2965418b02dbc6bd7"), + FE::from_hex_unchecked("15e624d7698fdf9b73dce29a5f24c465c15b52dec8172923a6ebc99a6ddc5e1"), + FE::from_hex_unchecked("5d3688ba56f34fdf56bc056ad8bf740ca0c2efef23b04a479f612fde5800a0a"), + FE::from_hex_unchecked("229abdef3fef7ae9e67ed336e82dc6c2e26d872d98b3cce811c69ae363b444d"), + FE::from_hex_unchecked("3e8096ecfcbcde2ee400801a56f236db2c43d1e33c92b57ac58daf2d3fc44db"), + FE::from_hex_unchecked("3ad5fec670d7039108d605aae834c7ce6a7cd4e1b47bf6a02265352c57db9bd"), + FE::from_hex_unchecked("7cf4598c0cf143875877afdbb4df6794ef597fff1f98557adca32046aeaef0a"), + FE::from_hex_unchecked("58aecc0081b55134a4d1c4c8f27932e4170c37841fef49aca0ec7a123c00ad6"), + FE::from_hex_unchecked("757b4b7ee98e0a15460b71995790396e4ef3c859db5b714ec09308d65d2ca61"), + FE::from_hex_unchecked("6b82800937f8981f3cd974f43322169963d2b54fd2b7ed348dc6cc226718b5d"), + FE::from_hex_unchecked("3a915b1814707273427e34ab8fbb7ca044f14088fedae9606b34a60b1e9c64"), + FE::from_hex_unchecked("54afbf1bd990043f9bc01028ff44195c0bb609d367b76269a627689547bfbef"), + FE::from_hex_unchecked("5e1ceb846fe1422b9524c7d014931072c3852df2d991470b08375edf6e762bb"), + FE::from_hex_unchecked("7f751f98968212ebe5dff3ce06e8cb916709e0c48e3020c6b2b01c1bec0814b"), + FE::from_hex_unchecked("36f6b64463f7c29fc3180616e340536bea7f01d226b68b6d45cd6dfbff811e4"), + FE::from_hex_unchecked("61135c9846faf39b4511d74fe8de8b48dd4d0e469d6703d7ed4fe4fe8e0dbac"), + FE::from_hex_unchecked("b58921a3fbdbb559b78f6acfca9a21a4ba83cc6e0ae3527fbaad907fc912b8"), + FE::from_hex_unchecked("22a4f8a5cdc7474b9d16b61c2973847211d84eb2fb27b816e52821c2e2b1b1e"), + FE::from_hex_unchecked("41cf6db5d6145edfeccbbc9a50b2ceedeb1765c61516ffcb112f810ad67036f"), + FE::from_hex_unchecked("be44689973db2b1cfc05fa8f4aec6fac6a0ff2fdfab744ade9de11416b6831"), + FE::from_hex_unchecked("39bf209c4e117e16489cda45128096d6d148a237142dc4951df0b8239be148b"), + FE::from_hex_unchecked("209cf541e5f74fc2b93310b8ce37b092a58282643860b5707c7eb980ea03a06"), + FE::from_hex_unchecked("6b562e6005f34ee0bdc218ba681b6ba7232e122287036d18c22dd5afa95326d"), + FE::from_hex_unchecked("e8103a23902be5dc6d5f59253a627a2a39c8aca11a914670e7a35dea38c8f"), + FE::from_hex_unchecked("6a3725548c664fd06bdc1b4d5f9bed83ef8ca7468d68f4fbbf345de2d552f72"), + FE::from_hex_unchecked("67fcd6997472e8e605d0f01a8eccc5f11a45c0aa21eb4ebb447b4af006a4a37"), + FE::from_hex_unchecked("26144c95c8de3634075784d28c06c162a44366f77792d4064c95db6ecb5cff0"), + FE::from_hex_unchecked("5b173c8b0eb7e9c4b3a874eb6307cda6fd875e3725061df895dc1466f350239"), + FE::from_hex_unchecked("7e1c2d6fde8ac9f87bae06ad491d391c448f877e53298b6370f2165c3d54ddb"), + FE::from_hex_unchecked("4db779f3e5b7424996f451b156fe4e28f74d61e7771f9e3fa433b57ca6627a9"), + FE::from_hex_unchecked("bb930d8a6c6583713435ec06b6fed7825c3f71114acb93e240eed6970993dd"), + FE::from_hex_unchecked("4472d73b2830565d708467e9296fb5599d3a08814c31c4189e9579c046e878f"), + FE::from_hex_unchecked("7ba9c303dfee2d89e10e3c883ca5ce5614d23739b7cb2052cc23612b11170e2"), + FE::from_hex_unchecked("21c0e3319ede47f0425dc9b2c1ed30e6356cb133e97579b822548eb9c4dc4b7"), + FE::from_hex_unchecked("2cfd61139e50ddd37b09933816e2a0932e53b7dc4f4947565c1d41e877eb191"), + FE::from_hex_unchecked("5abea18941a4976844544d92ee0eca65bdd10b3f170b0dc2f30acd37e26d8e7"), + FE::from_hex_unchecked("77088fdb015c7947a6265e44fef6f724ea28ae28b26e6eee5a751b7ce6bcc21"), + FE::from_hex_unchecked("3abdc9d677231325b3e3c43cfd443076b4ce33cddbc8446120dce84e6122b73"), + FE::from_hex_unchecked("2250f430b7fe7d12e5d00b6b83e52a52ca94879ccfab81a7a602662c2d62c4d"), + FE::from_hex_unchecked("5c92ef479c11bb51fb24ef76d57912b12660e7bd156d6cabbb1efb79a25861b"), + FE::from_hex_unchecked("235ec597391648b510f616fa8b87900fd08fd4208a785cffcf784a63a0fd5c6"), + FE::from_hex_unchecked("4ed4e872eb7e736207be77e9d11e38f396b5c0ba3376e855523c00b372cc668"), + FE::from_hex_unchecked("5f9406febca3879b756ef3f6331890b3d46afa705908f68fb7d861c4f275a1b"), + FE::from_hex_unchecked("1d9c501d9ff1fba621a9f61b68873c05f17b0384661f06d97edf441abdaa49d"), + FE::from_hex_unchecked("4b0de22bbd0a58534982c8e28d2f6e169e37ba694774c4dfa530f41c535952e"), + FE::from_hex_unchecked("1b4d48bd38a3f8602186aabb291eca0d319f0e3648b2574c49d6fd1b033d903"), + FE::from_hex_unchecked("7558bbea55584bf1725d8aa67ddba626b6596bbd2f4e65719702cefcead4bab"), + FE::from_hex_unchecked("1108f1a9500a52f561ea174600e266a70b157d56ece95b60a44cf7a3eef17be"), + FE::from_hex_unchecked("8913d96a4f36b12becb92b4b6ae3f8c209fb90caab6668567289b67087bf60"), + FE::from_hex_unchecked("6502262c51ad8f616926346857dec8cca2e99f5742b6bf223f4d8a6f32867a6"), + FE::from_hex_unchecked("7cb5fcdc00892812889280505c915bde962ea034378b343cd3a5931d2ec0e52"), + FE::from_hex_unchecked("2eb919524a89a26f90be9781a1515145baea3bc96b8cd1f01b221c4d2a1ce87"), + FE::from_hex_unchecked("58efb6272921bc5eada46635e3567dced0662c0161223e3c1c63e8de3ec3d73"), + FE::from_hex_unchecked("62fcd49ca9c7587b436d205ffc2a39594254a1ac34acd46d6955e7844d4f88e"), + FE::from_hex_unchecked("635895330838846e62d9acce0b625f885e5941e54bd3a2106fcf837aef5313b"), + FE::from_hex_unchecked("7da445b81e9b3d36d47a5f4d23b92a378a17f119d5e6e70629f8b41fefb12e3"), + FE::from_hex_unchecked("2b22dab62f0817e9fc5737e189d5096a9027882bef1738943b7016256118343"), + FE::from_hex_unchecked("1af01472348f395bacdfed1d27664d0d5bdea769be8fcb8fbef432b790e50d5"), + FE::from_hex_unchecked("76b172dbbeec5a31de313b9390f79ec9284163c8e4986bc5b682e5ac6360309"), + FE::from_hex_unchecked("70efaeae36f6af0f362f6cb423d2009b30ddb4178d46def0bdb2905b3e0862"), + FE::from_hex_unchecked("6cb99b36e521ac0a39872686b84ee1d28c4942b8036a1c25a0e4117ccaeedf"), + FE::from_hex_unchecked("29fd44305a5a9a70bbf9674e544bda0fb3d0fe5bb3aa743fd1b8a4fc1dc6055"), + FE::from_hex_unchecked("6b447ded1046e83629b184d8c36db3a11a6778d8848142aa6363d6619f9764"), + FE::from_hex_unchecked("642a8b4be4ba812cbfcf55a77339b5d357cceb6946fdc51c14b58f5b8989b59"), + FE::from_hex_unchecked("489e0a26f65a1eecc6cc6aa5b6e775cbc51a73700bd794a7acd79ae1d95882a"), + FE::from_hex_unchecked("3b19d4ef195975bbf78ab5dc2fd1d24816428f45a06293c1b9d57b9a02e9200"), + FE::from_hex_unchecked("7d2dd994756eacba576b74790b2194971596f9cd59e55ad2884c52039013df5"), + FE::from_hex_unchecked("1922810cc08f50bf300df869823b9f18b3327e29e9e765002970ef0f2e8c5f3"), + FE::from_hex_unchecked("52f3afaf7c9102f1d46e1d79a70745b39c04376aafff05771cbd4a88ed418ac"), + FE::from_hex_unchecked("7ccfc88e44a0507a95260f44203086e89552bbe53dcc46b376c5bcab6ea788e"), + FE::from_hex_unchecked("2949125939e6ad94100228beff83823f5157dd8e067bc8819e40a1ab008dd9c"), + FE::from_hex_unchecked("6cb64e3a0d37a6a4273ce4ee6929ba372d6811dde135af4078ba6e1912e1014"), + FE::from_hex_unchecked("d63b53707acf8962f05f688129bf30ad43714257949cd9ded4bf5953837fae"), + FE::from_hex_unchecked("bcb1549c9cabb5d13bb968b4ea22d0bb7d7460a6965702942092b32ef152d4"), + FE::from_hex_unchecked("3d1c5233657ce31f5ead698fe76f6492792a7205ba0531a0ca25b8d8fe798c1"), + FE::from_hex_unchecked("2240b9755182ee9066c2808b1e16ea448e26a83074558d9279f450b79f97516"), + FE::from_hex_unchecked("cc203d8b0f90e30fe8e54f343cef59fe8d70882137de70c9b43ab6615a646c"), + FE::from_hex_unchecked("310c6cc475d9346e061bacdc175ea9e119e937dea9d2100fa68e03c1f77910b"), + FE::from_hex_unchecked("7f84b639f52e57420bc947defced0d8cbdbe033f578699397b83667049106c7"), + FE::from_hex_unchecked("584ca7f01262c5bd89c4562f57139f47e9f038cb32ec35abe4e1da8de3e164a"), + FE::from_hex_unchecked("1135eefaf69b6e4af7d02f562868be3e02fdc72e01e9510531f9afa78abbbde"), + FE::from_hex_unchecked("372082b8a6c07100a50a3d33805827ad350c88b56f62c6d36a0d876856a99e8"), + FE::from_hex_unchecked("7c3c12b819a8aad87499bac1a143fc59674f132e33898f0c119e3d12462dfe6"), + FE::from_hex_unchecked("4f1354c51e8f6905b84157cfeff6822c056ce9e29d602eb46bd9b75a23836cf"), + FE::from_hex_unchecked("2da9f26a8271659075739ba206507a08ac360150e849950ef3973548fbd2fca"), + FE::from_hex_unchecked("287173956a2beb111b5ec29195e38cc3f6a65ff50801aa75fd78dd550702843"), + FE::from_hex_unchecked("7273101c190ff64212420095a51c8411c7f3227f6a7a4a64ae6ba7f9201e126"), + FE::from_hex_unchecked("2dbf2a6b56b26d23ebeb61e500687de749b03d3d349169699258ee4c98005fc"), + FE::from_hex_unchecked("85b6cbb29739a6808e67f00ab89b52ab89ef8d92530394e4b910efd706c7fb"), + FE::from_hex_unchecked("3d55b5f1171efda1dacbcbadfd5b910b493fa9589fd937e3e06ce26b08925a3"), + FE::from_hex_unchecked("aaedaa6ef2fa707d16b3b295410c0e44f7a2f8135c207824f6ae2a9b16e90c"), + FE::from_hex_unchecked("6aca6ebf70b1cb46c6331e9f1a5c4cc89b80f8adc5d18915c1cd0d496ccf5e1"), + FE::from_hex_unchecked("1678602af36c28abb010f831d403d94d5e90003e6d37c677e9dd157fb27761"), + FE::from_hex_unchecked("2022036bdf687f041b547fefdf36d4c2cd3f4b0526a88aafe60a0a8f508bad2"), + FE::from_hex_unchecked("7bfc350957c968ca664397414bdfb8f9b8dfe49fb63e32353d4e2e8d1d4af6"), + FE::from_hex_unchecked("2d639cbd418cb9fc24ea29ccd1d15ab81f43a499b27a06d3c5e2176f7ad79af"), + FE::from_hex_unchecked("ecdea7f959a4d488403d5b39687a1fe0dee3369e5fbc0f4779569f64506e0c"), + FE::from_hex_unchecked("3f656bdc4fefd92b70658e2f1992ef9f22e5f2d28c490e21d4e34357154b558"), + FE::from_hex_unchecked("d1b8cb1561eed32319638ccab9033dfec47596f8a6f4ce6594e19fddd59254"), + FE::from_hex_unchecked("758ffc77c62e3e0f86ef6ea01545ad76f281ec2941da7222d1e8b4e2ec1f192"), + FE::from_hex_unchecked("20315ca079570df995386e96aeaa1b4596aacd28f83c32f29a591c95e6fcac5"), + FE::from_hex_unchecked("3e55cf341e7c280cb05f3d6ff9c8d9f2cfe76b84a9d1b0f54884b316b740d8d"), + FE::from_hex_unchecked("4d56feb32cde74feede9749739be452e92c029007a06f6e67c81203bf650c68"), + FE::from_hex_unchecked("4ee807aa678a9a433b6171eaa6a2544497f7599fb8145d7e8089f465403c89b"), + FE::from_hex_unchecked("25d2bacc8f1ee7548cb5f394de2cb6e1f365e56a1bc579d0f9a8ad2ef2b3821"), + FE::from_hex_unchecked("5f573de597ce1709fc20051f6501268cd4b278811924af1f237d15feb17bd49"), + FE::from_hex_unchecked("30297c3c54a505f5826a280e053cf7a3c1e84a1dcf8b33c682cf85ddac86deb"), + FE::from_hex_unchecked("2f5e9c47c9a86e043c7526a59783f03c6bc79b69b8709fe6a052b93a8339ae8"), + FE::from_hex_unchecked("1bf75c7a739da8d29f9c23065ff8ccb1da7deec83e130bcd4a27a416c72b84b"), + FE::from_hex_unchecked("60563d5f852ae875989017bd5c4cfdc29cd27fc4e91eeabdb8e864df3c3c675"), + FE::from_hex_unchecked("7a4b1d70885aa820969635468daec94f8156c20e3131bd71005be1cd16ccf9e"), + FE::from_hex_unchecked("347bb025695e497f1e201cd62aa4600b8b85cf718cd1d400f39c10e59cc5852"), + FE::from_hex_unchecked("6783ab1e1ef97bb9e7f9381eb6ab0de2c4c9c2de413691ba8aa666292e9e217"), + FE::from_hex_unchecked("133e0280c6de90e7b3870a07823c081fd9c4cb99d534debd6a7bfb4e5b0dd46"), + FE::from_hex_unchecked("865d450ce29dc42fb5db72460b3560a2f093695573dff94fd0216eb925beec"), + FE::from_hex_unchecked("1de023f840e054a35526dabacf0dee948efba06bcbb414ecd81a6b301664e57"), + FE::from_hex_unchecked("55fc1e341bfdf7805015a96f724c5ac7cc7b892a292d38190631ab1a5388c4"), + FE::from_hex_unchecked("2df6557bfd4a4e7e7b27bf51552d2b5162706a3e624faca01a307ef8d532858"), + FE::from_hex_unchecked("113a8a66962ce08d92a6bd3e9c1d55ef8f226da95e4d629046d73d0507f6271"), + FE::from_hex_unchecked("271577d6ee9fa377f2c889874ba5b44ca1076033db5c2de4f3367b08c008e53"), + FE::from_hex_unchecked("3396b33911219b6b0365c09348a561ef1ccb956fc673bc5291d311866538574"), + FE::from_hex_unchecked("1e1392f2da08549c8a7d89e899189306170baa3c3436e6a5398f69c8f321636"), + FE::from_hex_unchecked("661545081032013df118e1d6e7c61a333e313b1a9a5b6d69c876bd2e7d694ca"), + FE::from_hex_unchecked("6b14294e71cd7fb776edbd432d20eb8f66d00533574e46573516f0cacdeec88"), + FE::from_hex_unchecked("7252fbbb06c2848338b1c41df31e4e51fe2a18e2406c671915cab6eb1a1d4f2"), + FE::from_hex_unchecked("3ccf71be7cc2a9abcf5a09807c69679430c03645747621b7f5327cb00ff99da"), + FE::from_hex_unchecked("29778dc707504fa6a9f7c97b4ceef0a9b39001d034441617757cd816dac919a"), + FE::from_hex_unchecked("39473f6f06bb99e33590d34e3bae36e491f7bbf86a26aa55a8f5b27bb98d4c5"), + FE::from_hex_unchecked("7ba7c32f875b71b895caa0215f996fd4ad92bab187e81417063dde91c08c027"), + FE::from_hex_unchecked("37c1367e49cbfc403b22aac82abf83b0ed083148a5f4c92839e5d769bdab6b6"), + FE::from_hex_unchecked("5c9eb899931d2f4b53ffcf833cdfa05c2068375ff933eb37ae34157c0b2d951"), + FE::from_hex_unchecked("5f6054a4d48698ec27772fb50a7d2e5c1557ffdc1ffd07331f2ca26c6e3b661"), + FE::from_hex_unchecked("20e6d62a2fe0fe9b0fab83e8c7d1e8bfd0fec827960e40a91df64664dcd7774"), + FE::from_hex_unchecked("6290a56a489ad52120c426fe0e409c2ff17adf51f528cafb0d026d14ffd6aac"), + FE::from_hex_unchecked("3703f16f990342c2267a6f7ece342705a32ca4c101417286279f6fc315edc7c"), + FE::from_hex_unchecked("5194962daf6679b9a0c32b5a9a307ba92e2c630f70e439195b680dd296df3fd"), + FE::from_hex_unchecked("e8eae20a79a7c1242c34617b01340fb5fd4bea2aa58b98d2400d9b515ee5e2"), + FE::from_hex_unchecked("369058169d63091ae28bfb28def7cd8d00dd7c2894fae4ffec65242afa5cd45"), + FE::from_hex_unchecked("418c963bc97195a74077503ee472f22cfdff0973190ab189c7b93103fd78167"), + FE::from_hex_unchecked("68d07a3eefc78dc5b28b3f4dc93167fb8c97112d14a25b4d4db559720156386"), + FE::from_hex_unchecked("517e892228df2d4f15a3c4241c98ba25ba0b5557375003f8748583a61836372"), + FE::from_hex_unchecked("5cc0f0f6cf9be94a150116e7932f8fe74ac20ad8100c41dc9c99538792e279b"), + FE::from_hex_unchecked("53d5d7863434c6629bdb1f8a648e4820883543e821f0f5c1668884c0be41ec8"), + FE::from_hex_unchecked("a158126b89e6b0a600bf53f8101707b072218912dd0d9df2528f67de24fdf5"), + FE::from_hex_unchecked("6b53b807265387ee582069a698323d44c204bed60672b8d8d073bed2fede503"), + FE::from_hex_unchecked("1097fb448406b7a6de0877efd58c01be53be83bde9601a9acc9e0ca2091fda0"), + FE::from_hex_unchecked("cbc0ff7239d3763902396389d67b3049ce1fefde66333ce37ca441f5a31bec"), + FE::from_hex_unchecked("79a3d91dd8a309c632eb43d57b5c5d838ceebd64603f68a8141ebef84280e72"), + FE::from_hex_unchecked("23fb472fe575135300f74e8f6de8fe1185078218eceb938900e7598a368db9"), + FE::from_hex_unchecked("7ac73134016d2a8a4c63a6b9494c0bd7a6ba87cc33e8a8e23ebda18bfb67c2a"), + FE::from_hex_unchecked("19a16068c3eac9c03f1b5c5ee2485ccc163d9ab17bb035d5df6e31c3dcf8f14"), + FE::from_hex_unchecked("1f24b4356a6bbfd4d4ef9fd1634752820ee86a925725ac392134d90def073ea"), + FE::from_hex_unchecked("3e44e7f7aeea6add59b6b4d11c60a528fb70727f35d817305971592333d36"), + FE::from_hex_unchecked("5f93b02f826741414535a511ed3eb4fe85987ae57bc9807cbd94cd7513d394e"), + FE::from_hex_unchecked("f0a0a88db99247d71c3d51d4197fa3fd1cc76e670607e35ca2d3bada29523a"), + FE::from_hex_unchecked("3432226916d31f3acac1e211431fd4cd2b6f2e80626af6564bdde3e77608db0"), + FE::from_hex_unchecked("55625941bfea6f48175192845a7ad74b0b82940ef5f393ca3830528d59cf919"), + FE::from_hex_unchecked("ddf48695b204477dfe4f8cb3ef1b39783e9b92f9276b858e2e585e318e20a4"), + FE::from_hex_unchecked("260730a657ff8f38851a679ab2a1490434ee50d4953e7c5d3194578b08ae8e3"), + FE::from_hex_unchecked("4cfd231373aa46d96283840bdb79ba6d7132775b398d324bcd206842b961aa9"), + FE::from_hex_unchecked("3203843c41cd453f14fa0bc0b2191a27ebc659e74fd48f981e963de57eff25d"), + FE::from_hex_unchecked("2c2f6ae5624d1fb8435d1c86bf76c260f5e77a54b006293705872e647cc46"), + FE::from_hex_unchecked("780225456e63903b3e561384ef2e73a85b0e142b69752381535022014765f06"), + FE::from_hex_unchecked("7f602ec1a80a051fd21b07f8e2960613082fc954b9a9ff641cc432a75c81887"), + FE::from_hex_unchecked("62561b0a0a72239b60f6aaf7022b7d323fe77cd7c1ab432f0c8c118ca7e6bca"), + FE::from_hex_unchecked("604fe5a6a22344aa69b05dea16b1cf22450c186d093754cb9b84a8a03b70bc8"), + FE::from_hex_unchecked("1cf9987a4044716d3dc140bf5f9b76f6eada5995905189f8682eaf88aef2b7b"), + FE::from_hex_unchecked("6bc0b2487c1eece3db47a4bdd60cf69debee233e91b50e9ee42ce22cbfbacbf"), + FE::from_hex_unchecked("2f5dbb5055eb749a11403b93e90338b7620c51356d2c6adcbf87ab7ea0792e6"), + FE::from_hex_unchecked("446328f4dddae6529743c43883d59c45f63b8a623a9cf318489e5fc4a550f61"), + FE::from_hex_unchecked("4ba30c5240cde5bca6c4010fb4b481a25817b43d358399958584d2c48f5af25"), + FE::from_hex_unchecked("5f5275f76425b15c89209117734ae85708351d2cf19af5fe39a32f89c2c8a89"), + FE::from_hex_unchecked("576f3b5156f4763e18c7f98df3b2f7b993cdda4eb8cb92415e1be8e6af2fc17"), + FE::from_hex_unchecked("11dc3f15cba928aed5a44b55a5b026df84a61719ed5adbb93c0e8e12d35ef3d"), + FE::from_hex_unchecked("44c40e6bd52e91ad9896403ae4f543ae1c1d9ea047d75f8a6442b8feda04dca"), + FE::from_hex_unchecked("1836d733a54013ebd0ccbf4974e80ac1954bf90fe9ea4e2c914ad01166026d8"), + FE::from_hex_unchecked("3c553be9776b628a8159d306ef084727611df8037761f00f84ca02ce731b3ac"), + FE::from_hex_unchecked("6ce94781c1a23fda1c7b87e0436b1b401ae11a6d757843e342f5017076a059"), + FE::from_hex_unchecked("381ec71fbdef3160253be9f00f4e6b9e107f457812effb7371cc2daa0acd0ed"), + FE::from_hex_unchecked("1844da9cc0eeadc6490d847320d9f3cd4fb574aa687bafdfe0ffa7bf2a8f1a1"), + FE::from_hex_unchecked("7a8bf471f902d5abb27fea5b401483dedf97101047459682acfd7f9b65a812f"), + FE::from_hex_unchecked("633b6fb004de62441915fb51ac174456f5a9cdff7aecb6e6b0d063839e56327"), + FE::from_hex_unchecked("179ee5cec496194771200382bfc6d17bbe546ba88fed8b17535fd70fbc50ab6"), + FE::from_hex_unchecked("2806c0786185986ea9891b42d565256b0312446f07435ac2cae194330bf8c42"), + FE::from_hex_unchecked("438703d948708ae90c7a6b8af194b8b603bb2cdfd26bfa356ac9bb6ee041393"), + FE::from_hex_unchecked("24446628f56029d7153bd3a482b7f6e1c56f4e02225c628a585d58a920035af"), + FE::from_hex_unchecked("4c2a76e5ce832e8b0685cdeeea3a253ae48f6606790d817bd96025e5435e259"), + FE::from_hex_unchecked("78a23323520994592933c079b148aed57d5e4ce1ab122d370983b8caa0e0300"), + FE::from_hex_unchecked("79ca6c5e1025b2151144ea5937dd07cadce1aa691b19e6db87070ba51ec22c0"), + FE::from_hex_unchecked("6b2e4a46e37af3cf952d9d34f8d6bd84a442ebfd1ac5d17314e48922af79c5d"), + FE::from_hex_unchecked("305d6cd95cc2eab6805d93d3d8d74e1ca7d443f11e34a18e3529e0d03435c2"), + FE::from_hex_unchecked("6097b4b8b90db14b39743ed23f8956cabb7aea70cc624a415c7c17b37fbf9a9"), + FE::from_hex_unchecked("64e1b3f16c26c8845bdb98373e77dad3bdcc90865b0f0af96288707c18893f"), + FE::from_hex_unchecked("649fafe673f21e623384d841221b73421c56014af2ffdf57f1579ae911fd335"), + FE::from_hex_unchecked("7d806dccbf1a2696b294404e849722f2baa2f4d19005a49d1ba288a77fefe30"), + FE::from_hex_unchecked("5951a37da53e3bbc0b3e2db1a9a235d7a03f48f443be6d659119c44aafc7522"), + FE::from_hex_unchecked("6d87fa479fb59524d1912c3554ae3d010496a31bdacb542c816a1607a907731"), + FE::from_hex_unchecked("1451cccd4200fa9d473ad73466b4e8c0a712a0b12bb6fc9462a3ac892acc9b2"), + FE::from_hex_unchecked("3ca1b6400b3e51007642535f1ca9b03832ca0faa15e1c4ed82dd1efdc0763da"), + FE::from_hex_unchecked("52c55735b2f0a6560ad1516a8f13592b0dd024ff4162539f993a99c7a1a4d95"), + FE::from_hex_unchecked("7e04de60aa80132f0149d1dee29617de750bd5ce3e9fa5e62951d65f6b924cd"), + FE::from_hex_unchecked("271784e6920a68e47c4c8fab71c8f8303ef29e26f289223edf63291c0a5495"), + FE::from_hex_unchecked("5c7c19061a84d5960a04b8f0adaa603c8afe93f17b7f0e56b49514af43d0c69"), + FE::from_hex_unchecked("172db5affe783af419da337cb79061e090943c2959dea1b38e4436f5482eafe"), + FE::from_hex_unchecked("518b7975a6d8d310eac9fe4082916f021a7ecbadf18809746a9e061a2cb9456"), + FE::from_hex_unchecked("20c5539dc45dd56d4bbc2440a9f5061d74b8ae5e37b34e8755a0315f1e196db"), + FE::from_hex_unchecked("1ea6f5fb309fa4a08bc7d516e80efc3a977b47208283cf35a9d8bc213b90b14"), + FE::from_hex_unchecked("50ce323c5128dc7fdd8ddd8ba9cfe2efd424b5de167c7257d1f766541e29ded"), + FE::from_hex_unchecked("401e37d0e276547695538b41d3c28215b865f5b7d1b497a8919284c613cb7d8"), + FE::from_hex_unchecked("645a0de30acc3117f2893056fc5880255daa12cc61261cc0fab9cf57c57397b"), + FE::from_hex_unchecked("69bc3841eb0a310d9e988d75f09f698d4fdc9d0d69219f676b66ae7fa3d495b"), + FE::from_hex_unchecked("2684bbe315ad2c4bdd47c38fe72db47cf0ae0c455cda5484baf523f136bdc6"), + FE::from_hex_unchecked("11e0f83c547ca5c68202e8d34e5595a88858c2afa664365e4acb821fd8a13ee"), + FE::from_hex_unchecked("4af4a7635f8c7515966567ceec34315d0f86ac66c1e5a5ecac945f1097b82ef"), + FE::from_hex_unchecked("4fba58cf8aaf4893cb7158908ccc18b1dc48894d2bb46225c72b11f4c74b271"), + FE::from_hex_unchecked("397c4c169115b468cc90da2e664f8c29a7f89be0ead679a38b0f44c8a2a0e20"), + FE::from_hex_unchecked("6563b9ebb6450dbad397fa5dd13c501f326dd7f32be22e20998f59ec7bacff"), + FE::from_hex_unchecked("376edb238f7b630ea81d307f4c79f9afec48562076dd09c36cd79e9cb817165"), + FE::from_hex_unchecked("60d4208bb50eb15f29ed22addcd50a1b337504039690eb858584cda96e2e061"), + FE::from_hex_unchecked("6a37d569d2fbc73dbff1019dc3465ec0f30da46918ab020344a52f1df9a9210"), + FE::from_hex_unchecked("d3b174c7290c6bf412083ff35d23821dc512f1df073c1b429130371ac63b1a"), + FE::from_hex_unchecked("226ed3d763477454b46eb2a5c3b814634d974919689fb489fe55e525b980373"), + FE::from_hex_unchecked("5f3997e7dafcb2de0e7a23d33d2fd9ef06f4d79bd7ffa1930e8b0080d218513"), + FE::from_hex_unchecked("7c5eec716d94634434df335a10bbac504f886f7f9d3c1648348c3fae8fdf14d"), + FE::from_hex_unchecked("53cc30d7fe0f84e7e24fd22c0f9ad68a89da85553f871ef63d2f55f57e1a7c"), + FE::from_hex_unchecked("368821ee335d71819b95769f47418569474a24f6e83b268fefa4cd58c4ec8fa"), + FE::from_hex_unchecked("5334f75b052c0235119816883040da72c6d0a61538bdfff46d6a242bfeb7a1"), + FE::from_hex_unchecked("5d0af4fcbd9e056c1020cca9d871ae68f80ee4af2ec6547cd49d6dca50aa431"), + FE::from_hex_unchecked("30131bce2fba5694114a19c46d24e00b4699dc00f1d53ba5ab99537901b1e65"), + FE::from_hex_unchecked("5646a95a7c1ae86b34c0750ed2e641c538f93f13161be3c4957660f2e788965"), + FE::from_hex_unchecked("4b9f291d7b430c79fac36230a11f43e78581f5259692b52c90df47b7d4ec01a"), + FE::from_hex_unchecked("5006d393d3480f41a98f19127072dc83e00becf6ceb4d73d890e74abae01a13"), + FE::from_hex_unchecked("62c9d42199f3b260e7cb8a115143106acf4f702e6b346fd202dc3b26a679d80"), + FE::from_hex_unchecked("51274d092db5099f180b1a8a13b7f2c7606836eabd8af54bf1d9ac2dc5717a5"), + FE::from_hex_unchecked("61fc552b8eb75e17ad0fb7aaa4ca528f415e14f0d9cdbed861a8db0bfff0c5b"), + ]; } diff --git a/crypto/src/hash/poseidon/starknet/round_constants.rs b/crypto/src/hash/poseidon/starknet/round_constants.rs deleted file mode 100644 index a592b6166b..0000000000 --- a/crypto/src/hash/poseidon/starknet/round_constants.rs +++ /dev/null @@ -1,463 +0,0 @@ -// These constants can be found both in Jonathan's implementation -// https://github.com/xJonathanLEI/starknet-rs/blob/35c287e1a06e6ab68447f5f0b9df53f910960f57/starknet-crypto-codegen/src/poseidon/params.rs -// And the round 0 ones matches the one used -// in Cairo Lang -// https://github.com/starkware-libs/cairo-lang/blob/c98fc0b50529185b7018208cb3460191eeb53e0d/src/starkware/cairo/stark_verifier/air/layouts/starknet/autogenerated.cairo#L1574-L1596 - -pub const ROUND_CONSTANTS_HEXSTRINGS: [[&str; 3]; 91] = [ - [ - "0x6861759ea556a2339dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe6f", - "0x3827681995d5af9ffc8397a3d00425a3da43f76abf28a64e4ab1a22f27508c4", - "0x3a3956d2fad44d0e7f760a2277dc7cb2cac75dc279b2d687a0dbe17704a8309", - ], - [ - "0x626c47a7d421fe1f13c4282214aa759291c78f926a2d1c6882031afe67ef4cd", - "0x78985f8e16505035bd6df5518cfd41f2d327fcc948d772cadfe17baca05d6a6", - "0x5427f10867514a3204c659875341243c6e26a68b456dc1d142dcf34341696ff", - ], - [ - "0x5af083f36e4c729454361733f0883c5847cd2c5d9d4cb8b0465e60edce699d7", - "0x7d71701bde3d06d54fa3f74f7b352a52d3975f92ff84b1ac77e709bfd388882", - "0x603da06882019009c26f8a6320a1c5eac1b64f699ffea44e39584467a6b1d3e", - ], - [ - "0x4332a6f6bde2f288e79ce13f47ad1cdeebd8870fd13a36b613b9721f6453a5d", - "0x53d0ebf61664c685310a04c4dec2e7e4b9a813aaeff60d6c9e8caeb5cba78e7", - "0x5346a68894845835ae5ebcb88028d2a6c82f99f928494ee1bfc2d15eaabfebc", - ], - [ - "0x550a9e24176509ea7631ccaecb7a4ab8694ab61f238797098147e69dd91e5a3", - "0x219dcccb783b1cbaa62773fedd3570e0f48ad3ed77c8b262b5794daa2687000", - "0x4b085eb1df4258c3453cc97445954bf3433b6ab9dd5a99592864c00f54a3f9a", - ], - [ - "0x53e8a8e8a404c503af2bf3c03e420ea5a465939d04b6c72e2da084e5aabb78d", - "0x5ca045c1312c09d1bd14d2537fe5c19fb4049cb137faf5df4f9ada962be8ca8", - "0x7c74922a456802c44997e959f27a5b06820b1ed97596a969939c46c162517f4", - ], - [ - "0xc0bba6880d2e686bf5088614b9684ff2526a20f91670435dc6f519bb7ab83f", - "0x4526bcaec43e8ebd708dd07234c1b2dc1a6203741decd72843849cd0f87934a", - "0x1cc9a17b00d3607d81efaea5a75a434bef44d92edc6d5b0bfe1ec7f01d613ed", - ], - [ - "0x28b1e269b84c4012aa8cdbead0bc1ce1eb7284e2b28ed90bc7b4a4fde8f01f", - "0x62af2f41d76c4ad1d9a2482fbdaf6590c19656bcb945b58bb724dc7a994498d", - "0x5cfd7e44946daa6b2618213b0d1bf4a2269bed2dc0d4dbf59e285eee627df1a", - ], - [ - "0x7ff2afb40f3300856fdd1b94da8d3bbcf0312ab9f16ac9bc31955dc8386a747", - "0x5cd236bdc15b54183e90bab8ae37f8aab40efae6fa9cd919b3248ee326e929c", - "0x5463841390e22d60c946418bf0e5822bd999084e30688e741a90bbd53a698a", - ], - [ - "0x24c940fff3fe8c8b2021f13eb4d71747efd44a4e51890ae8226e7406144f805", - "0x4e50cb07b3873268dc88f05393d9d03153ca4c02172dd1d7fc77d45e1b04555", - "0x62ca053e4da0fc87b430e53238d2bab1d9b499c35f375d7d0b32e1189b6dcb5", - ], - [ - "0x719f20ac59d1ebcaaf37fe0b851bc2419cd89100adff965951bff3d3d7e1191", - "0x7645ca5e87a9f916a82fe5bb90807f44050ac92ca52f5c798935cf47d55a8fd", - "0x15b8aeaca96ab53200eed38d248ecda23d4b71d17133438015391ca63663767", - ], - [ - "0x53d94dbbca7cb2aa8252f106292ac3b98799e908f928c196c1b658bf10b2e2", - "0x28f90b403e240f1c6f4c0a3b70edbb3942b447c615c0f033913831c34de2d1e", - "0x2485167dc233ba6e1161c4d0bf025159699dd2feb36e3e5b70ae6e770e22081", - ], - [ - "0x1c8b08a90d6ee46ff7de548541dd26988f7fdaacdd58698e938607a5feca6e8", - "0x105c3bf5cba256466b75e79d146f9880c7c4df5ecdad643ce05b16901c4881e", - "0x238019787f4cc0b627a65a21bef2106d5015b85dfbd77b2965418b02dbc6bd7", - ], - [ - "0x15e624d7698fdf9b73dce29a5f24c465c15b52dec8172923a6ebc99a6ddc5e1", - "0x5d3688ba56f34fdf56bc056ad8bf740ca0c2efef23b04a479f612fde5800a0a", - "0x229abdef3fef7ae9e67ed336e82dc6c2e26d872d98b3cce811c69ae363b444d", - ], - [ - "0x3e8096ecfcbcde2ee400801a56f236db2c43d1e33c92b57ac58daf2d3fc44db", - "0x3ad5fec670d7039108d605aae834c7ce6a7cd4e1b47bf6a02265352c57db9bd", - "0x7cf4598c0cf143875877afdbb4df6794ef597fff1f98557adca32046aeaef0a", - ], - [ - "0x58aecc0081b55134a4d1c4c8f27932e4170c37841fef49aca0ec7a123c00ad6", - "0x757b4b7ee98e0a15460b71995790396e4ef3c859db5b714ec09308d65d2ca61", - "0x6b82800937f8981f3cd974f43322169963d2b54fd2b7ed348dc6cc226718b5d", - ], - [ - "0x3a915b1814707273427e34ab8fbb7ca044f14088fedae9606b34a60b1e9c64", - "0x54afbf1bd990043f9bc01028ff44195c0bb609d367b76269a627689547bfbef", - "0x5e1ceb846fe1422b9524c7d014931072c3852df2d991470b08375edf6e762bb", - ], - [ - "0x7f751f98968212ebe5dff3ce06e8cb916709e0c48e3020c6b2b01c1bec0814b", - "0x36f6b64463f7c29fc3180616e340536bea7f01d226b68b6d45cd6dfbff811e4", - "0x61135c9846faf39b4511d74fe8de8b48dd4d0e469d6703d7ed4fe4fe8e0dbac", - ], - [ - "0xb58921a3fbdbb559b78f6acfca9a21a4ba83cc6e0ae3527fbaad907fc912b8", - "0x22a4f8a5cdc7474b9d16b61c2973847211d84eb2fb27b816e52821c2e2b1b1e", - "0x41cf6db5d6145edfeccbbc9a50b2ceedeb1765c61516ffcb112f810ad67036f", - ], - [ - "0xbe44689973db2b1cfc05fa8f4aec6fac6a0ff2fdfab744ade9de11416b6831", - "0x39bf209c4e117e16489cda45128096d6d148a237142dc4951df0b8239be148b", - "0x209cf541e5f74fc2b93310b8ce37b092a58282643860b5707c7eb980ea03a06", - ], - [ - "0x6b562e6005f34ee0bdc218ba681b6ba7232e122287036d18c22dd5afa95326d", - "0xe8103a23902be5dc6d5f59253a627a2a39c8aca11a914670e7a35dea38c8f", - "0x6a3725548c664fd06bdc1b4d5f9bed83ef8ca7468d68f4fbbf345de2d552f72", - ], - [ - "0x67fcd6997472e8e605d0f01a8eccc5f11a45c0aa21eb4ebb447b4af006a4a37", - "0x26144c95c8de3634075784d28c06c162a44366f77792d4064c95db6ecb5cff0", - "0x5b173c8b0eb7e9c4b3a874eb6307cda6fd875e3725061df895dc1466f350239", - ], - [ - "0x7e1c2d6fde8ac9f87bae06ad491d391c448f877e53298b6370f2165c3d54ddb", - "0x4db779f3e5b7424996f451b156fe4e28f74d61e7771f9e3fa433b57ca6627a9", - "0xbb930d8a6c6583713435ec06b6fed7825c3f71114acb93e240eed6970993dd", - ], - [ - "0x4472d73b2830565d708467e9296fb5599d3a08814c31c4189e9579c046e878f", - "0x7ba9c303dfee2d89e10e3c883ca5ce5614d23739b7cb2052cc23612b11170e2", - "0x21c0e3319ede47f0425dc9b2c1ed30e6356cb133e97579b822548eb9c4dc4b7", - ], - [ - "0x2cfd61139e50ddd37b09933816e2a0932e53b7dc4f4947565c1d41e877eb191", - "0x5abea18941a4976844544d92ee0eca65bdd10b3f170b0dc2f30acd37e26d8e7", - "0x77088fdb015c7947a6265e44fef6f724ea28ae28b26e6eee5a751b7ce6bcc21", - ], - [ - "0x3abdc9d677231325b3e3c43cfd443076b4ce33cddbc8446120dce84e6122b73", - "0x2250f430b7fe7d12e5d00b6b83e52a52ca94879ccfab81a7a602662c2d62c4d", - "0x5c92ef479c11bb51fb24ef76d57912b12660e7bd156d6cabbb1efb79a25861b", - ], - [ - "0x235ec597391648b510f616fa8b87900fd08fd4208a785cffcf784a63a0fd5c6", - "0x4ed4e872eb7e736207be77e9d11e38f396b5c0ba3376e855523c00b372cc668", - "0x5f9406febca3879b756ef3f6331890b3d46afa705908f68fb7d861c4f275a1b", - ], - [ - "0x1d9c501d9ff1fba621a9f61b68873c05f17b0384661f06d97edf441abdaa49d", - "0x4b0de22bbd0a58534982c8e28d2f6e169e37ba694774c4dfa530f41c535952e", - "0x1b4d48bd38a3f8602186aabb291eca0d319f0e3648b2574c49d6fd1b033d903", - ], - [ - "0x7558bbea55584bf1725d8aa67ddba626b6596bbd2f4e65719702cefcead4bab", - "0x1108f1a9500a52f561ea174600e266a70b157d56ece95b60a44cf7a3eef17be", - "0x8913d96a4f36b12becb92b4b6ae3f8c209fb90caab6668567289b67087bf60", - ], - [ - "0x6502262c51ad8f616926346857dec8cca2e99f5742b6bf223f4d8a6f32867a6", - "0x7cb5fcdc00892812889280505c915bde962ea034378b343cd3a5931d2ec0e52", - "0x2eb919524a89a26f90be9781a1515145baea3bc96b8cd1f01b221c4d2a1ce87", - ], - [ - "0x58efb6272921bc5eada46635e3567dced0662c0161223e3c1c63e8de3ec3d73", - "0x62fcd49ca9c7587b436d205ffc2a39594254a1ac34acd46d6955e7844d4f88e", - "0x635895330838846e62d9acce0b625f885e5941e54bd3a2106fcf837aef5313b", - ], - [ - "0x7da445b81e9b3d36d47a5f4d23b92a378a17f119d5e6e70629f8b41fefb12e3", - "0x2b22dab62f0817e9fc5737e189d5096a9027882bef1738943b7016256118343", - "0x1af01472348f395bacdfed1d27664d0d5bdea769be8fcb8fbef432b790e50d5", - ], - [ - "0x76b172dbbeec5a31de313b9390f79ec9284163c8e4986bc5b682e5ac6360309", - "0x70efaeae36f6af0f362f6cb423d2009b30ddb4178d46def0bdb2905b3e0862", - "0x6cb99b36e521ac0a39872686b84ee1d28c4942b8036a1c25a0e4117ccaeedf", - ], - [ - "0x29fd44305a5a9a70bbf9674e544bda0fb3d0fe5bb3aa743fd1b8a4fc1dc6055", - "0x6b447ded1046e83629b184d8c36db3a11a6778d8848142aa6363d6619f9764", - "0x642a8b4be4ba812cbfcf55a77339b5d357cceb6946fdc51c14b58f5b8989b59", - ], - [ - "0x489e0a26f65a1eecc6cc6aa5b6e775cbc51a73700bd794a7acd79ae1d95882a", - "0x3b19d4ef195975bbf78ab5dc2fd1d24816428f45a06293c1b9d57b9a02e9200", - "0x7d2dd994756eacba576b74790b2194971596f9cd59e55ad2884c52039013df5", - ], - [ - "0x1922810cc08f50bf300df869823b9f18b3327e29e9e765002970ef0f2e8c5f3", - "0x52f3afaf7c9102f1d46e1d79a70745b39c04376aafff05771cbd4a88ed418ac", - "0x7ccfc88e44a0507a95260f44203086e89552bbe53dcc46b376c5bcab6ea788e", - ], - [ - "0x2949125939e6ad94100228beff83823f5157dd8e067bc8819e40a1ab008dd9c", - "0x6cb64e3a0d37a6a4273ce4ee6929ba372d6811dde135af4078ba6e1912e1014", - "0xd63b53707acf8962f05f688129bf30ad43714257949cd9ded4bf5953837fae", - ], - [ - "0xbcb1549c9cabb5d13bb968b4ea22d0bb7d7460a6965702942092b32ef152d4", - "0x3d1c5233657ce31f5ead698fe76f6492792a7205ba0531a0ca25b8d8fe798c1", - "0x2240b9755182ee9066c2808b1e16ea448e26a83074558d9279f450b79f97516", - ], - [ - "0xcc203d8b0f90e30fe8e54f343cef59fe8d70882137de70c9b43ab6615a646c", - "0x310c6cc475d9346e061bacdc175ea9e119e937dea9d2100fa68e03c1f77910b", - "0x7f84b639f52e57420bc947defced0d8cbdbe033f578699397b83667049106c7", - ], - [ - "0x584ca7f01262c5bd89c4562f57139f47e9f038cb32ec35abe4e1da8de3e164a", - "0x1135eefaf69b6e4af7d02f562868be3e02fdc72e01e9510531f9afa78abbbde", - "0x372082b8a6c07100a50a3d33805827ad350c88b56f62c6d36a0d876856a99e8", - ], - [ - "0x7c3c12b819a8aad87499bac1a143fc59674f132e33898f0c119e3d12462dfe6", - "0x4f1354c51e8f6905b84157cfeff6822c056ce9e29d602eb46bd9b75a23836cf", - "0x2da9f26a8271659075739ba206507a08ac360150e849950ef3973548fbd2fca", - ], - [ - "0x287173956a2beb111b5ec29195e38cc3f6a65ff50801aa75fd78dd550702843", - "0x7273101c190ff64212420095a51c8411c7f3227f6a7a4a64ae6ba7f9201e126", - "0x2dbf2a6b56b26d23ebeb61e500687de749b03d3d349169699258ee4c98005fc", - ], - [ - "0x85b6cbb29739a6808e67f00ab89b52ab89ef8d92530394e4b910efd706c7fb", - "0x3d55b5f1171efda1dacbcbadfd5b910b493fa9589fd937e3e06ce26b08925a3", - "0xaaedaa6ef2fa707d16b3b295410c0e44f7a2f8135c207824f6ae2a9b16e90c", - ], - [ - "0x6aca6ebf70b1cb46c6331e9f1a5c4cc89b80f8adc5d18915c1cd0d496ccf5e1", - "0x1678602af36c28abb010f831d403d94d5e90003e6d37c677e9dd157fb27761", - "0x2022036bdf687f041b547fefdf36d4c2cd3f4b0526a88aafe60a0a8f508bad2", - ], - [ - "0x7bfc350957c968ca664397414bdfb8f9b8dfe49fb63e32353d4e2e8d1d4af6", - "0x2d639cbd418cb9fc24ea29ccd1d15ab81f43a499b27a06d3c5e2176f7ad79af", - "0xecdea7f959a4d488403d5b39687a1fe0dee3369e5fbc0f4779569f64506e0c", - ], - [ - "0x3f656bdc4fefd92b70658e2f1992ef9f22e5f2d28c490e21d4e34357154b558", - "0xd1b8cb1561eed32319638ccab9033dfec47596f8a6f4ce6594e19fddd59254", - "0x758ffc77c62e3e0f86ef6ea01545ad76f281ec2941da7222d1e8b4e2ec1f192", - ], - [ - "0x20315ca079570df995386e96aeaa1b4596aacd28f83c32f29a591c95e6fcac5", - "0x3e55cf341e7c280cb05f3d6ff9c8d9f2cfe76b84a9d1b0f54884b316b740d8d", - "0x4d56feb32cde74feede9749739be452e92c029007a06f6e67c81203bf650c68", - ], - [ - "0x4ee807aa678a9a433b6171eaa6a2544497f7599fb8145d7e8089f465403c89b", - "0x25d2bacc8f1ee7548cb5f394de2cb6e1f365e56a1bc579d0f9a8ad2ef2b3821", - "0x5f573de597ce1709fc20051f6501268cd4b278811924af1f237d15feb17bd49", - ], - [ - "0x30297c3c54a505f5826a280e053cf7a3c1e84a1dcf8b33c682cf85ddac86deb", - "0x2f5e9c47c9a86e043c7526a59783f03c6bc79b69b8709fe6a052b93a8339ae8", - "0x1bf75c7a739da8d29f9c23065ff8ccb1da7deec83e130bcd4a27a416c72b84b", - ], - [ - "0x60563d5f852ae875989017bd5c4cfdc29cd27fc4e91eeabdb8e864df3c3c675", - "0x7a4b1d70885aa820969635468daec94f8156c20e3131bd71005be1cd16ccf9e", - "0x347bb025695e497f1e201cd62aa4600b8b85cf718cd1d400f39c10e59cc5852", - ], - [ - "0x6783ab1e1ef97bb9e7f9381eb6ab0de2c4c9c2de413691ba8aa666292e9e217", - "0x133e0280c6de90e7b3870a07823c081fd9c4cb99d534debd6a7bfb4e5b0dd46", - "0x865d450ce29dc42fb5db72460b3560a2f093695573dff94fd0216eb925beec", - ], - [ - "0x1de023f840e054a35526dabacf0dee948efba06bcbb414ecd81a6b301664e57", - "0x55fc1e341bfdf7805015a96f724c5ac7cc7b892a292d38190631ab1a5388c4", - "0x2df6557bfd4a4e7e7b27bf51552d2b5162706a3e624faca01a307ef8d532858", - ], - [ - "0x113a8a66962ce08d92a6bd3e9c1d55ef8f226da95e4d629046d73d0507f6271", - "0x271577d6ee9fa377f2c889874ba5b44ca1076033db5c2de4f3367b08c008e53", - "0x3396b33911219b6b0365c09348a561ef1ccb956fc673bc5291d311866538574", - ], - [ - "0x1e1392f2da08549c8a7d89e899189306170baa3c3436e6a5398f69c8f321636", - "0x661545081032013df118e1d6e7c61a333e313b1a9a5b6d69c876bd2e7d694ca", - "0x6b14294e71cd7fb776edbd432d20eb8f66d00533574e46573516f0cacdeec88", - ], - [ - "0x7252fbbb06c2848338b1c41df31e4e51fe2a18e2406c671915cab6eb1a1d4f2", - "0x3ccf71be7cc2a9abcf5a09807c69679430c03645747621b7f5327cb00ff99da", - "0x29778dc707504fa6a9f7c97b4ceef0a9b39001d034441617757cd816dac919a", - ], - [ - "0x39473f6f06bb99e33590d34e3bae36e491f7bbf86a26aa55a8f5b27bb98d4c5", - "0x7ba7c32f875b71b895caa0215f996fd4ad92bab187e81417063dde91c08c027", - "0x37c1367e49cbfc403b22aac82abf83b0ed083148a5f4c92839e5d769bdab6b6", - ], - [ - "0x5c9eb899931d2f4b53ffcf833cdfa05c2068375ff933eb37ae34157c0b2d951", - "0x5f6054a4d48698ec27772fb50a7d2e5c1557ffdc1ffd07331f2ca26c6e3b661", - "0x20e6d62a2fe0fe9b0fab83e8c7d1e8bfd0fec827960e40a91df64664dcd7774", - ], - [ - "0x6290a56a489ad52120c426fe0e409c2ff17adf51f528cafb0d026d14ffd6aac", - "0x3703f16f990342c2267a6f7ece342705a32ca4c101417286279f6fc315edc7c", - "0x5194962daf6679b9a0c32b5a9a307ba92e2c630f70e439195b680dd296df3fd", - ], - [ - "0xe8eae20a79a7c1242c34617b01340fb5fd4bea2aa58b98d2400d9b515ee5e2", - "0x369058169d63091ae28bfb28def7cd8d00dd7c2894fae4ffec65242afa5cd45", - "0x418c963bc97195a74077503ee472f22cfdff0973190ab189c7b93103fd78167", - ], - [ - "0x68d07a3eefc78dc5b28b3f4dc93167fb8c97112d14a25b4d4db559720156386", - "0x517e892228df2d4f15a3c4241c98ba25ba0b5557375003f8748583a61836372", - "0x5cc0f0f6cf9be94a150116e7932f8fe74ac20ad8100c41dc9c99538792e279b", - ], - [ - "0x53d5d7863434c6629bdb1f8a648e4820883543e821f0f5c1668884c0be41ec8", - "0xa158126b89e6b0a600bf53f8101707b072218912dd0d9df2528f67de24fdf5", - "0x6b53b807265387ee582069a698323d44c204bed60672b8d8d073bed2fede503", - ], - [ - "0x1097fb448406b7a6de0877efd58c01be53be83bde9601a9acc9e0ca2091fda0", - "0xcbc0ff7239d3763902396389d67b3049ce1fefde66333ce37ca441f5a31bec", - "0x79a3d91dd8a309c632eb43d57b5c5d838ceebd64603f68a8141ebef84280e72", - ], - [ - "0x23fb472fe575135300f74e8f6de8fe1185078218eceb938900e7598a368db9", - "0x7ac73134016d2a8a4c63a6b9494c0bd7a6ba87cc33e8a8e23ebda18bfb67c2a", - "0x19a16068c3eac9c03f1b5c5ee2485ccc163d9ab17bb035d5df6e31c3dcf8f14", - ], - [ - "0x1f24b4356a6bbfd4d4ef9fd1634752820ee86a925725ac392134d90def073ea", - "0x3e44e7f7aeea6add59b6b4d11c60a528fb70727f35d817305971592333d36", - "0x5f93b02f826741414535a511ed3eb4fe85987ae57bc9807cbd94cd7513d394e", - ], - [ - "0xf0a0a88db99247d71c3d51d4197fa3fd1cc76e670607e35ca2d3bada29523a", - "0x3432226916d31f3acac1e211431fd4cd2b6f2e80626af6564bdde3e77608db0", - "0x55625941bfea6f48175192845a7ad74b0b82940ef5f393ca3830528d59cf919", - ], - [ - "0xddf48695b204477dfe4f8cb3ef1b39783e9b92f9276b858e2e585e318e20a4", - "0x260730a657ff8f38851a679ab2a1490434ee50d4953e7c5d3194578b08ae8e3", - "0x4cfd231373aa46d96283840bdb79ba6d7132775b398d324bcd206842b961aa9", - ], - [ - "0x3203843c41cd453f14fa0bc0b2191a27ebc659e74fd48f981e963de57eff25d", - "0x2c2f6ae5624d1fb8435d1c86bf76c260f5e77a54b006293705872e647cc46", - "0x780225456e63903b3e561384ef2e73a85b0e142b69752381535022014765f06", - ], - [ - "0x7f602ec1a80a051fd21b07f8e2960613082fc954b9a9ff641cc432a75c81887", - "0x62561b0a0a72239b60f6aaf7022b7d323fe77cd7c1ab432f0c8c118ca7e6bca", - "0x604fe5a6a22344aa69b05dea16b1cf22450c186d093754cb9b84a8a03b70bc8", - ], - [ - "0x1cf9987a4044716d3dc140bf5f9b76f6eada5995905189f8682eaf88aef2b7b", - "0x6bc0b2487c1eece3db47a4bdd60cf69debee233e91b50e9ee42ce22cbfbacbf", - "0x2f5dbb5055eb749a11403b93e90338b7620c51356d2c6adcbf87ab7ea0792e6", - ], - [ - "0x446328f4dddae6529743c43883d59c45f63b8a623a9cf318489e5fc4a550f61", - "0x4ba30c5240cde5bca6c4010fb4b481a25817b43d358399958584d2c48f5af25", - "0x5f5275f76425b15c89209117734ae85708351d2cf19af5fe39a32f89c2c8a89", - ], - [ - "0x576f3b5156f4763e18c7f98df3b2f7b993cdda4eb8cb92415e1be8e6af2fc17", - "0x11dc3f15cba928aed5a44b55a5b026df84a61719ed5adbb93c0e8e12d35ef3d", - "0x44c40e6bd52e91ad9896403ae4f543ae1c1d9ea047d75f8a6442b8feda04dca", - ], - [ - "0x1836d733a54013ebd0ccbf4974e80ac1954bf90fe9ea4e2c914ad01166026d8", - "0x3c553be9776b628a8159d306ef084727611df8037761f00f84ca02ce731b3ac", - "0x6ce94781c1a23fda1c7b87e0436b1b401ae11a6d757843e342f5017076a059", - ], - [ - "0x381ec71fbdef3160253be9f00f4e6b9e107f457812effb7371cc2daa0acd0ed", - "0x1844da9cc0eeadc6490d847320d9f3cd4fb574aa687bafdfe0ffa7bf2a8f1a1", - "0x7a8bf471f902d5abb27fea5b401483dedf97101047459682acfd7f9b65a812f", - ], - [ - "0x633b6fb004de62441915fb51ac174456f5a9cdff7aecb6e6b0d063839e56327", - "0x179ee5cec496194771200382bfc6d17bbe546ba88fed8b17535fd70fbc50ab6", - "0x2806c0786185986ea9891b42d565256b0312446f07435ac2cae194330bf8c42", - ], - [ - "0x438703d948708ae90c7a6b8af194b8b603bb2cdfd26bfa356ac9bb6ee041393", - "0x24446628f56029d7153bd3a482b7f6e1c56f4e02225c628a585d58a920035af", - "0x4c2a76e5ce832e8b0685cdeeea3a253ae48f6606790d817bd96025e5435e259", - ], - [ - "0x78a23323520994592933c079b148aed57d5e4ce1ab122d370983b8caa0e0300", - "0x79ca6c5e1025b2151144ea5937dd07cadce1aa691b19e6db87070ba51ec22c0", - "0x6b2e4a46e37af3cf952d9d34f8d6bd84a442ebfd1ac5d17314e48922af79c5d", - ], - [ - "0x305d6cd95cc2eab6805d93d3d8d74e1ca7d443f11e34a18e3529e0d03435c2", - "0x6097b4b8b90db14b39743ed23f8956cabb7aea70cc624a415c7c17b37fbf9a9", - "0x64e1b3f16c26c8845bdb98373e77dad3bdcc90865b0f0af96288707c18893f", - ], - [ - "0x649fafe673f21e623384d841221b73421c56014af2ffdf57f1579ae911fd335", - "0x7d806dccbf1a2696b294404e849722f2baa2f4d19005a49d1ba288a77fefe30", - "0x5951a37da53e3bbc0b3e2db1a9a235d7a03f48f443be6d659119c44aafc7522", - ], - [ - "0x6d87fa479fb59524d1912c3554ae3d010496a31bdacb542c816a1607a907731", - "0x1451cccd4200fa9d473ad73466b4e8c0a712a0b12bb6fc9462a3ac892acc9b2", - "0x3ca1b6400b3e51007642535f1ca9b03832ca0faa15e1c4ed82dd1efdc0763da", - ], - [ - "0x52c55735b2f0a6560ad1516a8f13592b0dd024ff4162539f993a99c7a1a4d95", - "0x7e04de60aa80132f0149d1dee29617de750bd5ce3e9fa5e62951d65f6b924cd", - "0x271784e6920a68e47c4c8fab71c8f8303ef29e26f289223edf63291c0a5495", - ], - [ - "0x5c7c19061a84d5960a04b8f0adaa603c8afe93f17b7f0e56b49514af43d0c69", - "0x172db5affe783af419da337cb79061e090943c2959dea1b38e4436f5482eafe", - "0x518b7975a6d8d310eac9fe4082916f021a7ecbadf18809746a9e061a2cb9456", - ], - [ - "0x20c5539dc45dd56d4bbc2440a9f5061d74b8ae5e37b34e8755a0315f1e196db", - "0x1ea6f5fb309fa4a08bc7d516e80efc3a977b47208283cf35a9d8bc213b90b14", - "0x50ce323c5128dc7fdd8ddd8ba9cfe2efd424b5de167c7257d1f766541e29ded", - ], - [ - "0x401e37d0e276547695538b41d3c28215b865f5b7d1b497a8919284c613cb7d8", - "0x645a0de30acc3117f2893056fc5880255daa12cc61261cc0fab9cf57c57397b", - "0x69bc3841eb0a310d9e988d75f09f698d4fdc9d0d69219f676b66ae7fa3d495b", - ], - [ - "0x2684bbe315ad2c4bdd47c38fe72db47cf0ae0c455cda5484baf523f136bdc6", - "0x11e0f83c547ca5c68202e8d34e5595a88858c2afa664365e4acb821fd8a13ee", - "0x4af4a7635f8c7515966567ceec34315d0f86ac66c1e5a5ecac945f1097b82ef", - ], - [ - "0x4fba58cf8aaf4893cb7158908ccc18b1dc48894d2bb46225c72b11f4c74b271", - "0x397c4c169115b468cc90da2e664f8c29a7f89be0ead679a38b0f44c8a2a0e20", - "0x6563b9ebb6450dbad397fa5dd13c501f326dd7f32be22e20998f59ec7bacff", - ], - [ - "0x376edb238f7b630ea81d307f4c79f9afec48562076dd09c36cd79e9cb817165", - "0x60d4208bb50eb15f29ed22addcd50a1b337504039690eb858584cda96e2e061", - "0x6a37d569d2fbc73dbff1019dc3465ec0f30da46918ab020344a52f1df9a9210", - ], - [ - "0xd3b174c7290c6bf412083ff35d23821dc512f1df073c1b429130371ac63b1a", - "0x226ed3d763477454b46eb2a5c3b814634d974919689fb489fe55e525b980373", - "0x5f3997e7dafcb2de0e7a23d33d2fd9ef06f4d79bd7ffa1930e8b0080d218513", - ], - [ - "0x7c5eec716d94634434df335a10bbac504f886f7f9d3c1648348c3fae8fdf14d", - "0x53cc30d7fe0f84e7e24fd22c0f9ad68a89da85553f871ef63d2f55f57e1a7c", - "0x368821ee335d71819b95769f47418569474a24f6e83b268fefa4cd58c4ec8fa", - ], - [ - "0x5334f75b052c0235119816883040da72c6d0a61538bdfff46d6a242bfeb7a1", - "0x5d0af4fcbd9e056c1020cca9d871ae68f80ee4af2ec6547cd49d6dca50aa431", - "0x30131bce2fba5694114a19c46d24e00b4699dc00f1d53ba5ab99537901b1e65", - ], - [ - "0x5646a95a7c1ae86b34c0750ed2e641c538f93f13161be3c4957660f2e788965", - "0x4b9f291d7b430c79fac36230a11f43e78581f5259692b52c90df47b7d4ec01a", - "0x5006d393d3480f41a98f19127072dc83e00becf6ceb4d73d890e74abae01a13", - ], - [ - "0x62c9d42199f3b260e7cb8a115143106acf4f702e6b346fd202dc3b26a679d80", - "0x51274d092db5099f180b1a8a13b7f2c7606836eabd8af54bf1d9ac2dc5717a5", - "0x61fc552b8eb75e17ad0fb7aaa4ca528f415e14f0d9cdbed861a8db0bfff0c5b", - ], -]; diff --git a/crypto/src/merkle_tree/backends/field_element.rs b/crypto/src/merkle_tree/backends/field_element.rs index d9ad88fe17..11171f2180 100644 --- a/crypto/src/merkle_tree/backends/field_element.rs +++ b/crypto/src/merkle_tree/backends/field_element.rs @@ -1,12 +1,8 @@ -use crate::hash::poseidon::starknet::parameters::{DefaultPoseidonParams, PermutationParameters}; -use crate::hash::poseidon::starknet::Poseidon; +use crate::hash::poseidon::Poseidon; use crate::merkle_tree::traits::IsMerkleTreeBackend; use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsField, IsPrimeField}, - }, + field::{element::FieldElement, traits::IsField}, traits::Serializable, }; use sha3::{ @@ -33,19 +29,19 @@ impl IsMerkleTreeBackend for FieldElementBackend where F: IsField, - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, [u8; NUM_BYTES]: From::OutputSize>>, { type Node = [u8; NUM_BYTES]; type Data = FieldElement; - fn hash_data(&self, input: &FieldElement) -> [u8; NUM_BYTES] { + fn hash_data(input: &FieldElement) -> [u8; NUM_BYTES] { let mut hasher = D::new(); hasher.update(input.serialize()); hasher.finalize().into() } - fn hash_new_parent(&self, left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { + fn hash_new_parent(left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { let mut hasher = D::new(); hasher.update(left); hasher.update(right); @@ -53,36 +49,28 @@ where } } -#[derive(Clone)] -pub struct TreePoseidon { - poseidon: Poseidon, +#[derive(Clone, Default)] +pub struct TreePoseidon { + _poseidon: PhantomData

, } -impl Default for TreePoseidon +impl

IsMerkleTreeBackend for TreePoseidon

where - F: IsPrimeField, + P: Poseidon + Default, + FieldElement: Sync + Send, { - fn default() -> Self { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - let poseidon = Poseidon::new_with_params(params); - - Self { poseidon } - } -} - -impl IsMerkleTreeBackend for TreePoseidon -where - F: IsPrimeField, -{ - type Node = FieldElement; - type Data = FieldElement; + type Node = FieldElement; + type Data = FieldElement; - fn hash_data(&self, input: &FieldElement) -> FieldElement { - self.poseidon.hash_single(input) + fn hash_data(input: &FieldElement) -> FieldElement { + P::hash_single(input) } - fn hash_new_parent(&self, left: &FieldElement, right: &FieldElement) -> FieldElement { - self.poseidon.hash(left, right) + fn hash_new_parent( + left: &FieldElement, + right: &FieldElement, + ) -> FieldElement { + P::hash(left, right) } } diff --git a/crypto/src/merkle_tree/backends/field_element_vector.rs b/crypto/src/merkle_tree/backends/field_element_vector.rs index dd245001a6..509c56752c 100644 --- a/crypto/src/merkle_tree/backends/field_element_vector.rs +++ b/crypto/src/merkle_tree/backends/field_element_vector.rs @@ -1,13 +1,9 @@ use std::marker::PhantomData; -use crate::hash::poseidon::starknet::parameters::{DefaultPoseidonParams, PermutationParameters}; -use crate::hash::poseidon::starknet::Poseidon; +use crate::hash::poseidon::Poseidon; use crate::merkle_tree::traits::IsMerkleTreeBackend; use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsField, IsPrimeField}, - }, + field::{element::FieldElement, traits::IsField}, traits::Serializable, }; use sha3::{ @@ -36,11 +32,12 @@ where F: IsField, FieldElement: Serializable, [u8; NUM_BYTES]: From::OutputSize>>, + Vec>: Sync + Send, { type Node = [u8; NUM_BYTES]; type Data = Vec>; - fn hash_data(&self, input: &Vec>) -> [u8; NUM_BYTES] { + fn hash_data(input: &Vec>) -> [u8; NUM_BYTES] { let mut hasher = D::new(); for element in input.iter() { hasher.update(element.serialize()); @@ -50,7 +47,7 @@ where result_hash } - fn hash_new_parent(&self, left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { + fn hash_new_parent(left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { let mut hasher = D::new(); hasher.update(left); hasher.update(right); @@ -60,36 +57,29 @@ where } } -#[derive(Clone)] -pub struct BatchPoseidonTree { - poseidon: Poseidon, +#[derive(Clone, Default)] +pub struct BatchPoseidonTree { + _poseidon: PhantomData

, } -impl Default for BatchPoseidonTree +impl

IsMerkleTreeBackend for BatchPoseidonTree

where - F: IsPrimeField, + P: Poseidon + Default, + Vec>: Sync + Send, + FieldElement: Sync + Send, { - fn default() -> Self { - let params = PermutationParameters::new_with(DefaultPoseidonParams::CairoStark252); - let poseidon = Poseidon::new_with_params(params); - - Self { poseidon } - } -} - -impl IsMerkleTreeBackend for BatchPoseidonTree -where - F: IsPrimeField, -{ - type Node = FieldElement; - type Data = Vec>; + type Node = FieldElement; + type Data = Vec>; - fn hash_data(&self, input: &Vec>) -> FieldElement { - self.poseidon.hash_many(input) + fn hash_data(input: &Vec>) -> FieldElement { + P::hash_many(input) } - fn hash_new_parent(&self, left: &FieldElement, right: &FieldElement) -> FieldElement { - self.poseidon.hash(left, right) + fn hash_new_parent( + left: &FieldElement, + right: &FieldElement, + ) -> FieldElement { + P::hash(left, right) } } diff --git a/crypto/src/merkle_tree/merkle.rs b/crypto/src/merkle_tree/merkle.rs index e97d821f5b..a7ff332444 100644 --- a/crypto/src/merkle_tree/merkle.rs +++ b/crypto/src/merkle_tree/merkle.rs @@ -13,8 +13,7 @@ where B: IsMerkleTreeBackend, { pub fn build(unhashed_leaves: &[B::Data]) -> Self { - let hasher = B::default(); - let mut hashed_leaves: Vec = hasher.hash_leaves(unhashed_leaves); + let mut hashed_leaves: Vec = B::hash_leaves(unhashed_leaves); //The leaf must be a power of 2 set hashed_leaves = complete_until_power_of_two(&mut hashed_leaves); @@ -26,7 +25,7 @@ where inner_nodes.extend(hashed_leaves); //Build the inner nodes of the tree - build(&mut inner_nodes, ROOT, &hasher); + build::(&mut inner_nodes, ROOT); MerkleTree { root: inner_nodes[ROOT].clone(), diff --git a/crypto/src/merkle_tree/proof.rs b/crypto/src/merkle_tree/proof.rs index c2914cf503..21c1b9c746 100644 --- a/crypto/src/merkle_tree/proof.rs +++ b/crypto/src/merkle_tree/proof.rs @@ -20,14 +20,13 @@ impl Proof { where B: IsMerkleTreeBackend, { - let hasher = B::default(); - let mut hashed_value = hasher.hash_data(value); + let mut hashed_value = B::hash_data(value); for sibling_node in self.merkle_path.iter() { if index % 2 == 0 { - hashed_value = hasher.hash_new_parent(&hashed_value, sibling_node); + hashed_value = B::hash_new_parent(&hashed_value, sibling_node); } else { - hashed_value = hasher.hash_new_parent(sibling_node, &hashed_value); + hashed_value = B::hash_new_parent(sibling_node, &hashed_value); } index >>= 1; diff --git a/crypto/src/merkle_tree/test_merkle.rs b/crypto/src/merkle_tree/test_merkle.rs index 076e26fce7..4fc8e2cdc1 100644 --- a/crypto/src/merkle_tree/test_merkle.rs +++ b/crypto/src/merkle_tree/test_merkle.rs @@ -23,15 +23,18 @@ impl Default for TestBackend { } } -impl IsMerkleTreeBackend for TestBackend { +impl IsMerkleTreeBackend for TestBackend +where + FieldElement: Sync + Send, +{ type Node = FieldElement; type Data = FieldElement; - fn hash_data(&self, input: &Self::Data) -> Self::Node { + fn hash_data(input: &Self::Data) -> Self::Node { input + input } - fn hash_new_parent(&self, left: &Self::Node, right: &Self::Node) -> Self::Node { + fn hash_new_parent(left: &Self::Node, right: &Self::Node) -> Self::Node { left + right } } diff --git a/crypto/src/merkle_tree/traits.rs b/crypto/src/merkle_tree/traits.rs index e8e010eb0f..dce3e253b6 100644 --- a/crypto/src/merkle_tree/traits.rs +++ b/crypto/src/merkle_tree/traits.rs @@ -1,23 +1,28 @@ +#[cfg(feature = "parallel")] +use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; + /// A backend for Merkle trees. This defines raw `Data` from which the Merkle /// tree is built from. It also defines the `Node` type and the hash function /// used to build parent nodes from children nodes. pub trait IsMerkleTreeBackend: Default { - type Node: PartialEq + Eq + Clone; - type Data; + type Node: PartialEq + Eq + Clone + Sync + Send; + type Data: Sync + Send; /// This function takes a single variable `Data` and converts it to a node. - fn hash_data(&self, leaf: &Self::Data) -> Self::Node; + fn hash_data(leaf: &Self::Data) -> Self::Node; /// This function takes the list of data from which the Merkle /// tree will be built from and converts it to a list of leaf nodes. - fn hash_leaves(&self, unhashed_leaves: &[Self::Data]) -> Vec { - unhashed_leaves - .iter() - .map(|leaf| self.hash_data(leaf)) - .collect() + fn hash_leaves(unhashed_leaves: &[Self::Data]) -> Vec { + #[cfg(feature = "parallel")] + let iter = unhashed_leaves.par_iter(); + #[cfg(not(feature = "parallel"))] + let iter = unhashed_leaves.iter(); + + iter.map(|leaf| Self::hash_data(leaf)).collect() } /// This function takes to children nodes and builds a new parent node. /// It will be used in the construction of the Merkle tree. - fn hash_new_parent(&self, child_1: &Self::Node, child_2: &Self::Node) -> Self::Node; + fn hash_new_parent(child_1: &Self::Node, child_2: &Self::Node) -> Self::Node; } diff --git a/crypto/src/merkle_tree/utils.rs b/crypto/src/merkle_tree/utils.rs index 64dc0de833..ab76176bb7 100644 --- a/crypto/src/merkle_tree/utils.rs +++ b/crypto/src/merkle_tree/utils.rs @@ -28,7 +28,7 @@ pub fn is_power_of_two(x: usize) -> bool { (x != 0) && ((x & (x - 1)) == 0) } -pub fn build(nodes: &mut Vec, parent_index: usize, hasher: &B) +pub fn build(nodes: &mut Vec, parent_index: usize) where B::Node: Clone, { @@ -39,11 +39,10 @@ where let left_child_index = left_child_index(parent_index); let right_child_index = right_child_index(parent_index); - build(nodes, left_child_index, hasher); - build(nodes, right_child_index, hasher); + build::(nodes, left_child_index); + build::(nodes, right_child_index); - nodes[parent_index] = - hasher.hash_new_parent(&nodes[left_child_index], &nodes[right_child_index]); + nodes[parent_index] = B::hash_new_parent(&nodes[left_child_index], &nodes[right_child_index]); } pub fn is_leaf(lenght: usize, node_index: usize) -> bool { @@ -73,8 +72,7 @@ mod tests { // expected |2|4|6|8| fn hash_leaves_from_a_list_of_field_elemnts() { let values: Vec = (1..5).map(FE::new).collect(); - let hasher = TestBackend::default(); - let hashed_leaves = hasher.hash_leaves(&values); + let hashed_leaves = TestBackend::hash_leaves(&values); let list_of_nodes = &[FE::new(2), FE::new(4), FE::new(6), FE::new(8)]; for (leaf, expected_leaf) in hashed_leaves.iter().zip(list_of_nodes) { assert_eq!(leaf, expected_leaf); @@ -105,8 +103,7 @@ mod tests { let mut nodes = vec![FE::zero(); leaves.len() - 1]; nodes.extend(leaves); - let hasher = TestBackend::default(); - build::>(&mut nodes, ROOT, &hasher); + build::>(&mut nodes, ROOT); assert_eq!(nodes[ROOT], FE::new(10)); } } diff --git a/examples/merkle-tree-cli/src/main.rs b/examples/merkle-tree-cli/src/main.rs index 9817bf39d6..b23b3f5e1d 100644 --- a/examples/merkle-tree-cli/src/main.rs +++ b/examples/merkle-tree-cli/src/main.rs @@ -1,13 +1,12 @@ mod commands; use clap::Parser; use commands::{MerkleArgs, MerkleEntity}; -use lambdaworks_crypto::{ - hash::poseidon::Poseidon, - merkle_tree::{merkle::MerkleTree, proof::Proof}, +use lambdaworks_crypto::hash::poseidon::starknet::PoseidonCairoStark252; +use lambdaworks_crypto::merkle_tree::{ + backends::field_element::TreePoseidon, merkle::MerkleTree, proof::Proof, }; -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::BLS12381PrimeField, - field::element::FieldElement, +use lambdaworks_math::field::{ + element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, }; use std::io::BufWriter; use std::{ @@ -15,7 +14,7 @@ use std::{ io::{self, Write}, }; -type FE = FieldElement; +type FE = FieldElement; fn load_fe_from_file(file_path: &String) -> Result { FE::from_hex(&fs::read_to_string(file_path)?.replace('\n', "")) @@ -32,7 +31,7 @@ fn load_tree_values(tree_path: &String) -> Result, io::Error> { fn generate_merkle_tree(tree_path: String) -> Result<(), io::Error> { let values: Vec = load_tree_values(&tree_path)?; - let merkle_tree = MerkleTree::>::build(&values); + let merkle_tree = MerkleTree::>::build(&values); let root = merkle_tree.root.representative().to_string(); println!("Generated merkle tree with root: {:?}", root); @@ -46,7 +45,7 @@ fn generate_merkle_tree(tree_path: String) -> Result<(), io::Error> { fn generate_merkle_proof(tree_path: String, pos: usize) -> Result<(), io::Error> { let values: Vec = load_tree_values(&tree_path)?; - let merkle_tree = MerkleTree::>::build(&values); + let merkle_tree = MerkleTree::>::build(&values); let Some(proof) = merkle_tree.get_proof_by_pos(pos) else { return Err(io::Error::new(io::ErrorKind::Other, "Index out of bounds")); @@ -72,7 +71,7 @@ fn verify_merkle_proof( let file_str = fs::read_to_string(proof_path)?; let proof: Proof = serde_json::from_str(&file_str)?; - match proof.verify::>(&root_hash, index, &leaf) { + match proof.verify::>(&root_hash, index, &leaf) { true => println!("\x1b[32mMerkle proof verified succesfully\x1b[0m"), false => println!("\x1b[31mMerkle proof failed verifying\x1b[0m"), } diff --git a/exercises/message/src/cairo/air.rs b/exercises/message/src/cairo/air.rs index 4afa9076c4..d361058279 100644 --- a/exercises/message/src/cairo/air.rs +++ b/exercises/message/src/cairo/air.rs @@ -485,8 +485,7 @@ fn add_pub_memory_in_public_input_section( let mut v_aux = values.to_owned(); let public_input_section = addresses.len() - public_input.public_memory.len(); - let output_range = public_input.memory_segments.get(&MemorySegment::Output); - let pub_memory_addrs = get_pub_memory_addrs(output_range, public_input); + let pub_memory_addrs = public_input.public_memory.keys().cloned().collect(); a_aux.splice(public_input_section.., pub_memory_addrs); for i in public_input_section..a_aux.len() { diff --git a/exercises/message/src/starks/fri/mod.rs b/exercises/message/src/starks/fri/mod.rs index c93a1cd411..f7fb9fc186 100644 --- a/exercises/message/src/starks/fri/mod.rs +++ b/exercises/message/src/starks/fri/mod.rs @@ -61,7 +61,7 @@ where let last_value = last_poly .coefficients() - .get(0) + .first() .unwrap_or(&FieldElement::zero()) .clone(); diff --git a/math/Cargo.toml b/math/Cargo.toml index 1189fa5788..bc6b393dcf 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -12,6 +12,8 @@ thiserror = { version = "1.0", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } serde_json = { version = "1.0", optional = true } proptest = { version = "1.1.0", optional = true } +winter-math = { package = "winter-math", version = "0.6.4", default-features = false, optional = true } +miden-core = { package = "miden-core" , version = "0.7", default-features = false, optional = true } # rayon rayon = { version = "1.7", optional = true } @@ -41,6 +43,7 @@ std = ["dep:thiserror"] lambdaworks-serde-binary = ["dep:serde", "std"] lambdaworks-serde-string = ["dep:serde", "dep:serde_json", "std"] proptest = ["dep:proptest"] +winter_compatibility = ["winter-math", "miden-core"] # gpu metal = [ @@ -92,3 +95,4 @@ harness = false name = "criterion_metal" harness = false required-features = ["metal"] + diff --git a/math/benches/criterion_fft.rs b/math/benches/criterion_fft.rs index 8d1c761351..5ee486caa2 100644 --- a/math/benches/criterion_fft.rs +++ b/math/benches/criterion_fft.rs @@ -6,7 +6,7 @@ use utils::stark252_utils; mod utils; -const SIZE_ORDERS: [u64; 4] = [21, 22, 23, 24]; +const SIZE_ORDERS: [u64; 5] = [20, 21, 22, 23, 24]; pub fn fft_benchmarks(c: &mut Criterion) { let mut group = c.benchmark_group("Ordered FFT"); @@ -22,7 +22,7 @@ pub fn fft_benchmarks(c: &mut Criterion) { group.bench_with_input( "Sequential from NR radix2", - &(input_nat, twiddles_bitrev), + &(input_nat.clone(), twiddles_bitrev.clone()), |bench, (input, twiddles)| { bench.iter_batched( || input.clone(), @@ -46,6 +46,21 @@ pub fn fft_benchmarks(c: &mut Criterion) { ); }, ); + if order % 2 == 0 { + group.bench_with_input( + "Sequential from NR radix4", + &(input_nat, twiddles_bitrev), + |bench, (input, twiddles)| { + bench.iter_batched( + || input.clone(), + |mut input| { + fft_functions::ordered_fft_nr4(&mut input, twiddles); + }, + BatchSize::LargeInput, + ); + }, + ); + } } group.finish(); diff --git a/math/benches/utils/fft_functions.rs b/math/benches/utils/fft_functions.rs index 7dc97e04f3..2bb587599e 100644 --- a/math/benches/utils/fft_functions.rs +++ b/math/benches/utils/fft_functions.rs @@ -3,12 +3,10 @@ use criterion::black_box; use lambdaworks_math::fft::cpu::{ bit_reversing::in_place_bit_reverse_permute, - fft::{in_place_nr_2radix_fft, in_place_rn_2radix_fft}, + fft::{in_place_nr_2radix_fft, in_place_nr_4radix_fft, in_place_rn_2radix_fft}, roots_of_unity::get_twiddles, }; -use lambdaworks_math::{ - fft::polynomial::FFTPoly, field::traits::RootsConfig, polynomial::Polynomial, -}; +use lambdaworks_math::{field::traits::RootsConfig, polynomial::Polynomial}; use super::stark252_utils::{F, FE}; @@ -20,6 +18,10 @@ pub fn ordered_fft_rn(input: &mut [FE], twiddles: &[FE]) { in_place_rn_2radix_fft(input, twiddles); } +pub fn ordered_fft_nr4(input: &mut [FE], twiddles: &[FE]) { + in_place_nr_4radix_fft(input, twiddles); +} + pub fn twiddles_generation(order: u64, config: RootsConfig) { get_twiddles::(order, config).unwrap(); } @@ -29,9 +31,9 @@ pub fn bitrev_permute(input: &mut [FE]) { } pub fn poly_evaluate_fft(poly: &Polynomial) -> Vec { - poly.evaluate_fft(black_box(1), black_box(None)).unwrap() + Polynomial::evaluate_fft::(poly, black_box(1), black_box(None)).unwrap() } pub fn poly_interpolate_fft(evals: &[FE]) { - Polynomial::interpolate_fft(evals).unwrap(); + Polynomial::interpolate_fft::(evals).unwrap(); } diff --git a/math/benches/utils/metal_functions.rs b/math/benches/utils/metal_functions.rs index 6838c291b1..7fd60c514f 100644 --- a/math/benches/utils/metal_functions.rs +++ b/math/benches/utils/metal_functions.rs @@ -1,6 +1,5 @@ use lambdaworks_gpu::metal::abstractions::state::MetalState; use lambdaworks_math::fft::gpu::metal::ops::*; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::{field::traits::RootsConfig, polynomial::Polynomial}; // WARN: These should always be fields supported by Metal, else the last two benches will use CPU FFT. @@ -22,8 +21,8 @@ pub fn bitrev_permute(input: &[FE]) { } pub fn poly_evaluate_fft(poly: &Polynomial) { - poly.evaluate_fft(1, None).unwrap(); + Polynomial::evaluate_fft::(poly, 1, None).unwrap(); } pub fn poly_interpolate_fft(evals: &[FE]) { - Polynomial::interpolate_fft(evals).unwrap(); + Polynomial::interpolate_fft::(evals).unwrap(); } diff --git a/math/src/elliptic_curve/edwards/traits.rs b/math/src/elliptic_curve/edwards/traits.rs index aedb1e7a88..9890c051cd 100644 --- a/math/src/elliptic_curve/edwards/traits.rs +++ b/math/src/elliptic_curve/edwards/traits.rs @@ -12,7 +12,7 @@ pub trait IsEdwards: IsEllipticCurve + Clone + Debug { y: &FieldElement, ) -> FieldElement { (Self::a() * x.pow(2_u16) + y.pow(2_u16)) - - FieldElement::one() + - FieldElement::::one() - Self::d() * x.pow(2_u16) * y.pow(2_u16) } } diff --git a/math/src/elliptic_curve/point.rs b/math/src/elliptic_curve/point.rs index aaf62e7d14..b55b541e1c 100644 --- a/math/src/elliptic_curve/point.rs +++ b/math/src/elliptic_curve/point.rs @@ -68,7 +68,8 @@ impl Eq for ProjectivePoint {} mod tests { use crate::cyclic_group::IsGroup; use crate::elliptic_curve::short_weierstrass::curves::test_curve_1::{ - TestCurve1, TestCurveQuadraticNonResidue, TEST_CURVE_1_MAIN_SUBGROUP_ORDER, + TestCurve1, TestCurvePrimeField, TestCurveQuadraticNonResidue, + TEST_CURVE_1_MAIN_SUBGROUP_ORDER, }; use crate::elliptic_curve::short_weierstrass::curves::test_curve_2::TestCurve2; use crate::field::element::FieldElement; @@ -78,7 +79,7 @@ mod tests { use crate::field::extensions::quadratic::QuadraticExtensionFieldElement; #[allow(clippy::upper_case_acronyms)] - type FEE = QuadraticExtensionFieldElement; + type FEE = QuadraticExtensionFieldElement; // This tests only apply for the specific curve found in the configuration file. #[test] diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs index a822c3d4dd..effa080bc1 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs @@ -1,27 +1,21 @@ use super::field_extension::BLS12381PrimeField; -use crate::cyclic_group::IsGroup; -use crate::elliptic_curve::short_weierstrass::curves::bls12_381::curve::BLS12381Curve; -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::field::element::FieldElement; -use crate::unsigned_integer::element::U256; - -#[cfg(feature = "std")] use crate::{ - elliptic_curve::traits::FromAffine, errors::ByteConversionError, traits::ByteConversion, + elliptic_curve::short_weierstrass::{ + curves::bls12_381::curve::BLS12381Curve, point::ShortWeierstrassProjectivePoint, + }, + field::element::FieldElement, }; #[cfg(feature = "std")] use std::{cmp::Ordering, ops::Neg}; +#[cfg(feature = "std")] +use crate::{ + cyclic_group::IsGroup, elliptic_curve::traits::FromAffine, errors::ByteConversionError, + traits::ByteConversion, +}; + pub type G1Point = ShortWeierstrassProjectivePoint; pub type BLS12381FieldElement = FieldElement; -const MODULUS: U256 = - U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); - -pub fn check_point_is_in_subgroup(point: &G1Point) -> bool { - let inf = G1Point::neutral_element(); - let aux_point = point.operate_with_self(MODULUS); - inf == aux_point -} #[cfg(feature = "std")] pub fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result { @@ -68,7 +62,8 @@ pub fn decompress_g1_point(input_bytes: &mut [u8; 48]) -> Result; pub type BLS12381TwistCurveFieldElement = FieldElement; @@ -35,18 +43,111 @@ impl IsShortWeierstrass for BLS12381Curve { } } +/// This is equal to the frobenius trace of the BLS12 381 curve minus one or seed value z. +pub const MILLER_LOOP_CONSTANT: u64 = 0xd201000000010000; + +/// ๐›ฝ : primitive cube root of unity of ๐นโ‚š that ยงsatisfies the minimal equation +/// ๐›ฝยฒ + ๐›ฝ + 1 = 0 mod ๐‘ +pub const CUBE_ROOT_OF_UNITY_G1: BLS12381FieldElement = FieldElement::from_hex_unchecked( + "5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe", +); + +/// x-coordinate of ๐œ โˆ˜ ๐œ‹_q โˆ˜ ๐œโปยน, where ๐œ is the isomorphism u:E'(๐”ฝโ‚šโ‚†) โˆ’> E(๐”ฝโ‚šโ‚โ‚‚) from the twist to E +pub const ENDO_U: BLS12381TwistCurveFieldElement = +BLS12381TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("0"), + FieldElement::from_hex_unchecked("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad") +]); + +/// y-coordinate of ๐œ โˆ˜ ๐œ‹_q โˆ˜ ๐œโปยน, where ๐œ is the isomorphism u:E'(๐”ฝโ‚šโ‚†) โˆ’> E(๐”ฝโ‚šโ‚โ‚‚) from the twist to E +pub const ENDO_V: BLS12381TwistCurveFieldElement = +BLS12381TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2"), + FieldElement::from_hex_unchecked("6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09") +]); + +impl ShortWeierstrassProjectivePoint { + /// Returns ๐œ™(P) = (๐‘ฅ, ๐‘ฆ) โ‡’ (๐›ฝ๐‘ฅ, ๐‘ฆ), where ๐›ฝ is the Cube Root of Unity in the base prime field + /// https://eprint.iacr.org/2022/352.pdf 2 Preliminaries + fn phi(&self) -> Self { + // This clone is unsightly + let mut a = self.clone(); + a.0.value[0] = a.x() * CUBE_ROOT_OF_UNITY_G1; + a + } + + /// ๐œ™(P) = โˆ’๐‘ขยฒP + /// https://eprint.iacr.org/2022/352.pdf 4.3 Prop. 4 + pub fn is_in_subgroup(&self) -> bool { + self.operate_with_self(MILLER_LOOP_CONSTANT) + .operate_with_self(MILLER_LOOP_CONSTANT) + .neg() + == self.phi() + } +} + +impl ShortWeierstrassProjectivePoint { + /// ๐œ“(P) = ๐œ โˆ˜ ๐œ‹โ‚š โˆ˜ ๐œโปยน, where ๐œ is the isomorphism u:E'(๐”ฝโ‚šโ‚†) โˆ’> E(๐”ฝโ‚šโ‚โ‚‚) from the twist to E,, ๐œ‹โ‚š is the p-power frobenius endomorphism + /// and ๐œ“ satisifies minmal equation ๐‘‹ยฒ + ๐‘ก๐‘‹ + ๐‘ž = ๐‘‚ + /// https://eprint.iacr.org/2022/352.pdf 4.2 (7) + fn psi(&self) -> Self { + let [x, y, z] = self.coordinates(); + Self::new([ + x.conjugate() * ENDO_U, + y.conjugate() * ENDO_V, + z.conjugate(), + ]) + } + + /// ๐œ“(P) = ๐‘ขP, where ๐‘ข = SEED of the curve + /// https://eprint.iacr.org/2022/352.pdf 4.2 + pub fn is_in_subgroup(&self) -> bool { + self.psi() == self.operate_with_self(MILLER_LOOP_CONSTANT).neg() + } +} + #[cfg(test)] mod tests { use super::*; use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, + cyclic_group::IsGroup, + elliptic_curve::{ + short_weierstrass::curves::bls12_381::field_extension::BLS12381_PRIME_FIELD_ORDER, + traits::EllipticCurveError, + }, field::element::FieldElement, + unsigned_integer::element::U384, }; - use super::BLS12381Curve; + // -15132376222941642751 = MILLER_LOOP_CONSTANT + 1 = -d20100000000ffff + // we want the positive of this coordinate based on x^2 - tx + q + pub const TRACE_OF_FROBENIUS: U256 = U256::from_u64(15132376222941642751); + + const ENDO_U_2: BLS12381TwistCurveFieldElement = + BLS12381TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac"), + FieldElement::from_hex_unchecked("0") + ]); + + const ENDO_V_2: BLS12381TwistCurveFieldElement = + BLS12381TwistCurveFieldElement::const_from_raw([ + FieldElement::from_hex_unchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa"), + FieldElement::from_hex_unchecked("0") + ]); + + // Cmoputes the psi^2() 'Untwist Frobenius Endomorphism' + fn psi_square( + p: &ShortWeierstrassProjectivePoint, + ) -> ShortWeierstrassProjectivePoint { + let [x, y, z] = p.coordinates(); + // Since power of frobenius map is 2 we apply once as applying twice is inverse + ShortWeierstrassProjectivePoint::new([x * ENDO_U_2, y * ENDO_V_2, z.clone()]) + } #[allow(clippy::upper_case_acronyms)] type FEE = FieldElement; + #[allow(clippy::upper_case_acronyms)] + type FTE = FieldElement; fn point_1() -> ShortWeierstrassProjectivePoint { let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); @@ -117,4 +218,74 @@ mod tests { g.operate_with_self(3_u16) ); } + + #[test] + fn generator_g1_is_in_subgroup() { + let g = BLS12381Curve::generator(); + assert!(g.is_in_subgroup()) + } + + #[test] + fn arbitrary_g1_point_is_in_subgroup() { + let g = BLS12381Curve::generator().operate_with_self(32u64); + assert!(g.is_in_subgroup()) + } + + //TODO + #[test] + fn arbitrary_g1_point_not_in_subgroup() { + let x = FEE::new_base("178212cbe4a3026c051d4f867364b3ea84af623f93233b347ffcd3d6b16f16e0a7aedbe1c78d33c6beca76b2b75c8486"); + let y = FEE::new_base("13a8b1347e5b43bc4051754b2a29928b5df78cf03ca3b1f73d0424b09fccdef116c9f0ecbec7420a99b2dd785209e9d"); + let p = BLS12381Curve::create_point_from_affine(x, y).unwrap(); + assert!(!p.is_in_subgroup()) + } + + #[test] + fn generator_g2_is_in_subgroup() { + let g = BLS12381TwistCurve::generator(); + assert!(g.is_in_subgroup()) + } + + #[test] + fn arbitrary_g2_point_is_in_subgroup() { + let g = BLS12381TwistCurve::generator().operate_with_self(32u64); + assert!(g.is_in_subgroup()) + } + + //`TODO` + #[test] + fn arbitrary_g2_point_not_in_subgroup() { + let x = FTE::new([ + FEE::new(U384::from_hex_unchecked("97798b4a61ac301bbee71e36b5174e2f4adfe3e1729bdae1fcc9965ae84181be373aa80414823eed694f1270014012d")), + FEE::new(U384::from_hex_unchecked("c9852cc6e61868966249aec153b50b29b3c22409f4c7880fd13121981c103c8ef84d9ea29b552431360e82cf69219fa")) + ]); + let y = FTE::new([ + FEE::new(U384::from_hex_unchecked("16cb3a60f3fa52c8273aceeb94c4c7303e8074aa9eedec7355bbb1e8cceedd4ec1497f573f62822140377b8e339619ed")), + FEE::new(U384::from_hex_unchecked("1cd919b08afe06bebe9adf6223a55868a6fd8b77efc5c67b60fff39be36e9b44b7f10db16827c83b43ad2dad1947778")) + ]); + + let p = BLS12381TwistCurve::create_point_from_affine(x, y).unwrap(); + assert!(!p.is_in_subgroup()) + } + + #[test] + fn g2_conjugate_works() { + let a = FTE::zero(); + let mut expected = a.conjugate(); + expected = expected.conjugate(); + + assert_eq!(a, expected); + } + + #[test] + fn untwist_morphism_has_minimal_poly() { + // generator + let p = BLS12381TwistCurve::generator(); + let psi_square = psi_square(&p); + let tx = p.psi().operate_with_self(TRACE_OF_FROBENIUS).neg(); + let q = p.operate_with_self(BLS12381_PRIME_FIELD_ORDER); + // Minimal Polynomial of Untwist Frobenius Endomorphism: X^2 + tX + q, where X = psh(P) -> psi(p)^2 - t * psi(p) + q * p = 0 + let min_poly = psi_square.operate_with(&tx.neg()).operate_with(&q); + assert!(min_poly.is_neutral_element()) + } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs index 2c1246bcfa..4d57d18cd3 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs @@ -6,7 +6,7 @@ use crate::field::{ quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, }, fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - traits::IsField, + traits::{IsField, IsSubFieldOf}, }; use crate::traits::ByteConversion; use crate::unsigned_integer::element::U384; @@ -71,7 +71,7 @@ impl IsField for Degree2ExtensionField { /// Returns the division of `a` and `b` fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - Self::mul(a, &Self::inv(b).unwrap()) + ::mul(a, &Self::inv(b).unwrap()) } /// Returns a boolean indicating whether `a` and `b` are equal component wise. @@ -103,6 +103,52 @@ impl IsField for Degree2ExtensionField { } } +impl IsSubFieldOf for BLS12381PrimeField { + fn mul( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::mul(a, b[0].value())); + let c1 = FieldElement::from_raw(::mul(a, b[1].value())); + [c0, c1] + } + + fn add( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::add(a, b[0].value())); + let c1 = FieldElement::from_raw(*b[1].value()); + [c0, c1] + } + + fn div( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let b_inv = Degree2ExtensionField::inv(b).unwrap(); + >::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::sub(a, b[0].value())); + let c1 = FieldElement::from_raw(::neg(b[1].value())); + [c0, c1] + } + + fn embed(a: Self::BaseType) -> ::BaseType { + [FieldElement::from_raw(a), FieldElement::zero()] + } + + #[cfg(feature = "std")] + fn to_subfield_vec(b: ::BaseType) -> Vec { + b.into_iter().map(|x| x.to_raw()).collect() + } +} + impl ByteConversion for FieldElement { #[cfg(feature = "std")] fn to_bytes_be(&self) -> Vec { @@ -142,9 +188,7 @@ impl ByteConversion for FieldElement { /////////////// #[derive(Debug, Clone)] pub struct LevelTwoResidue; -impl HasCubicNonResidue for LevelTwoResidue { - type BaseField = Degree2ExtensionField; - +impl HasCubicNonResidue for LevelTwoResidue { fn residue() -> FieldElement { FieldElement::new([ FieldElement::new(U384::from("1")), @@ -153,13 +197,11 @@ impl HasCubicNonResidue for LevelTwoResidue { } } -pub type Degree6ExtensionField = CubicExtensionField; +pub type Degree6ExtensionField = CubicExtensionField; #[derive(Debug, Clone)] pub struct LevelThreeResidue; -impl HasQuadraticNonResidue for LevelThreeResidue { - type BaseField = Degree6ExtensionField; - +impl HasQuadraticNonResidue for LevelThreeResidue { fn residue() -> FieldElement { FieldElement::new([ FieldElement::zero(), @@ -169,7 +211,7 @@ impl HasQuadraticNonResidue for LevelThreeResidue { } } -pub type Degree12ExtensionField = QuadraticExtensionField; +pub type Degree12ExtensionField = QuadraticExtensionField; impl FieldElement { pub fn new_base(a_hex: &str) -> Self { @@ -181,6 +223,11 @@ impl FieldElement { pub fn new_base(a_hex: &str) -> Self { Self::new([FieldElement::new(U384::from(a_hex)), FieldElement::zero()]) } + + pub fn conjugate(&self) -> Self { + let [a, b] = self.value(); + Self::new([a.clone(), -b]) + } } impl FieldElement { @@ -323,4 +370,43 @@ mod tests { assert_eq!(g_to_fp12_x, expectedx); assert_eq!(g_to_fp12_y, expectedy); } + + #[test] + fn add_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + let b = FieldElement::::from(2); + assert_eq!(a + &b, a_extension + b); + } + + #[test] + fn mul_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + let b = FieldElement::::from(2); + assert_eq!(a * &b, a_extension * b); + } + + #[test] + fn sub_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + let b = FieldElement::::from(2); + assert_eq!(a - &b, a_extension - b); + } + + #[test] + fn div_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + let b = FieldElement::::from(2); + assert_eq!(a / &b, a_extension / b); + } + + #[test] + fn embed_base_field_with_degree_2_extension() { + let a = FieldElement::::from(3); + let a_extension = FieldElement::::from(3); + assert_eq!(a.to_extension::(), a_extension); + } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs index 376434bd7d..0e2afa8095 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs @@ -1,18 +1,26 @@ -use super::field_extension::{Degree12ExtensionField, Degree2ExtensionField}; -use crate::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::Degree6ExtensionField, - field::element::FieldElement, unsigned_integer::element::UnsignedInteger, +use super::{ + curve::{BLS12381Curve, MILLER_LOOP_CONSTANT}, + field_extension::{BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField}, + twist::BLS12381TwistCurve, }; - -use super::{curve::BLS12381Curve, twist::BLS12381TwistCurve}; use crate::{ cyclic_group::IsGroup, - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::LevelTwoResidue, - elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint, - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, - elliptic_curve::traits::IsPairing, field::extensions::cubic::HasCubicNonResidue, + elliptic_curve::{ + short_weierstrass::{ + curves::bls12_381::field_extension::{Degree6ExtensionField, LevelTwoResidue}, + point::ShortWeierstrassProjectivePoint, + traits::IsShortWeierstrass, + }, + traits::IsPairing, + }, + errors::PairingError, + field::{element::FieldElement, extensions::cubic::HasCubicNonResidue}, + unsigned_integer::element::{UnsignedInteger, U256}, }; +pub const SUBGROUP_ORDER: U256 = + U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + #[derive(Clone)] pub struct BLS12381AtePairing; impl IsPairing for BLS12381AtePairing { @@ -23,21 +31,22 @@ impl IsPairing for BLS12381AtePairing { /// Compute the product of the ate pairings for a list of point pairs. fn compute_batch( pairs: &[(&Self::G1Point, &Self::G2Point)], - ) -> FieldElement { + ) -> Result, PairingError> { let mut result = FieldElement::one(); for (p, q) in pairs { + if !p.is_in_subgroup() || !q.is_in_subgroup() { + return Err(PairingError::PointNotInSubgroup); + } if !p.is_neutral_element() && !q.is_neutral_element() { let p = p.to_affine(); let q = q.to_affine(); result = result * miller(&q, &p); } } - final_exponentiation(&result) + Ok(final_exponentiation(&result)) } } -/// This is equal to the frobenius trace of the BLS12 381 curve minus one. -const MILLER_LOOP_CONSTANT: u64 = 0xd201000000010000; fn double_accumulate_line( t: &mut ShortWeierstrassProjectivePoint, p: &ShortWeierstrassProjectivePoint, @@ -47,22 +56,23 @@ fn double_accumulate_line( let [px, py, _] = p.coordinates(); let residue = LevelTwoResidue::residue(); let two_inv = FieldElement::::new_base("d0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd556"); + let three = FieldElement::::from(3); let a = &two_inv * x1 * y1; let b = y1.square(); let c = z1.square(); - let d = FieldElement::from(3) * &c; + let d = &three * &c; let e = BLS12381TwistCurve::b() * d; - let f = FieldElement::from(3) * &e; + let f = &three * &e; let g = two_inv * (&b + &f); let h = (y1 + z1).square() - (&b + &c); let x3 = &a * (&b - &f); - let y3 = g.square() - (FieldElement::from(3) * e.square()); + let y3 = g.square() - (&three * e.square()); let z3 = &b * &h; let [h0, h1] = h.value(); - let x1_sq_3 = FieldElement::from(3) * x1.square(); + let x1_sq_3 = three * x1.square(); let [x1_sq_30, x1_sq_31] = x1_sq_3.value(); t.0.value = [x3, y3, z3]; @@ -77,7 +87,7 @@ fn double_accumulate_line( let [a1, a3, a5] = y.value(); let b0 = e - b; let b2 = FieldElement::new([x1_sq_30 * px, x1_sq_31 * px]); - let b3 = FieldElement::new([-h0 * py, -h1 * py]); + let b3 = FieldElement::::new([-h0 * py, -h1 * py]); *accumulator = FieldElement::new([ FieldElement::new([ a0 * &b0 + &residue * (a3 * &b3 + a4 * &b2), // w0 @@ -111,7 +121,7 @@ fn add_accumulate_line( let e = &lambda * &d; let f = z1 * c; let g = x1 * d; - let h = &e + f - FieldElement::from(2) * &g; + let h = &e + f - FieldElement::::from(2) * &g; let i = y1 * &e; let x3 = &lambda * &h; @@ -128,7 +138,7 @@ fn add_accumulate_line( let [a1, a3, a5] = y.value(); let b0 = -lambda.clone() * y2 + theta.clone() * x2; let b2 = FieldElement::new([-theta0 * px, -theta1 * px]); - let b3 = FieldElement::new([lambda0 * py, lambda1 * py]); + let b3 = FieldElement::::new([lambda0 * py, lambda1 * py]); *accumulator = FieldElement::new([ FieldElement::new([ a0 * &b0 + &residue * (a3 * &b3 + a4 * &b2), // w0 @@ -186,7 +196,7 @@ fn frobenius_square( let f0 = FieldElement::new([a0.clone(), a1 * &omega_3, a2 * &omega_3_squared]); let f1 = FieldElement::new([b0.clone(), b1 * omega_3, b2 * omega_3_squared]); - FieldElement::new([f0, f1 * w_raised_to_p_squared_minus_one]) + FieldElement::new([f0, w_raised_to_p_squared_minus_one * f1]) } // To understand more about how to reduce the final exponentiation @@ -255,7 +265,8 @@ mod tests { &p.operate_with_self(a * b).to_affine(), &q.neg().to_affine(), ), - ]); + ]) + .unwrap(); assert_eq!(result, FieldElement::one()); } @@ -263,12 +274,24 @@ mod tests { fn ate_pairing_returns_one_when_one_element_is_the_neutral_element() { let p = BLS12381Curve::generator().to_affine(); let q = ShortWeierstrassProjectivePoint::neutral_element(); - let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]); + let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]).unwrap(); assert_eq!(result, FieldElement::one()); let p = ShortWeierstrassProjectivePoint::neutral_element(); let q = BLS12381TwistCurve::generator(); - let result = BLS12381AtePairing::compute_batch(&[(&p, &q.to_affine())]); + let result = BLS12381AtePairing::compute_batch(&[(&p, &q.to_affine())]).unwrap(); assert_eq!(result, FieldElement::one()); } + + #[test] + fn ate_pairing_errors_when_one_element_is_not_in_subgroup() { + let p = ShortWeierstrassProjectivePoint::new([ + FieldElement::one(), + FieldElement::one(), + FieldElement::one(), + ]); + let q = ShortWeierstrassProjectivePoint::neutral_element(); + let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]); + assert!(result.is_err()) + } } diff --git a/math/src/elliptic_curve/short_weierstrass/curves/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/mod.rs index 9c53071b27..d48704395b 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/mod.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/mod.rs @@ -1,5 +1,7 @@ pub mod bls12_377; pub mod bls12_381; +pub mod pallas; pub mod stark_curve; pub mod test_curve_1; pub mod test_curve_2; +pub mod vesta; diff --git a/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs b/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs new file mode 100644 index 0000000000..d932d7522e --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs @@ -0,0 +1,136 @@ +use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; +use crate::elliptic_curve::traits::IsEllipticCurve; +use crate::field::fields::pallas_field::Pallas255PrimeField; +use crate::{ + elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, +}; + +#[derive(Clone, Debug)] +pub struct PallasCurve; + +impl IsEllipticCurve for PallasCurve { + type BaseField = Pallas255PrimeField; + type PointRepresentation = ShortWeierstrassProjectivePoint; + + fn generator() -> Self::PointRepresentation { + Self::PointRepresentation::new([ + -FieldElement::::one(), + FieldElement::::from(2), + FieldElement::one(), + ]) + } +} + +impl IsShortWeierstrass for PallasCurve { + fn a() -> FieldElement { + FieldElement::from(0) + } + + fn b() -> FieldElement { + FieldElement::from(5) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, + field::element::FieldElement, + }; + + use super::PallasCurve; + + #[allow(clippy::upper_case_acronyms)] + type FE = FieldElement; + + fn point_1() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5", + ); + let y = FE::from_hex_unchecked( + "13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8", + ); + PallasCurve::create_point_from_affine(x, y).unwrap() + } + + fn point_1_times_5() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "17a21304fffd6749d6173d4e0acd9724d98a97453b3491c0e5a53b06cf039b13", + ); + let y = FE::from_hex_unchecked( + "2f9bde429091a1089e52a6cc5dc789e1a58eeded0cf72dccc33b7af685a982d", + ); + PallasCurve::create_point_from_affine(x, y).unwrap() + } + + #[test] + fn adding_five_times_point_1_works() { + let point_1 = point_1(); + let point_1_times_5 = point_1_times_5(); + assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); + } + + #[test] + fn create_valid_point_works() { + let p = point_1(); + assert_eq!( + *p.x(), + FE::from_hex_unchecked( + "bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5" + ) + ); + assert_eq!( + *p.y(), + FE::from_hex_unchecked( + "13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8" + ) + ); + assert_eq!(*p.z(), FE::from_hex_unchecked("1")); + } + + #[test] + fn create_invalid_points_returns_an_error() { + assert_eq!( + PallasCurve::create_point_from_affine(FE::from(0), FE::from(1)), + Err(EllipticCurveError::InvalidPoint) + ); + } + + #[test] + fn equality_works() { + let g = PallasCurve::generator(); + let g2 = g.operate_with_self(2_u16); + let g2_other = g.operate_with(&g); + assert_ne!(&g2, &g); + assert_eq!(&g, &g); + assert_eq!(&g2, &g2_other); + } + + #[test] + fn g_operated_with_g_satifies_ec_equation() { + let g = PallasCurve::generator(); + let g2 = g.operate_with_self(2_u16); + + // get x and y from affine coordinates + let g2_affine = g2.to_affine(); + let x = g2_affine.x(); + let y = g2_affine.y(); + + // calculate both sides of Pallas curve equation + let five = PallasCurve::b(); + let y_sq_0 = x.pow(3_u16) + five; + let y_sq_1 = y.pow(2_u16); + + assert_eq!(y_sq_0, y_sq_1); + } + + #[test] + fn operate_with_self_works_1() { + let g = PallasCurve::generator(); + assert_eq!( + g.operate_with(&g).operate_with(&g), + g.operate_with_self(3_u16) + ); + } +} diff --git a/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs new file mode 100644 index 0000000000..201a862ce5 --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs @@ -0,0 +1 @@ +pub mod curve; diff --git a/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs b/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs index 030339f74b..c8601ce23c 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs @@ -19,15 +19,15 @@ pub const TEST_CURVE_1_PRIME_FIELD_ORDER: u64 = 59; /// Order of the subgroup of the curve. pub const TEST_CURVE_1_MAIN_SUBGROUP_ORDER: u64 = 5; +pub type TestCurvePrimeField = U64PrimeField; + /// In F59 the element -1 is not a square. We use this property /// to construct a Quadratic Field Extension out of it by adding /// its square root. #[derive(Debug, Clone)] pub struct TestCurveQuadraticNonResidue; -impl HasQuadraticNonResidue for TestCurveQuadraticNonResidue { - type BaseField = U64PrimeField; - - fn residue() -> FieldElement> { +impl HasQuadraticNonResidue for TestCurveQuadraticNonResidue { + fn residue() -> FieldElement { -FieldElement::one() } } @@ -37,7 +37,7 @@ impl HasQuadraticNonResidue for TestCurveQuadraticNonResidue { pub struct TestCurve1; impl IsEllipticCurve for TestCurve1 { - type BaseField = QuadraticExtensionField; + type BaseField = QuadraticExtensionField; type PointRepresentation = ShortWeierstrassProjectivePoint; fn generator() -> Self::PointRepresentation { diff --git a/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs b/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs index 961e9e5129..ca71c4a9d4 100644 --- a/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs +++ b/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs @@ -34,9 +34,7 @@ type TestCurve2PrimeField = MontgomeryBackendPrimeField; /// its square root. #[derive(Debug, Clone)] pub struct TestCurve2QuadraticNonResidue; -impl HasQuadraticNonResidue for TestCurve2QuadraticNonResidue { - type BaseField = TestCurve2PrimeField; - +impl HasQuadraticNonResidue for TestCurve2QuadraticNonResidue { fn residue() -> FieldElement { -FieldElement::one() } @@ -47,7 +45,7 @@ impl HasQuadraticNonResidue for TestCurve2QuadraticNonResidue { pub struct TestCurve2; impl IsEllipticCurve for TestCurve2 { - type BaseField = QuadraticExtensionField; + type BaseField = QuadraticExtensionField; type PointRepresentation = ShortWeierstrassProjectivePoint; fn generator() -> Self::PointRepresentation { diff --git a/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs b/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs new file mode 100644 index 0000000000..ac871a3d6f --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs @@ -0,0 +1,136 @@ +use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; +use crate::elliptic_curve::traits::IsEllipticCurve; +use crate::field::fields::vesta_field::Vesta255PrimeField; +use crate::{ + elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, +}; + +#[derive(Clone, Debug)] +pub struct VestaCurve; + +impl IsEllipticCurve for VestaCurve { + type BaseField = Vesta255PrimeField; + type PointRepresentation = ShortWeierstrassProjectivePoint; + + fn generator() -> Self::PointRepresentation { + Self::PointRepresentation::new([ + -FieldElement::::one(), + FieldElement::::from(2), + FieldElement::one(), + ]) + } +} + +impl IsShortWeierstrass for VestaCurve { + fn a() -> FieldElement { + FieldElement::from(0) + } + + fn b() -> FieldElement { + FieldElement::from(5) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, + field::element::FieldElement, + }; + + use super::VestaCurve; + + #[allow(clippy::upper_case_acronyms)] + type FE = FieldElement; + + fn point_1() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "c4e6a8789457a64e1638783181963d4c4399a5a8cdb30af4038664ce431033c", + ); + let y = FE::from_hex_unchecked( + "2d8c9125be9a3ac50371e462f63dfc3fbbf645e9a93d6b7da71c13d3065e3ce5", + ); + VestaCurve::create_point_from_affine(x, y).unwrap() + } + + fn point_1_times_5() -> ShortWeierstrassProjectivePoint { + let x = FE::from_hex_unchecked( + "1266f29f1478410eaa62fb1ab064f7d9259f515600544165972a89c9941c72c3", + ); + let y = FE::from_hex_unchecked( + "3a893b592bd487cd25c5d4237b02987e1b78206e70989f3209e24a40b89499fd", + ); + VestaCurve::create_point_from_affine(x, y).unwrap() + } + + #[test] + fn adding_five_times_point_1_works() { + let point_1 = point_1(); + let point_1_times_5 = point_1_times_5(); + assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); + } + + #[test] + fn create_valid_point_works() { + let p = point_1(); + assert_eq!( + *p.x(), + FE::from_hex_unchecked( + "c4e6a8789457a64e1638783181963d4c4399a5a8cdb30af4038664ce431033c" + ) + ); + assert_eq!( + *p.y(), + FE::from_hex_unchecked( + "2d8c9125be9a3ac50371e462f63dfc3fbbf645e9a93d6b7da71c13d3065e3ce5" + ) + ); + assert_eq!(*p.z(), FE::from_hex_unchecked("1")); + } + + #[test] + fn create_invalid_points_returns_an_error() { + assert_eq!( + VestaCurve::create_point_from_affine(FE::from(0), FE::from(1)), + Err(EllipticCurveError::InvalidPoint) + ); + } + + #[test] + fn equality_works() { + let g = VestaCurve::generator(); + let g2 = g.operate_with_self(2_u16); + let g2_other = g.operate_with(&g); + assert_ne!(&g2, &g); + assert_eq!(&g, &g); + assert_eq!(&g2, &g2_other); + } + + #[test] + fn g_operated_with_g_satifies_ec_equation() { + let g = VestaCurve::generator(); + let g2 = g.operate_with_self(2_u16); + + // get x and y from affine coordinates + let g2_affine = g2.to_affine(); + let x = g2_affine.x(); + let y = g2_affine.y(); + + // calculate both sides of Pallas curve equation + let five = VestaCurve::b(); + let y_sq_0 = x.pow(3_u16) + five; + let y_sq_1 = y.pow(2_u16); + + assert_eq!(y_sq_0, y_sq_1); + } + + #[test] + fn operate_with_self_works_1() { + let g = VestaCurve::generator(); + assert_eq!( + g.operate_with(&g).operate_with(&g), + g.operate_with_self(3_u16) + ); + } +} diff --git a/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs b/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs new file mode 100644 index 0000000000..201a862ce5 --- /dev/null +++ b/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs @@ -0,0 +1 @@ +pub mod curve; diff --git a/math/src/elliptic_curve/short_weierstrass/point.rs b/math/src/elliptic_curve/short_weierstrass/point.rs index 13acb3ca78..d4adf0f922 100644 --- a/math/src/elliptic_curve/short_weierstrass/point.rs +++ b/math/src/elliptic_curve/short_weierstrass/point.rs @@ -50,7 +50,7 @@ impl ShortWeierstrassProjectivePoint { Self(self.0.to_affine()) } - fn double(&self) -> Self { + pub fn double(&self) -> Self { let [px, py, pz] = self.coordinates(); let px_square = px * px; @@ -212,8 +212,7 @@ impl IsGroup for ShortWeierstrassProjectivePoint { #[derive(PartialEq)] pub enum PointFormat { Projective, - // TO DO: - // Uncompressed, + Uncompressed, // Compressed, } @@ -233,7 +232,7 @@ where { /// Serialize the points in the given format #[cfg(feature = "std")] - pub fn serialize(&self, _point_format: PointFormat, endianness: Endianness) -> Vec { + pub fn serialize(&self, point_format: PointFormat, endianness: Endianness) -> Vec { // TODO: Add more compact serialization formats // Uncompressed affine / Compressed @@ -242,59 +241,101 @@ where let y_bytes: Vec; let z_bytes: Vec; - let [x, y, z] = self.coordinates(); - if endianness == Endianness::BigEndian { - x_bytes = x.to_bytes_be(); - y_bytes = y.to_bytes_be(); - z_bytes = z.to_bytes_be(); - } else { - x_bytes = x.to_bytes_le(); - y_bytes = y.to_bytes_le(); - z_bytes = z.to_bytes_le(); + match point_format { + PointFormat::Projective => { + let [x, y, z] = self.coordinates(); + if endianness == Endianness::BigEndian { + x_bytes = x.to_bytes_be(); + y_bytes = y.to_bytes_be(); + z_bytes = z.to_bytes_be(); + } else { + x_bytes = x.to_bytes_le(); + y_bytes = y.to_bytes_le(); + z_bytes = z.to_bytes_le(); + } + bytes.extend(&x_bytes); + bytes.extend(&y_bytes); + bytes.extend(&z_bytes); + } + PointFormat::Uncompressed => { + let affine_representation = self.to_affine(); + let [x, y, _z] = affine_representation.coordinates(); + if endianness == Endianness::BigEndian { + x_bytes = x.to_bytes_be(); + y_bytes = y.to_bytes_be(); + } else { + x_bytes = x.to_bytes_le(); + y_bytes = y.to_bytes_le(); + } + bytes.extend(&x_bytes); + bytes.extend(&y_bytes); + } } - - bytes.extend(&x_bytes); - bytes.extend(&y_bytes); - bytes.extend(&z_bytes); - bytes } pub fn deserialize( bytes: &[u8], - _point_format: PointFormat, + point_format: PointFormat, endianness: Endianness, ) -> Result { - if bytes.len() % 3 != 0 { - return Err(DeserializationError::InvalidAmountOfBytes); - } + match point_format { + PointFormat::Projective => { + if bytes.len() % 3 != 0 { + return Err(DeserializationError::InvalidAmountOfBytes); + } - let len = bytes.len() / 3; - let x: FieldElement; - let y: FieldElement; - let z: FieldElement; + let len = bytes.len() / 3; + let x: FieldElement; + let y: FieldElement; + let z: FieldElement; - if endianness == Endianness::BigEndian { - x = ByteConversion::from_bytes_be(&bytes[..len])?; - y = ByteConversion::from_bytes_be(&bytes[len..len * 2])?; - z = ByteConversion::from_bytes_be(&bytes[len * 2..])?; - } else { - x = ByteConversion::from_bytes_le(&bytes[..len])?; - y = ByteConversion::from_bytes_le(&bytes[len..len * 2])?; - z = ByteConversion::from_bytes_le(&bytes[len * 2..])?; - } + if endianness == Endianness::BigEndian { + x = ByteConversion::from_bytes_be(&bytes[..len])?; + y = ByteConversion::from_bytes_be(&bytes[len..len * 2])?; + z = ByteConversion::from_bytes_be(&bytes[len * 2..])?; + } else { + x = ByteConversion::from_bytes_le(&bytes[..len])?; + y = ByteConversion::from_bytes_le(&bytes[len..len * 2])?; + z = ByteConversion::from_bytes_le(&bytes[len * 2..])?; + } - if z == FieldElement::zero() { - let point = Self::new([x, y, z]); - if point.is_neutral_element() { - Ok(point) - } else { - Err(DeserializationError::FieldFromBytesError) + if z == FieldElement::zero() { + let point = Self::new([x, y, z]); + if point.is_neutral_element() { + Ok(point) + } else { + Err(DeserializationError::FieldFromBytesError) + } + } else if E::defining_equation(&(&x / &z), &(&y / &z)) == FieldElement::zero() { + Ok(Self::new([x, y, z])) + } else { + Err(DeserializationError::FieldFromBytesError) + } + } + PointFormat::Uncompressed => { + if bytes.len() % 2 != 0 { + return Err(DeserializationError::InvalidAmountOfBytes); + } + + let len = bytes.len() / 2; + let x: FieldElement; + let y: FieldElement; + + if endianness == Endianness::BigEndian { + x = ByteConversion::from_bytes_be(&bytes[..len])?; + y = ByteConversion::from_bytes_be(&bytes[len..])?; + } else { + x = ByteConversion::from_bytes_le(&bytes[..len])?; + y = ByteConversion::from_bytes_le(&bytes[len..])?; + } + + if E::defining_equation(&x, &y) == FieldElement::zero() { + Ok(Self::new([x, y, FieldElement::one()])) + } else { + Err(DeserializationError::FieldFromBytesError) + } } - } else if E::defining_equation(&(&x / &z), &(&y / &z)) == FieldElement::zero() { - Ok(Self::new([x, y, z])) - } else { - Err(DeserializationError::FieldFromBytesError) } } } @@ -347,7 +388,7 @@ mod tests { #[cfg(feature = "std")] #[test] - fn byte_conversion_from_and_to_be() { + fn byte_conversion_from_and_to_be_projective() { let expected_point = point(); let bytes_be = expected_point.serialize(PointFormat::Projective, Endianness::BigEndian); @@ -361,7 +402,20 @@ mod tests { #[cfg(feature = "std")] #[test] - fn byte_conversion_from_and_to_le() { + fn byte_conversion_from_and_to_be_uncompressed() { + let expected_point = point(); + let bytes_be = expected_point.serialize(PointFormat::Uncompressed, Endianness::BigEndian); + let result = ShortWeierstrassProjectivePoint::deserialize( + &bytes_be, + PointFormat::Uncompressed, + Endianness::BigEndian, + ); + assert_eq!(expected_point, result.unwrap()); + } + + #[cfg(feature = "std")] + #[test] + fn byte_conversion_from_and_to_le_projective() { let expected_point = point(); let bytes_be = expected_point.serialize(PointFormat::Projective, Endianness::LittleEndian); @@ -375,7 +429,22 @@ mod tests { #[cfg(feature = "std")] #[test] - fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work() { + fn byte_conversion_from_and_to_le_uncompressed() { + let expected_point = point(); + let bytes_be = + expected_point.serialize(PointFormat::Uncompressed, Endianness::LittleEndian); + + let result = ShortWeierstrassProjectivePoint::deserialize( + &bytes_be, + PointFormat::Uncompressed, + Endianness::LittleEndian, + ); + assert_eq!(expected_point, result.unwrap()); + } + + #[cfg(feature = "std")] + #[test] + fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work_projective() { let bytes = point().serialize(PointFormat::Projective, Endianness::LittleEndian); let result = ShortWeierstrassProjectivePoint::::deserialize( @@ -392,7 +461,24 @@ mod tests { #[cfg(feature = "std")] #[test] - fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work() { + fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work_uncompressed() { + let bytes = point().serialize(PointFormat::Uncompressed, Endianness::LittleEndian); + + let result = ShortWeierstrassProjectivePoint::::deserialize( + &bytes, + PointFormat::Uncompressed, + Endianness::BigEndian, + ); + + assert_eq!( + result.unwrap_err(), + DeserializationError::FieldFromBytesError + ); + } + + #[cfg(feature = "std")] + #[test] + fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work_projective() { let bytes = point().serialize(PointFormat::Projective, Endianness::BigEndian); let result = ShortWeierstrassProjectivePoint::::deserialize( @@ -407,8 +493,25 @@ mod tests { ); } + #[cfg(feature = "std")] + #[test] + fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work_uncompressed() { + let bytes = point().serialize(PointFormat::Uncompressed, Endianness::BigEndian); + + let result = ShortWeierstrassProjectivePoint::::deserialize( + &bytes, + PointFormat::Uncompressed, + Endianness::LittleEndian, + ); + + assert_eq!( + result.unwrap_err(), + DeserializationError::FieldFromBytesError + ); + } + #[test] - fn cannot_create_point_from_wrong_number_of_bytes_le() { + fn cannot_create_point_from_wrong_number_of_bytes_le_projective() { let bytes = &[0_u8; 13]; let result = ShortWeierstrassProjectivePoint::::deserialize( @@ -424,7 +527,23 @@ mod tests { } #[test] - fn cannot_create_point_from_wrong_number_of_bytes_be() { + fn cannot_create_point_from_wrong_number_of_bytes_le_uncompressed() { + let bytes = &[0_u8; 13]; + + let result = ShortWeierstrassProjectivePoint::::deserialize( + bytes, + PointFormat::Uncompressed, + Endianness::LittleEndian, + ); + + assert_eq!( + result.unwrap_err(), + DeserializationError::InvalidAmountOfBytes + ); + } + + #[test] + fn cannot_create_point_from_wrong_number_of_bytes_be_projective() { let bytes = &[0_u8; 13]; let result = ShortWeierstrassProjectivePoint::::deserialize( @@ -438,4 +557,20 @@ mod tests { DeserializationError::InvalidAmountOfBytes ); } + + #[test] + fn cannot_create_point_from_wrong_number_of_bytes_be_uncompressed() { + let bytes = &[0_u8; 13]; + + let result = ShortWeierstrassProjectivePoint::::deserialize( + bytes, + PointFormat::Uncompressed, + Endianness::BigEndian, + ); + + assert_eq!( + result.unwrap_err(), + DeserializationError::InvalidAmountOfBytes + ); + } } diff --git a/math/src/elliptic_curve/traits.rs b/math/src/elliptic_curve/traits.rs index 675c2ff491..1e3355b48c 100644 --- a/math/src/elliptic_curve/traits.rs +++ b/math/src/elliptic_curve/traits.rs @@ -1,5 +1,6 @@ use crate::{ cyclic_group::IsGroup, + errors::PairingError, field::{element::FieldElement, traits::IsField}, }; use core::fmt::Debug; @@ -41,11 +42,15 @@ pub trait IsPairing { type OutputField: IsField; /// Compute the product of the pairings for a list of point pairs. - fn compute_batch(pairs: &[(&Self::G1Point, &Self::G2Point)]) - -> FieldElement; + fn compute_batch( + pairs: &[(&Self::G1Point, &Self::G2Point)], + ) -> Result, PairingError>; /// Compute the ate pairing between point `p` in G1 and `q` in G2. - fn compute(p: &Self::G1Point, q: &Self::G2Point) -> FieldElement { + fn compute( + p: &Self::G1Point, + q: &Self::G2Point, + ) -> Result, PairingError> { Self::compute_batch(&[(p, q)]) } } diff --git a/math/src/errors.rs b/math/src/errors.rs index 4a33326305..d0ceb22ecb 100644 --- a/math/src/errors.rs +++ b/math/src/errors.rs @@ -22,6 +22,11 @@ pub enum DeserializationError { InvalidValue, } +#[derive(Debug, PartialEq, Eq)] +pub enum PairingError { + PointNotInSubgroup, +} + impl From for DeserializationError { fn from(error: ByteConversionError) -> Self { match error { diff --git a/math/src/fft/cpu/fft.rs b/math/src/fft/cpu/fft.rs index 194e596dda..e2910f8cd6 100644 --- a/math/src/fft/cpu/fft.rs +++ b/math/src/fft/cpu/fft.rs @@ -1,4 +1,7 @@ -use crate::field::{element::FieldElement, traits::IsFFTField}; +use crate::field::{ + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, +}; /// In-Place Radix-2 NR DIT FFT algorithm over a slice of two-adic field elements. /// It's required that the twiddle factors are in bit-reverse order. Else this function will not @@ -12,9 +15,12 @@ use crate::field::{element::FieldElement, traits::IsFFTField}; /// - NR: natural to reverse order, meaning that the input is naturally ordered and the output will /// be bit-reversed ordered. /// - DIT: decimation in time -pub fn in_place_nr_2radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) +/// +/// It supports values in a field E and domain in a subfield F. +pub fn in_place_nr_2radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { // divide input in groups, starting with 1, duplicating the number of groups in each stage. let mut group_count = 1; @@ -60,6 +66,8 @@ where /// - RN: reverse to natural order, meaning that the input is bit-reversed ordered and the output will /// be naturally ordered. /// - DIT: decimation in time +/// +/// It supports values in a field E and domain in a subfield F. #[allow(dead_code)] pub fn in_place_rn_2radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) where @@ -96,6 +104,79 @@ where } } +/// In-Place Radix-4 NR DIT FFT algorithm over a slice of two-adic field elements. +/// It's required that the twiddle factors are in bit-reverse order. Else this function will not +/// return fourier transformed values. +/// Also the input size needs to be a power of two. +/// It's recommended to use the current safe abstractions instead of this function. +/// +/// Performs a fast fourier transform with the next attributes: +/// - In-Place: an auxiliary vector of data isn't needed for the algorithm. +/// - Radix-4: the algorithm halves the problem size log(n) times. +/// - NR: natural to reverse order, meaning that the input is naturally ordered and the output will +/// be bit-reversed ordered. +/// - DIT: decimation in time +pub fn in_place_nr_4radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) +where + F: IsFFTField + IsSubFieldOf, + E: IsField, +{ + debug_assert!(input.len().is_power_of_two()); + debug_assert!(input.len().ilog2() % 2 == 0); // Even power of 2 => x is power of 4 + + // divide input in groups, starting with 1, duplicating the number of groups in each stage. + let mut group_count = 1; + let mut group_size = input.len(); + + // for each group, there'll be group_size / 4 butterflies. + // a butterfly is the atomic operation of a FFT, e.g: + // x' = x + yw2 + zw1 + tw1w2 + // y' = x - yw2 + zw1 - tw1w2 + // z' = x + yw3 - zw1 - tw1w3 + // t' = x - yw3 - zw1 + tw1w3 + // The 0.25 factor is what gives FFT its performance, it recursively divides the problem size + // by 4 (group size). + + while group_count < input.len() { + #[allow(clippy::needless_range_loop)] // the suggestion would obfuscate a bit the algorithm + for group in 0..group_count { + let first_in_group = group * group_size; + let first_in_next_group = first_in_group + group_size / 4; + + let (w1, w2, w3) = ( + &twiddles[group], + &twiddles[2 * group], + &twiddles[2 * group + 1], + ); + + for i in first_in_group..first_in_next_group { + let (j, k, l) = ( + i + group_size / 4, + i + group_size / 2, + i + 3 * group_size / 4, + ); + + let zw1 = w1 * &input[k]; + let tw1 = w1 * &input[l]; + let a = w2 * (&input[j] + &tw1); + let b = w3 * (&input[j] - &tw1); + + let x = &input[i] + &zw1 + &a; + let y = &input[i] + &zw1 - &a; + let z = &input[i] - &zw1 + &b; + let t = &input[i] - &zw1 - &b; + + input[i] = x; + input[j] = y; + input[k] = z; + input[l] = t; + } + } + group_count *= 4; + group_size /= 4; + } +} + #[cfg(test)] mod tests { use crate::fft::cpu::bit_reversing::in_place_bit_reverse_permute; @@ -120,7 +201,12 @@ mod tests { } } prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec { + fn field_vec(max_exp: u8)(vec in (1..max_exp).prop_flat_map(|i| collection::vec(field_element(), 1 << i))) -> Vec { + vec + } + } + prop_compose! { + fn field_vec_r4(max_exp: u8)(vec in (1..max_exp).prop_flat_map(|i| collection::vec(field_element(), 1 << (2 * i)))) -> Vec { vec } } @@ -135,7 +221,7 @@ mod tests { let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); let mut result = coeffs; - in_place_nr_2radix_fft(&mut result, &twiddles); + in_place_nr_2radix_fft::(&mut result, &twiddles); in_place_bit_reverse_permute(&mut result); prop_assert_eq!(expected, result); @@ -155,5 +241,20 @@ mod tests { prop_assert_eq!(result, expected); } + + // Property-based test that ensures NR Radix-2 FFT gives the same result as a naive DFT. + #[test] + fn test_nr_4radix_fft_matches_naive_eval(coeffs in field_vec_r4(5)) { + let expected = naive_matrix_dft_test(&coeffs); + + let order = coeffs.len().trailing_zeros(); + let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); + + let mut result = coeffs; + in_place_nr_4radix_fft::(&mut result, &twiddles); + in_place_bit_reverse_permute(&mut result); + + prop_assert_eq!(expected, result); + } } } diff --git a/math/src/fft/cpu/ops.rs b/math/src/fft/cpu/ops.rs index da9876f98d..f4bdffd7a3 100644 --- a/math/src/fft/cpu/ops.rs +++ b/math/src/fft/cpu/ops.rs @@ -1,16 +1,19 @@ use crate::{ fft::errors::FFTError, - field::{element::FieldElement, traits::IsFFTField}, + field::{ + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, + }, }; use super::{bit_reversing::in_place_bit_reverse_permute, fft::in_place_nr_2radix_fft}; -/// Executes Fast Fourier Transform over elements of a two-adic finite field `F`. Usually used for -/// fast polynomial evaluation. -pub fn fft( - input: &[FieldElement], +/// Executes Fast Fourier Transform over elements of a two-adic finite field `E` and domain in a +/// subfield `F`. Usually used for fast polynomial evaluation. +pub fn fft, E: IsField>( + input: &[FieldElement], twiddles: &[FieldElement], -) -> Result>, FFTError> { +) -> Result>, FFTError> { if !input.len().is_power_of_two() { return Err(FFTError::InputError(input.len())); } diff --git a/math/src/fft/cpu/roots_of_unity.rs b/math/src/fft/cpu/roots_of_unity.rs index 6632cc6d16..43456860e0 100644 --- a/math/src/fft/cpu/roots_of_unity.rs +++ b/math/src/fft/cpu/roots_of_unity.rs @@ -54,7 +54,7 @@ pub fn get_powers_of_primitive_root_coset( offset: &FieldElement, ) -> Result>, FFTError> { let root = F::get_primitive_root_of_unity(n)?; - let results = (0..count).map(|i| root.pow(i) * offset); + let results = (0..count).map(|i| offset * root.pow(i)); Ok(results.collect()) } diff --git a/math/src/fft/gpu/metal/ops.rs b/math/src/fft/gpu/metal/ops.rs index c9d7131f25..c22fc86200 100644 --- a/math/src/fft/gpu/metal/ops.rs +++ b/math/src/fft/gpu/metal/ops.rs @@ -1,6 +1,6 @@ use crate::field::{ element::FieldElement, - traits::{IsFFTField, RootsConfig}, + traits::{IsFFTField, IsField, IsSubFieldOf, RootsConfig}, }; use lambdaworks_gpu::metal::abstractions::{errors::MetalError, state::*}; @@ -15,11 +15,17 @@ use core::mem; /// in this order too. Natural order means that input[i] corresponds to the i-th coefficient, /// as opposed to bit-reverse order in which input[bit_rev(i)] corresponds to the i-th /// coefficient. -pub fn fft( - input: &[FieldElement], +/// +/// It supports values in a field E and domain in a subfield F. +pub fn fft( + input: &[FieldElement], twiddles: &[FieldElement], state: &MetalState, -) -> Result>, MetalError> { +) -> Result>, MetalError> +where + F: IsFFTField + IsSubFieldOf, + E: IsField, +{ // TODO: make a twiddle factor abstraction for handling invalid twiddles if !input.len().is_power_of_two() { return Err(MetalError::InputError(input.len())); @@ -55,7 +61,7 @@ pub fn fft( let result = MetalState::retrieve_contents(&input_buffer); let result = bitrev_permutation::(&result, state)?; - Ok(result.iter().map(FieldElement::from_raw).collect()) + Ok(result.into_iter().map(FieldElement::from_raw).collect()) } /// Generates 2^{`order-1`} twiddle factors in parallel, with a certain `config`, in Metal. @@ -89,7 +95,7 @@ pub fn gen_twiddles( let (command_buffer, command_encoder) = state.setup_command(&pipeline, Some(&[(0, &result_buffer)])); - let root = F::get_primitive_root_of_unity::(order).unwrap(); + let root = F::get_primitive_root_of_unity(order).unwrap(); command_encoder.set_bytes(1, mem::size_of::() as u64, void_ptr(&root)); let grid_size = MTLSize::new(len as u64, 1, 1); @@ -103,7 +109,7 @@ pub fn gen_twiddles( }); let result = MetalState::retrieve_contents(&result_buffer); - Ok(result.iter().map(FieldElement::from_raw).collect()) + Ok(result.into_iter().map(FieldElement::from_raw).collect()) } /// Executes a parallel bit-reverse permutation with the elements of `input`, in Metal. @@ -173,7 +179,7 @@ mod tests { fn test_metal_fft_matches_sequential(input in field_vec(6)) { let metal_state = MetalState::new(None).unwrap(); let order = input.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); + let twiddles = get_twiddles::(order.into(), RootsConfig::BitReverse).unwrap(); let metal_result = super::fft(&input, &twiddles, &metal_state).unwrap(); let sequential_result = crate::fft::cpu::ops::fft(&input, &twiddles).unwrap(); @@ -190,7 +196,7 @@ mod tests { let metal_state = MetalState::new(None).unwrap(); let order = input.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); + let twiddles = get_twiddles::(order.into(), RootsConfig::BitReverse).unwrap(); let metal_result = super::fft(&input, &twiddles, &metal_state).unwrap(); let sequential_result = crate::fft::cpu::ops::fft(&input, &twiddles).unwrap(); diff --git a/math/src/fft/gpu/metal/polynomial.rs b/math/src/fft/gpu/metal/polynomial.rs index 4e1714fe41..f46e3e29ac 100644 --- a/math/src/fft/gpu/metal/polynomial.rs +++ b/math/src/fft/gpu/metal/polynomial.rs @@ -1,7 +1,7 @@ use crate::{ field::{ element::FieldElement, - traits::{IsFFTField, RootsConfig}, + traits::{IsFFTField, IsField, IsSubFieldOf, RootsConfig}, }, polynomial::Polynomial, }; @@ -9,30 +9,34 @@ use lambdaworks_gpu::metal::abstractions::{errors::MetalError, state::MetalState use super::ops::*; -pub fn evaluate_fft_metal(coeffs: &[FieldElement]) -> Result>, MetalError> +pub fn evaluate_fft_metal( + coeffs: &[FieldElement], +) -> Result>, MetalError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let state = MetalState::new(None)?; let order = coeffs.len().trailing_zeros(); - let twiddles = gen_twiddles(order.into(), RootsConfig::BitReverse, &state)?; + let twiddles = gen_twiddles::(order.into(), RootsConfig::BitReverse, &state)?; fft(coeffs, &twiddles, &state) } /// Returns a new polynomial that interpolates `fft_evals`, which are evaluations using twiddle /// factors. This is considered to be the inverse operation of [evaluate_fft_metal()]. -pub fn interpolate_fft_metal( - fft_evals: &[FieldElement], -) -> Result>, MetalError> +pub fn interpolate_fft_metal( + fft_evals: &[FieldElement], +) -> Result>, MetalError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let metal_state = MetalState::new(None)?; let order = fft_evals.len().trailing_zeros(); - let twiddles = gen_twiddles(order.into(), RootsConfig::BitReverseInversed, &metal_state)?; + let twiddles = gen_twiddles::(order.into(), RootsConfig::BitReverseInversed, &metal_state)?; let coeffs = fft(fft_evals, &twiddles, &metal_state)?; diff --git a/math/src/fft/polynomial.rs b/math/src/fft/polynomial.rs index eaaf40b97f..481e326925 100644 --- a/math/src/fft/polynomial.rs +++ b/math/src/fft/polynomial.rs @@ -1,5 +1,6 @@ use crate::fft::errors::FFTError; +use crate::field::traits::{IsField, IsSubFieldOf}; use crate::{ field::{ element::FieldElement, @@ -15,58 +16,37 @@ use crate::fft::gpu::metal::polynomial::{evaluate_fft_metal, interpolate_fft_met use super::cpu::{ops, roots_of_unity}; -pub trait FFTPoly { - fn evaluate_fft( - &self, - blowup_factor: usize, - domain_size: Option, - ) -> Result>, FFTError>; - fn evaluate_offset_fft( - &self, - blowup_factor: usize, - domain_size: Option, - offset: &FieldElement, - ) -> Result>, FFTError>; - fn interpolate_fft( - fft_evals: &[FieldElement], - ) -> Result>, FFTError>; - fn interpolate_offset_fft( - fft_evals: &[FieldElement], - offset: &FieldElement, - ) -> Result>, FFTError>; -} - -impl FFTPoly for Polynomial> { - /// Returns `N` evaluations of this polynomial using FFT (so the results +impl Polynomial> { + /// Returns `N` evaluations of this polynomial using FFT over a domain in a subfield F of E (so the results /// are P(w^i), with w being a primitive root of unity). /// `N = max(self.coeff_len(), domain_size).next_power_of_two() * blowup_factor`. /// If `domain_size` is `None`, it defaults to 0. - fn evaluate_fft( - &self, + pub fn evaluate_fft>( + poly: &Polynomial>, blowup_factor: usize, domain_size: Option, - ) -> Result>, FFTError> { + ) -> Result>, FFTError> { let domain_size = domain_size.unwrap_or(0); - let len = std::cmp::max(self.coeff_len(), domain_size).next_power_of_two() * blowup_factor; + let len = std::cmp::max(poly.coeff_len(), domain_size).next_power_of_two() * blowup_factor; - if self.coefficients().is_empty() { + if poly.coefficients().is_empty() { return Ok(vec![FieldElement::zero(); len]); } - let mut coeffs = self.coefficients().to_vec(); + let mut coeffs = poly.coefficients().to_vec(); coeffs.resize(len, FieldElement::zero()); // padding with zeros will make FFT return more evaluations of the same polynomial. #[cfg(feature = "metal")] { if !F::field_name().is_empty() { - Ok(evaluate_fft_metal(&coeffs)?) + Ok(evaluate_fft_metal::(&coeffs)?) } else { println!( "GPU evaluation failed for field {}. Program will fallback to CPU.", std::any::type_name::() ); - evaluate_fft_cpu(&coeffs) + evaluate_fft_cpu::(&coeffs) } } @@ -76,44 +56,46 @@ impl FFTPoly for Polynomial> { if F::field_name() == "stark256" { Ok(evaluate_fft_cuda(&coeffs)?) } else { - evaluate_fft_cpu(&coeffs) + evaluate_fft_cpu::(&coeffs) } } #[cfg(all(not(feature = "metal"), not(feature = "cuda")))] { - evaluate_fft_cpu(&coeffs) + evaluate_fft_cpu::(&coeffs) } } - /// Returns `N` evaluations with an offset of this polynomial using FFT + /// Returns `N` evaluations with an offset of this polynomial using FFT over a domain in a subfield F of E /// (so the results are P(w^i), with w being a primitive root of unity). /// `N = max(self.coeff_len(), domain_size).next_power_of_two() * blowup_factor`. /// If `domain_size` is `None`, it defaults to 0. - fn evaluate_offset_fft( - &self, + pub fn evaluate_offset_fft>( + poly: &Polynomial>, blowup_factor: usize, domain_size: Option, offset: &FieldElement, - ) -> Result>, FFTError> { - let scaled = self.scale(offset); - scaled.evaluate_fft(blowup_factor, domain_size) + ) -> Result>, FFTError> { + let scaled = poly.scale(offset); + Polynomial::evaluate_fft::(&scaled, blowup_factor, domain_size) } /// Returns a new polynomial that interpolates `(w^i, fft_evals[i])`, with `w` being a - /// Nth primitive root of unity, and `i in 0..N`, with `N = fft_evals.len()`. + /// Nth primitive root of unity in a subfield F of E, and `i in 0..N`, with `N = fft_evals.len()`. /// This is considered to be the inverse operation of [Self::evaluate_fft()]. - fn interpolate_fft(fft_evals: &[FieldElement]) -> Result { + pub fn interpolate_fft>( + fft_evals: &[FieldElement], + ) -> Result { #[cfg(feature = "metal")] { if !F::field_name().is_empty() { - Ok(interpolate_fft_metal(fft_evals)?) + Ok(interpolate_fft_metal::(fft_evals)?) } else { println!( "GPU interpolation failed for field {}. Program will fallback to CPU.", std::any::type_name::() ); - interpolate_fft_cpu(fft_evals) + interpolate_fft_cpu::(fft_evals) } } @@ -122,63 +104,67 @@ impl FFTPoly for Polynomial> { if !F::field_name().is_empty() { Ok(interpolate_fft_cuda(fft_evals)?) } else { - interpolate_fft_cpu(fft_evals) + interpolate_fft_cpu::(fft_evals) } } #[cfg(all(not(feature = "metal"), not(feature = "cuda")))] { - interpolate_fft_cpu(fft_evals) + interpolate_fft_cpu::(fft_evals) } } /// Returns a new polynomial that interpolates offset `(w^i, fft_evals[i])`, with `w` being a - /// Nth primitive root of unity, and `i in 0..N`, with `N = fft_evals.len()`. + /// Nth primitive root of unity in a subfield F of E, and `i in 0..N`, with `N = fft_evals.len()`. /// This is considered to be the inverse operation of [Self::evaluate_offset_fft()]. - fn interpolate_offset_fft( - fft_evals: &[FieldElement], + pub fn interpolate_offset_fft>( + fft_evals: &[FieldElement], offset: &FieldElement, - ) -> Result>, FFTError> { - let scaled = Polynomial::interpolate_fft(fft_evals)?; + ) -> Result>, FFTError> { + let scaled = Polynomial::interpolate_fft::(fft_evals)?; Ok(scaled.scale(&offset.inv().unwrap())) } } -pub fn compose_fft( - poly_1: &Polynomial>, - poly_2: &Polynomial>, -) -> Polynomial> +pub fn compose_fft( + poly_1: &Polynomial>, + poly_2: &Polynomial>, +) -> Polynomial> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { - let poly_2_evaluations = poly_2.evaluate_fft(1, None).unwrap(); + let poly_2_evaluations = Polynomial::evaluate_fft::(poly_2, 1, None).unwrap(); let values: Vec<_> = poly_2_evaluations .iter() .map(|value| poly_1.evaluate(value)) .collect(); - Polynomial::interpolate_fft(values.as_slice()).unwrap() + Polynomial::interpolate_fft::(values.as_slice()).unwrap() } -pub fn evaluate_fft_cpu(coeffs: &[FieldElement]) -> Result>, FFTError> +pub fn evaluate_fft_cpu(coeffs: &[FieldElement]) -> Result>, FFTError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let order = coeffs.len().trailing_zeros(); - let twiddles = roots_of_unity::get_twiddles(order.into(), RootsConfig::BitReverse)?; + let twiddles = roots_of_unity::get_twiddles::(order.into(), RootsConfig::BitReverse)?; // Bit reverse order is needed for NR DIT FFT. ops::fft(coeffs, &twiddles) } -pub fn interpolate_fft_cpu( - fft_evals: &[FieldElement], -) -> Result>, FFTError> +pub fn interpolate_fft_cpu( + fft_evals: &[FieldElement], +) -> Result>, FFTError> where - F: IsFFTField, + F: IsFFTField + IsSubFieldOf, + E: IsField, { let order = fft_evals.len().trailing_zeros(); - let twiddles = roots_of_unity::get_twiddles(order.into(), RootsConfig::BitReverseInversed)?; + let twiddles = + roots_of_unity::get_twiddles::(order.into(), RootsConfig::BitReverseInversed)?; let coeffs = ops::fft(fft_evals, &twiddles)?; @@ -191,7 +177,10 @@ mod tests { #[cfg(all(not(feature = "metal"), not(feature = "cuda")))] use crate::field::traits::IsField; - use crate::field::traits::RootsConfig; + use crate::field::{ + test_fields::u64_test_field::{U64TestField, U64TestFieldExtension}, + traits::RootsConfig, + }; use proptest::{collection, prelude::*}; use roots_of_unity::{get_powers_of_primitive_root, get_powers_of_primitive_root_coset}; @@ -206,7 +195,7 @@ mod tests { let twiddles = get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); - let fft_eval = poly.evaluate_fft(1, None).unwrap(); + let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); let naive_eval = poly.evaluate_slice(&twiddles); (fft_eval, naive_eval) @@ -222,9 +211,8 @@ mod tests { let twiddles = get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset).unwrap(); - let fft_eval = poly - .evaluate_offset_fft(blowup_factor, None, &offset) - .unwrap(); + let fft_eval = + Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); let naive_eval = poly.evaluate_slice(&twiddles); (fft_eval, naive_eval) @@ -238,7 +226,7 @@ mod tests { get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_fft(fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); (fft_poly, naive_poly) } @@ -259,8 +247,8 @@ mod tests { fn gen_fft_interpolate_and_evaluate( poly: Polynomial>, ) -> (Polynomial>, Polynomial>) { - let eval = poly.evaluate_fft(1, None).unwrap(); - let new_poly = Polynomial::interpolate_fft(&eval).unwrap(); + let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); (poly, new_poly) } @@ -357,7 +345,7 @@ mod tests { let p = Polynomial::new(&[FE::new(0), FE::new(2)]); let q = Polynomial::new(&[FE::new(0), FE::new(0), FE::new(0), FE::new(1)]); assert_eq!( - compose_fft(&p, &q), + compose_fft::(&p, &q), Polynomial::new(&[FE::new(0), FE::new(0), FE::new(0), FE::new(2)]) ); } @@ -447,4 +435,21 @@ mod tests { } } } + + #[test] + fn test_fft_with_values_in_field_extension_over_domain_in_prime_field() { + type TF = U64TestField; + type TL = U64TestFieldExtension; + + let a = FieldElement::::from(&[FieldElement::one(), FieldElement::one()]); + let b = FieldElement::::from(&[-FieldElement::from(2), FieldElement::from(17)]); + let c = FieldElement::::one(); + let poly = Polynomial::new(&[a, b, c]); + + let eval = Polynomial::evaluate_offset_fft::(&poly, 8, Some(4), &FieldElement::from(2)) + .unwrap(); + let new_poly = + Polynomial::interpolate_offset_fft::(&eval, &FieldElement::from(2)).unwrap(); + assert_eq!(poly, new_poly); + } } diff --git a/math/src/fft/test_helpers.rs b/math/src/fft/test_helpers.rs index d45c74e86e..0c177a1da1 100644 --- a/math/src/fft/test_helpers.rs +++ b/math/src/fft/test_helpers.rs @@ -12,7 +12,8 @@ pub fn naive_matrix_dft_test(input: &[FieldElement]) -> Vec(order.into(), n, RootsConfig::Natural).unwrap(); let mut output = Vec::with_capacity(n); for row in 0..n { diff --git a/math/src/field/element.rs b/math/src/field/element.rs index 3c1f864843..4007195070 100644 --- a/math/src/field/element.rs +++ b/math/src/field/element.rs @@ -32,7 +32,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; use serde::Deserialize; use super::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}; -use super::traits::{IsPrimeField, LegendreSymbol}; +use super::traits::{IsPrimeField, IsSubFieldOf, LegendreSymbol}; /// A field element with operations algorithms defined in `F` #[allow(clippy::derived_hash_with_manual_eq)] @@ -63,6 +63,17 @@ impl FieldElement { numbers[0] = bi_inv; Ok(()) } + + #[inline(always)] + pub fn to_subfield_vec(self) -> Vec> + where + S: IsSubFieldOf, + { + S::to_subfield_vec(self.value) + .into_iter() + .map(|x| FieldElement::from_raw(x)) + .collect() + } } /// From overloading for field elements @@ -95,10 +106,8 @@ where F::BaseType: Clone, F: IsField, { - pub fn from_raw(value: &F::BaseType) -> Self { - Self { - value: value.clone(), - } + pub fn from_raw(value: F::BaseType) -> Self { + Self { value } } pub const fn const_from_raw(value: F::BaseType) -> Self { @@ -119,59 +128,64 @@ where impl Eq for FieldElement where F: IsField {} /// Addition operator overloading for field elements -impl Add<&FieldElement> for &FieldElement +impl Add<&FieldElement> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn add(self, rhs: &FieldElement) -> Self::Output { + fn add(self, rhs: &FieldElement) -> Self::Output { Self::Output { - value: F::add(&self.value, &rhs.value), + value: >::add(&self.value, &rhs.value), } } } -impl Add> for FieldElement +impl Add> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn add(self, rhs: FieldElement) -> Self::Output { + fn add(self, rhs: FieldElement) -> Self::Output { &self + &rhs } } -impl Add<&FieldElement> for FieldElement +impl Add<&FieldElement> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn add(self, rhs: &FieldElement) -> Self::Output { + fn add(self, rhs: &FieldElement) -> Self::Output { &self + rhs } } -impl Add> for &FieldElement +impl Add> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn add(self, rhs: FieldElement) -> Self::Output { + fn add(self, rhs: FieldElement) -> Self::Output { self + &rhs } } /// AddAssign operator overloading for field elements -impl AddAssign> for FieldElement +impl AddAssign> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { fn add_assign(&mut self, rhs: FieldElement) { - self.value = F::add(&self.value, &rhs.value); + self.value = >::add(&rhs.value, &self.value); } } @@ -186,142 +200,154 @@ where } /// Subtraction operator overloading for field elements*/ -impl Sub<&FieldElement> for &FieldElement +impl Sub<&FieldElement> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn sub(self, rhs: &FieldElement) -> Self::Output { + fn sub(self, rhs: &FieldElement) -> Self::Output { Self::Output { - value: F::sub(&self.value, &rhs.value), + value: >::sub(&self.value, &rhs.value), } } } -impl Sub> for FieldElement +impl Sub> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn sub(self, rhs: FieldElement) -> Self::Output { + fn sub(self, rhs: FieldElement) -> Self::Output { &self - &rhs } } -impl Sub<&FieldElement> for FieldElement +impl Sub<&FieldElement> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn sub(self, rhs: &FieldElement) -> Self::Output { + fn sub(self, rhs: &FieldElement) -> Self::Output { &self - rhs } } -impl Sub> for &FieldElement +impl Sub> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn sub(self, rhs: FieldElement) -> Self::Output { + fn sub(self, rhs: FieldElement) -> Self::Output { self - &rhs } } /// Multiplication operator overloading for field elements*/ -impl Mul<&FieldElement> for &FieldElement +impl Mul<&FieldElement> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn mul(self, rhs: &FieldElement) -> Self::Output { + fn mul(self, rhs: &FieldElement) -> Self::Output { Self::Output { - value: F::mul(&self.value, &rhs.value), + value: >::mul(&self.value, &rhs.value), } } } -impl Mul> for FieldElement +impl Mul> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn mul(self, rhs: FieldElement) -> Self::Output { + fn mul(self, rhs: FieldElement) -> Self::Output { &self * &rhs } } -impl Mul<&FieldElement> for FieldElement +impl Mul<&FieldElement> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn mul(self, rhs: &FieldElement) -> Self::Output { + fn mul(self, rhs: &FieldElement) -> Self::Output { &self * rhs } } -impl Mul> for &FieldElement +impl Mul> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn mul(self, rhs: FieldElement) -> Self::Output { + fn mul(self, rhs: FieldElement) -> Self::Output { self * &rhs } } /// Division operator overloading for field elements*/ -impl Div<&FieldElement> for &FieldElement +impl Div<&FieldElement> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn div(self, rhs: &FieldElement) -> Self::Output { + fn div(self, rhs: &FieldElement) -> Self::Output { Self::Output { - value: F::div(&self.value, &rhs.value), + value: >::div(&self.value, &rhs.value), } } } -impl Div> for FieldElement +impl Div> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn div(self, rhs: FieldElement) -> Self::Output { + fn div(self, rhs: FieldElement) -> Self::Output { &self / &rhs } } -impl Div<&FieldElement> for FieldElement +impl Div<&FieldElement> for FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn div(self, rhs: &FieldElement) -> Self::Output { + fn div(self, rhs: &FieldElement) -> Self::Output { &self / rhs } } -impl Div> for &FieldElement +impl Div> for &FieldElement where - F: IsField, + F: IsSubFieldOf, + L: IsField, { - type Output = FieldElement; + type Output = FieldElement; - fn div(self, rhs: FieldElement) -> Self::Output { + fn div(self, rhs: FieldElement) -> Self::Output { self / &rhs } } @@ -418,6 +444,21 @@ where pub fn zero() -> Self { Self { value: F::zero() } } + + /// Returns the raw base type + pub fn to_raw(self) -> F::BaseType { + self.value + } + + #[inline(always)] + pub fn to_extension(self) -> FieldElement + where + F: IsSubFieldOf, + { + FieldElement { + value: >::embed(self.value), + } + } } impl FieldElement { @@ -447,10 +488,20 @@ impl FieldElement { value: F::from_hex(hex_string)?, }) } + + #[cfg(feature = "std")] + /// Creates a hexstring from a `FieldElement` without `0x`. + pub fn to_hex(&self) -> String { + F::to_hex(&self.value) + } } #[cfg(feature = "lambdaworks-serde-binary")] -impl Serialize for FieldElement { +impl Serialize for FieldElement +where + F: IsField, + F::BaseType: ByteConversion, +{ fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -478,7 +529,11 @@ impl Serialize for FieldElement { } #[cfg(feature = "lambdaworks-serde-binary")] -impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { +impl<'de, F> Deserialize<'de> for FieldElement +where + F: IsField, + F::BaseType: ByteConversion, +{ fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -491,7 +546,7 @@ impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { struct FieldElementVisitor(PhantomData F>); - impl<'de, F: IsPrimeField> Visitor<'de> for FieldElementVisitor { + impl<'de, F: IsField> Visitor<'de> for FieldElementVisitor { type Value = FieldElement; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -515,7 +570,7 @@ impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { } let value = value.ok_or_else(|| de::Error::missing_field("value"))?; let val = F::BaseType::from_bytes_be(&value).unwrap(); - Ok(FieldElement::from_raw(&val)) + Ok(FieldElement::from_raw(val)) } fn visit_seq(self, mut seq: S) -> Result, S::Error> @@ -531,7 +586,7 @@ impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { } let value = value.ok_or_else(|| de::Error::missing_field("value"))?; let val = F::BaseType::from_bytes_be(&value).unwrap(); - Ok(FieldElement::from_raw(&val)) + Ok(FieldElement::from_raw(val)) } } diff --git a/math/src/field/extensions/cubic.rs b/math/src/field/extensions/cubic.rs index ac57d7dfe7..7443b48a78 100644 --- a/math/src/field/extensions/cubic.rs +++ b/math/src/field/extensions/cubic.rs @@ -1,6 +1,6 @@ use crate::field::element::FieldElement; use crate::field::errors::FieldError; -use crate::field::traits::IsField; +use crate::field::traits::{IsField, IsSubFieldOf}; #[cfg(feature = "lambdaworks-serde-binary")] use crate::traits::ByteConversion; use core::fmt::Debug; @@ -9,21 +9,20 @@ use core::marker::PhantomData; /// A general cubic extension field over `F` /// with cubic non residue `Q::residue()` #[derive(Debug, Clone, PartialEq, Eq)] -pub struct CubicExtensionField { - phantom: PhantomData, +pub struct CubicExtensionField { + field: PhantomData, + non_residue: PhantomData, } -pub type CubicExtensionFieldElement = FieldElement>; +pub type CubicExtensionFieldElement = FieldElement>; /// Trait to fix a cubic non residue. /// Used to construct a cubic extension field by adding /// a square root of `residue()`. -pub trait HasCubicNonResidue { - type BaseField: IsField; - +pub trait HasCubicNonResidue { /// This function must return an element that is not a cube in Fp, /// that is, a cubic non-residue. - fn residue() -> FieldElement; + fn residue() -> FieldElement; } #[cfg(feature = "lambdaworks-serde-binary")] @@ -56,17 +55,15 @@ where } } -impl IsField for CubicExtensionField +impl IsField for CubicExtensionField where - Q: Clone + Debug + HasCubicNonResidue, + F: IsField, + Q: Clone + Debug + HasCubicNonResidue, { - type BaseType = [FieldElement; 3]; + type BaseType = [FieldElement; 3]; /// Returns the component wise addition of `a` and `b` - fn add( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> [FieldElement; 3] { + fn add(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { [&a[0] + &b[0], &a[1] + &b[1], &a[2] + &b[2]] } @@ -74,10 +71,7 @@ where /// equation: /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Q::residue() + (a0 * b1 + a1 * b0) * t /// where `t.pow(2)` equals `Q::residue()`. - fn mul( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> [FieldElement; 3] { + fn mul(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { let v0 = &a[0] * &b[0]; let v1 = &a[1] * &b[1]; let v2 = &a[2] * &b[2]; @@ -90,24 +84,18 @@ where } /// Returns the component wise subtraction of `a` and `b` - fn sub( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> [FieldElement; 3] { + fn sub(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { [&a[0] - &b[0], &a[1] - &b[1], &a[2] - &b[2]] } /// Returns the component wise negation of `a` - fn neg(a: &[FieldElement; 3]) -> [FieldElement; 3] { + fn neg(a: &[FieldElement; 3]) -> [FieldElement; 3] { [-&a[0], -&a[1], -&a[2]] } /// Returns the multiplicative inverse of `a` - fn inv( - a: &[FieldElement; 3], - ) -> Result<[FieldElement; 3], FieldError> { - let three = FieldElement::from(3_u64); - + fn inv(a: &[FieldElement; 3]) -> Result<[FieldElement; 3], FieldError> { + let three = FieldElement::::from(3_u64); let d = a[0].pow(3_u64) + a[1].pow(3_u64) * Q::residue() + a[2].pow(3_u64) * Q::residue().pow(2_u64) @@ -121,20 +109,17 @@ where } /// Returns the division of `a` and `b` - fn div( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> [FieldElement; 3] { - Self::mul(a, &Self::inv(b).unwrap()) + fn div(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { + ::mul(a, &Self::inv(b).unwrap()) } /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> bool { + fn eq(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> bool { a[0] == b[0] && a[1] == b[1] && a[2] == b[2] } /// Returns the additive neutral element of the field extension. - fn zero() -> [FieldElement; 3] { + fn zero() -> [FieldElement; 3] { [ FieldElement::zero(), FieldElement::zero(), @@ -143,7 +128,7 @@ where } /// Returns the multiplicative neutral element of the field extension. - fn one() -> [FieldElement; 3] { + fn one() -> [FieldElement; 3] { [ FieldElement::one(), FieldElement::zero(), @@ -164,11 +149,66 @@ where /// of that element in the field. /// Note: for this case this is simply the identity, because the components /// already have correct representations. - fn from_base_type(x: [FieldElement; 3]) -> [FieldElement; 3] { + fn from_base_type(x: [FieldElement; 3]) -> [FieldElement; 3] { x } } +impl IsSubFieldOf> for F +where + F: IsField, + Q: Clone + Debug + HasCubicNonResidue, +{ + fn mul( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::mul(a, b[0].value())); + let c1 = FieldElement::from_raw(F::mul(a, b[1].value())); + let c2 = FieldElement::from_raw(F::mul(a, b[2].value())); + [c0, c1, c2] + } + + fn add( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::add(a, b[0].value())); + [c0, b[1].clone(), b[2].clone()] + } + + fn div( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let b_inv = as IsField>::inv(b).unwrap(); + >>::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::sub(a, b[0].value())); + let c1 = FieldElement::from_raw(F::neg(b[1].value())); + let c2 = FieldElement::from_raw(F::neg(b[2].value())); + [c0, c1, c2] + } + + fn embed(a: Self::BaseType) -> as IsField>::BaseType { + [ + FieldElement::from_raw(a), + FieldElement::zero(), + FieldElement::zero(), + ] + } + + #[cfg(feature = "std")] + fn to_subfield_vec(b: as IsField>::BaseType) -> Vec { + b.into_iter().map(|x| x.to_raw()).collect() + } +} + #[cfg(test)] mod tests { use crate::field::fields::u64_prime_field::{U64FieldElement, U64PrimeField}; @@ -179,16 +219,14 @@ mod tests { #[derive(Debug, Clone)] struct MyCubicNonResidue; - impl HasCubicNonResidue for MyCubicNonResidue { - type BaseField = U64PrimeField; - + impl HasCubicNonResidue> for MyCubicNonResidue { fn residue() -> FieldElement> { -FieldElement::from(11) } } type FE = U64FieldElement; - type MyFieldExtensionBackend = CubicExtensionField; + type MyFieldExtensionBackend = CubicExtensionField, MyCubicNonResidue>; #[allow(clippy::upper_case_acronyms)] type FEE = FieldElement; @@ -285,4 +323,68 @@ mod tests { let expected_result = FEE::new([FE::new(8), FE::new(3), FE::new(5)]); assert_eq!(a.inv().unwrap(), expected_result); } + + #[test] + fn test_add_as_subfield_1() { + let a = FE::new(5); + let b = FEE::new([-FE::new(2), FE::new(8), FE::new(10)]); + let expected_result = FEE::new([FE::new(3), FE::new(8), FE::new(10)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_add_as_subfield_2() { + let a = FE::new(12); + let b = FEE::new([-FE::new(4), FE::new(2), FE::new(8)]); + let expected_result = FEE::new([FE::new(8), FE::new(2), FE::new(8)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_sub_as_subfield_1() { + let a = FE::new(3); + let b = FEE::new([-FE::new(2), FE::new(8), FE::new(2)]); + let expected_result = FEE::new([FE::new(5), FE::new(5), FE::new(11)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_sub_as_subfield_2() { + let a = FE::new(12); + let b = FEE::new([-FE::new(4), FE::new(2), FE::new(3)]); + let expected_result = FEE::new([FE::new(3), FE::new(11), FE::new(10)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_mul_as_subfield_1() { + let a = FE::new(5); + let b = FEE::new([-FE::new(2), FE::new(8), FE::new(6)]); + let expected_result = FEE::new([FE::new(3), FE::new(1), FE::new(4)]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_mul_as_subfield_2() { + let a = FE::new(11); + let b = FEE::new([-FE::new(4), FE::new(2), FE::new(15)]); + let expected_result = FEE::new([FE::new(8), FE::new(9), FE::new(9)]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_div_as_subfield_1() { + let a = FE::new(2); + let b = FEE::new([-FE::new(2), FE::new(8), FE::new(5)]); + let expected_result = FEE::new([FE::new(8), FE::new(4), FE::new(10)]); + assert_eq!(a / b, expected_result); + } + + #[test] + fn test_div_as_subfield_2() { + let a = FE::new(4); + let b = FEE::new([-FE::new(4), FE::new(2), FE::new(2)]); + let expected_result = FEE::new([FE::new(3), FE::new(6), FE::new(11)]); + assert_eq!(a / b, expected_result); + } } diff --git a/math/src/field/extensions/quadratic.rs b/math/src/field/extensions/quadratic.rs index 3cb09cd0d6..6cd3fd46fc 100644 --- a/math/src/field/extensions/quadratic.rs +++ b/math/src/field/extensions/quadratic.rs @@ -1,6 +1,6 @@ use crate::field::element::FieldElement; use crate::field::errors::FieldError; -use crate::field::traits::IsField; +use crate::field::traits::{IsField, IsSubFieldOf}; #[cfg(feature = "lambdaworks-serde-binary")] use crate::traits::ByteConversion; use core::fmt::Debug; @@ -9,24 +9,28 @@ use core::marker::PhantomData; /// A general quadratic extension field over `F` /// with quadratic non residue `Q::residue()` #[derive(Debug, Clone, PartialEq, Eq)] -pub struct QuadraticExtensionField { - phantom: PhantomData, +pub struct QuadraticExtensionField +where + F: IsField, + T: HasQuadraticNonResidue, +{ + field: PhantomData, + non_residue: PhantomData, } -pub type QuadraticExtensionFieldElement = FieldElement>; +pub type QuadraticExtensionFieldElement = FieldElement>; /// Trait to fix a quadratic non residue. /// Used to construct a quadratic extension field by adding /// a square root of `residue()`. -pub trait HasQuadraticNonResidue { - type BaseField: IsField; - - fn residue() -> FieldElement; +pub trait HasQuadraticNonResidue { + fn residue() -> FieldElement; } -impl FieldElement> +impl FieldElement> where - Q: Clone + Debug + HasQuadraticNonResidue, + F: IsField, + Q: Clone + Debug + HasQuadraticNonResidue, { pub fn conjugate(&self) -> Self { let [a, b] = self.value(); @@ -64,17 +68,15 @@ where } } -impl IsField for QuadraticExtensionField +impl IsField for QuadraticExtensionField where - Q: Clone + Debug + HasQuadraticNonResidue, + F: IsField, + Q: Clone + Debug + HasQuadraticNonResidue, { - type BaseType = [FieldElement; 2]; + type BaseType = [FieldElement; 2]; /// Returns the component wise addition of `a` and `b` - fn add( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> [FieldElement; 2] { + fn add(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { [&a[0] + &b[0], &a[1] + &b[1]] } @@ -82,10 +84,7 @@ where /// equation: /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Q::residue() + (a0 * b1 + a1 * b0) * t /// where `t.pow(2)` equals `Q::residue()`. - fn mul( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> [FieldElement; 2] { + fn mul(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { let q = Q::residue(); let a0b0 = &a[0] * &b[0]; let a1b1 = &a[1] * &b[1]; @@ -93,7 +92,7 @@ where [&a0b0 + &a1b1 * q, z - a0b0 - a1b1] } - fn square(a: &[FieldElement; 2]) -> [FieldElement; 2] { + fn square(a: &[FieldElement; 2]) -> [FieldElement; 2] { let [a0, a1] = a; let v0 = a0 * a1; let c0 = (a0 + a1) * (a0 + Q::residue() * a1) - &v0 - Q::residue() * &v0; @@ -102,47 +101,39 @@ where } /// Returns the component wise subtraction of `a` and `b` - fn sub( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> [FieldElement; 2] { + fn sub(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { [&a[0] - &b[0], &a[1] - &b[1]] } /// Returns the component wise negation of `a` - fn neg(a: &[FieldElement; 2]) -> [FieldElement; 2] { + fn neg(a: &[FieldElement; 2]) -> [FieldElement; 2] { [-&a[0], -&a[1]] } /// Returns the multiplicative inverse of `a` /// This uses the equality `(a0 + a1 * t) * (a0 - a1 * t) = a0.pow(2) - a1.pow(2) * Q::residue()` - fn inv( - a: &[FieldElement; 2], - ) -> Result<[FieldElement; 2], FieldError> { + fn inv(a: &[FieldElement; 2]) -> Result<[FieldElement; 2], FieldError> { let inv_norm = (a[0].pow(2_u64) - Q::residue() * a[1].pow(2_u64)).inv()?; Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) } /// Returns the division of `a` and `b` - fn div( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> [FieldElement; 2] { - Self::mul(a, &Self::inv(b).unwrap()) + fn div(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { + ::mul(a, &Self::inv(b).unwrap()) } /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> bool { + fn eq(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> bool { a[0] == b[0] && a[1] == b[1] } /// Returns the additive neutral element of the field extension. - fn zero() -> [FieldElement; 2] { + fn zero() -> [FieldElement; 2] { [FieldElement::zero(), FieldElement::zero()] } /// Returns the multiplicative neutral element of the field extension. - fn one() -> [FieldElement; 2] { + fn one() -> [FieldElement; 2] { [FieldElement::one(), FieldElement::zero()] } @@ -155,12 +146,66 @@ where /// of that element in the field. /// Note: for this case this is simply the identity, because the components /// already have correct representations. - fn from_base_type(x: [FieldElement; 2]) -> [FieldElement; 2] { + fn from_base_type(x: [FieldElement; 2]) -> [FieldElement; 2] { x } } -impl FieldElement> {} +impl IsSubFieldOf> for F +where + F: IsField, + Q: Clone + Debug + HasQuadraticNonResidue, +{ + fn mul( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::mul(a, b[0].value())); + let c1 = FieldElement::from_raw(F::mul(a, b[1].value())); + [c0, c1] + } + + fn add( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::add(a, b[0].value())); + [c0, b[1].clone()] + } + + fn div( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let b_inv = as IsField>::inv(b).unwrap(); + >>::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: & as IsField>::BaseType, + ) -> as IsField>::BaseType { + let c0 = FieldElement::from_raw(F::sub(a, b[0].value())); + let c1 = FieldElement::from_raw(F::neg(b[1].value())); + [c0, c1] + } + + fn embed(a: Self::BaseType) -> as IsField>::BaseType { + [FieldElement::from_raw(a), FieldElement::zero()] + } + + #[cfg(feature = "std")] + fn to_subfield_vec( + b: as IsField>::BaseType, + ) -> Vec { + b.into_iter().map(|x| x.to_raw()).collect() + } +} + +impl> + FieldElement> +{ +} #[cfg(test)] mod tests { @@ -172,16 +217,15 @@ mod tests { #[derive(Debug, Clone)] struct MyQuadraticNonResidue; - impl HasQuadraticNonResidue for MyQuadraticNonResidue { - type BaseField = U64PrimeField; - + impl HasQuadraticNonResidue> for MyQuadraticNonResidue { fn residue() -> FieldElement> { -FieldElement::one() } } type FE = U64FieldElement; - type MyFieldExtensionBackend = QuadraticExtensionField; + type MyFieldExtensionBackend = + QuadraticExtensionField, MyQuadraticNonResidue>; #[allow(clippy::upper_case_acronyms)] type FEE = FieldElement; @@ -285,4 +329,68 @@ mod tests { let expected_result = FEE::new([FE::new(12), -FE::new(5)]); assert_eq!(a.conjugate(), expected_result); } + + #[test] + fn test_add_as_subfield_1() { + let a = -FE::new(2); + let b = FEE::new([FE::new(0), FE::new(3)]); + let expected_result = FEE::new([FE::new(57), FE::new(3)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_add_as_subfield_2() { + let a = -FE::new(4); + let b = FEE::new([FE::new(12), FE::new(5)]); + let expected_result = FEE::new([FE::new(8), FE::new(5)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_sub_as_subfield_1() { + let a = FE::new(0); + let b = FEE::new([-FE::new(2), FE::new(8)]); + let expected_result = FEE::new([FE::new(2), FE::new(51)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_sub_a_subfield_2() { + let a = FE::new(12); + let b = FEE::new([-FE::new(4), -FE::new(2)]); + let expected_result = FEE::new([FE::new(16), FE::new(2)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_mul_as_subfield_1() { + let a = FE::new(2); + let b = FEE::new([-FE::new(2), FE::new(8)]); + let expected_result = FEE::new([FE::new(55), FE::new(16)]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_mul_as_subfield_2() { + let a = FE::new(12); + let b = FEE::new([-FE::new(4), FE::new(2)]); + let expected_result = FEE::new([FE::new(11), FE::new(24)]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_div_as_subfield_1() { + let a = FE::new(3); + let b = FEE::new([-FE::new(2), FE::new(8)]); + let expected_result = FEE::new([FE::new(19), FE::new(17)]); + assert_eq!(a / b, expected_result); + } + + #[test] + fn test_div_as_subfield_2() { + let a = FE::new(22); + let b = FEE::new([FE::new(4), FE::new(2)]); + let expected_result = FEE::new([FE::new(28), FE::new(45)]); + assert_eq!(a / b, expected_result); + } } diff --git a/math/src/field/fields/fft_friendly/babybear.rs b/math/src/field/fields/fft_friendly/babybear.rs index 68da03f91a..a516e85232 100644 --- a/math/src/field/fields/fft_friendly/babybear.rs +++ b/math/src/field/fields/fft_friendly/babybear.rs @@ -2,8 +2,9 @@ use crate::{ field::{ element::FieldElement, fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + traits::IsFFTField, }, - unsigned_integer::element::U64, + unsigned_integer::element::{UnsignedInteger, U64}, }; pub type U64MontgomeryBackendPrimeField = MontgomeryBackendPrimeField; @@ -18,6 +19,22 @@ impl IsModulus for MontgomeryConfigBabybear31PrimeField { pub type Babybear31PrimeField = U64MontgomeryBackendPrimeField; +//a two-adic primitive root of unity is 21^(2^24) +// 21^(2^24)=1 mod 2013265921 +// 2^27(2^4-1)+1 where n=27 (two-adicity) and k=2^4+1 + +//In the future we should allow this with metal and cuda feature, and just dispatch it to the CPU until the implementation is done +#[cfg(any(not(feature = "metal"), not(feature = "cuda")))] +impl IsFFTField for Babybear31PrimeField { + const TWO_ADICITY: u64 = 24; + + const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = UnsignedInteger { limbs: [21] }; + + fn field_name() -> &'static str { + "babybear31" + } +} + impl FieldElement { pub fn to_bytes_le(&self) -> [u8; 8] { let limbs = self.representative().limbs; @@ -31,59 +48,218 @@ impl FieldElement { } #[cfg(test)] -mod test_babybear_31_bytes_ops { - use super::Babybear31PrimeField; - use crate::{field::element::FieldElement, traits::ByteConversion}; - - #[test] - #[cfg(feature = "std")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - ", - ); - let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } +mod tests { + use super::*; - #[test] - #[cfg(feature = "std")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - ", - ); - let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } + mod test_babybear_31_bytes_ops { + use super::*; + use crate::{field::element::FieldElement, traits::ByteConversion}; - #[test] - - fn byte_serialization_and_deserialization_works_le() { - let element = FieldElement::::from_hex_unchecked( - "\ - 7654321076543210\ - ", - ); - let bytes = element.to_bytes_le(); - let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); - assert_eq!(element, from_bytes); + #[test] + #[cfg(feature = "std")] + fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { + let element = + FieldElement::::from_hex_unchecked("0123456701234567"); + let bytes = element.to_bytes_le(); + let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); + assert_eq!(bytes, expected_bytes); + } + + #[test] + #[cfg(feature = "std")] + fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { + let element = + FieldElement::::from_hex_unchecked("0123456701234567"); + let bytes = element.to_bytes_be(); + let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); + assert_eq!(bytes, expected_bytes); + } + + #[test] + fn byte_serialization_and_deserialization_works_le() { + let element = + FieldElement::::from_hex_unchecked("7654321076543210"); + let bytes = element.to_bytes_le(); + let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); + assert_eq!(element, from_bytes); + } + + #[test] + fn byte_serialization_and_deserialization_works_be() { + let element = + FieldElement::::from_hex_unchecked("7654321076543210"); + let bytes = element.to_bytes_be(); + let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); + assert_eq!(element, from_bytes); + } } - #[test] - - fn byte_serialization_and_deserialization_works_be() { - let element = FieldElement::::from_hex_unchecked( - "\ - 7654321076543210\ - ", - ); - let bytes = element.to_bytes_be(); - let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); - assert_eq!(element, from_bytes); + #[cfg(all(feature = "std", not(feature = "instruments")))] + mod test_babybear_31_fft { + use super::*; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::fft::cpu::roots_of_unity::{ + get_powers_of_primitive_root, get_powers_of_primitive_root_coset, + }; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::field::element::FieldElement; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::field::traits::{IsFFTField, RootsConfig}; + use crate::polynomial::Polynomial; + use proptest::{collection, prelude::*, std_facade::Vec}; + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_evaluation( + poly: Polynomial>, + ) -> (Vec>, Vec>) { + let len = poly.coeff_len().next_power_of_two(); + let order = len.trailing_zeros(); + let twiddles = + get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); + + let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + (fft_eval, naive_eval) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_coset_and_naive_evaluation( + poly: Polynomial>, + offset: FieldElement, + blowup_factor: usize, + ) -> (Vec>, Vec>) { + let len = poly.coeff_len().next_power_of_two(); + let order = (len * blowup_factor).trailing_zeros(); + let twiddles = + get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) + .unwrap(); + + let fft_eval = + Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + (fft_eval, naive_eval) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_interpolate( + fft_evals: &[FieldElement], + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = + get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); + + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); + + (fft_poly, naive_poly) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_coset_interpolate( + fft_evals: &[FieldElement], + offset: &FieldElement, + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); + + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); + + (fft_poly, naive_poly) + } + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_interpolate_and_evaluate( + poly: Polynomial>, + ) -> (Polynomial>, Polynomial>) { + let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); + + (poly, new_poly) + } + + prop_compose! { + fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } + // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. + // also it can't exceed the test field's two-adicity. + } + prop_compose! { + fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FieldElement { + FieldElement::::from(num) + } + } + prop_compose! { + fn offset()(num in any::(), factor in any::()) -> FieldElement { FieldElement::::from(num).pow(factor) } + } + prop_compose! { + fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec> { + vec + } + } + prop_compose! { + fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec> { + vec + } + } + prop_compose! { + fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial> { + Polynomial::new(&coeffs) + } + } + prop_compose! { + fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial> { + Polynomial::new(&coeffs) + } + } + + proptest! { + // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_matches_naive_evaluation(poly in poly(8)) { + let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); + prop_assert_eq!(fft_eval, naive_eval); + } + + // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { + let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); + prop_assert_eq!(fft_eval, naive_eval); + } + + // #[cfg(not(any(feature = "metal"),not(feature = "cuda")))] + // Property-based test that ensures FFT interpolation is the same as naive.. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) + .prop_filter("Avoid polynomials of size not power of two", + |evals| evals.len().is_power_of_two())) { + let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); + prop_assert_eq!(fft_poly, naive_poly); + } + + // Property-based test that ensures FFT interpolation with an offset is the same as naive. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) + .prop_filter("Avoid polynomials of size not power of two", + |evals| evals.len().is_power_of_two())) { + let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); + prop_assert_eq!(fft_poly, naive_poly); + } + + // Property-based test that ensures interpolation is the inverse operation of evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_is_inverse_of_evaluate( + poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { + let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); + prop_assert_eq!(poly, new_poly); + } + } } } diff --git a/math/src/field/fields/fft_friendly/mod.rs b/math/src/field/fields/fft_friendly/mod.rs index 92b6a52868..a9b2f0f2eb 100644 --- a/math/src/field/fields/fft_friendly/mod.rs +++ b/math/src/field/fields/fft_friendly/mod.rs @@ -1,6 +1,8 @@ /// Implemenation of the Babybear Prime Field p = 2^31 - 2^27 + 1 pub mod babybear; -/// Implementation of two-adic Prime Field over 256 bit unsigned integers. +/// Implemenation of the quadratic extension of the babybear field +pub mod quadratic_babybear; +/// Implementation of two-adic prime field over 256 bit unsigned integers. pub mod stark_252_prime_field; /// Implemenation of the Goldilocks Prime Field p = 2^64 - 2^32 + 1 pub mod u64_goldilocks; diff --git a/math/src/field/fields/fft_friendly/quadratic_babybear.rs b/math/src/field/fields/fft_friendly/quadratic_babybear.rs new file mode 100644 index 0000000000..d3d4ea0c9a --- /dev/null +++ b/math/src/field/fields/fft_friendly/quadratic_babybear.rs @@ -0,0 +1,80 @@ +use crate::field::{ + element::FieldElement, extensions::quadratic::*, + fields::fft_friendly::babybear::Babybear31PrimeField, +}; + +/// Quadratic field extension of Babybear +pub type QuadraticBabybearField = + QuadraticExtensionField; + +impl HasQuadraticNonResidue for Babybear31PrimeField { + fn residue() -> FieldElement { + -FieldElement::one() + } +} + +/// Field element type for the quadratic extension of Babybear +pub type QuadraticBabybearFieldElement = + QuadraticExtensionFieldElement; + +#[cfg(test)] +mod tests { + use super::*; + + type FE = FieldElement; + type Fee = QuadraticBabybearFieldElement; + + #[test] + fn test_add_quadratic() { + let a = Fee::new([FE::from(0), FE::from(3)]); + let b = Fee::new([-FE::from(2), FE::from(8)]); + let expected_result = Fee::new([FE::from(0) - FE::from(2), FE::from(3) + FE::from(8)]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_sub_quadratic() { + let a = Fee::new([FE::from(0), FE::from(3)]); + let b = Fee::new([-FE::from(2), FE::from(8)]); + let expected_result = Fee::new([FE::from(0) + FE::from(2), FE::from(3) - FE::from(8)]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_mul_quadratic() { + let a = Fee::new([FE::from(12), FE::from(5)]); + let b = Fee::new([-FE::from(4), FE::from(2)]); + let expected_result = Fee::new([ + FE::from(12) * (-FE::from(4)) + + FE::from(5) * FE::from(2) * Babybear31PrimeField::residue(), + FE::from(12) * FE::from(2) + FE::from(5) * (-FE::from(4)), + ]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_inv_quadratic() { + let a = Fee::new([FE::from(12), FE::from(5)]); + let inv_norm = (FE::from(12).pow(2_u64) + - Babybear31PrimeField::residue() * FE::from(5).pow(2_u64)) + .inv() + .unwrap(); + let expected_result = Fee::new([FE::from(12) * &inv_norm, -&FE::from(5) * inv_norm]); + assert_eq!(a.inv().unwrap(), expected_result); + } + + #[test] + fn test_div_quadratic() { + let a = Fee::new([FE::from(12), FE::from(5)]); + let b = Fee::new([-FE::from(4), FE::from(2)]); + let expected_result = &a * b.inv().unwrap(); + assert_eq!(a / b, expected_result); + } + + #[test] + fn test_conjugate_quadratic() { + let a = Fee::new([FE::from(12), FE::from(5)]); + let expected_result = Fee::new([FE::from(12), -FE::from(5)]); + assert_eq!(a.conjugate(), expected_result); + } +} diff --git a/math/src/field/fields/mersenne31/extension.rs b/math/src/field/fields/mersenne31/extension.rs index e8773e8d5a..3c89a21472 100644 --- a/math/src/field/fields/mersenne31/extension.rs +++ b/math/src/field/fields/mersenne31/extension.rs @@ -93,12 +93,11 @@ impl IsField for Mersenne31Complex { } } -pub type Mersenne31ComplexQuadraticExtensionField = QuadraticExtensionField; +pub type Mersenne31ComplexQuadraticExtensionField = + QuadraticExtensionField; //TODO: Check this should be for complex and not base field -impl HasQuadraticNonResidue for Mersenne31Complex { - type BaseField = Mersenne31Complex; - +impl HasQuadraticNonResidue for Mersenne31Complex { // Verifiable in Sage with // ```sage // p = 2**31 - 1 # Mersenne31 @@ -117,11 +116,10 @@ impl HasQuadraticNonResidue for Mersenne31Complex { } } -pub type Mersenne31ComplexCubicExtensionField = CubicExtensionField; - -impl HasCubicNonResidue for Mersenne31Complex { - type BaseField = Mersenne31Complex; +pub type Mersenne31ComplexCubicExtensionField = + CubicExtensionField; +impl HasCubicNonResidue for Mersenne31Complex { // Verifiable in Sage with // ```sage // p = 2**31 - 1 # Mersenne31 diff --git a/math/src/field/fields/mersenne31/field.rs b/math/src/field/fields/mersenne31/field.rs index 7a07597f95..5454cd6814 100644 --- a/math/src/field/fields/mersenne31/field.rs +++ b/math/src/field/fields/mersenne31/field.rs @@ -173,6 +173,11 @@ impl IsPrimeField for Mersenne31Field { } u32::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u32) -> String { + format!("{:X}", x) + } } impl FieldElement { @@ -399,4 +404,11 @@ mod tests { let b = F::from_base_type(1u32); assert_eq!(b, F::one()); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let num = F::from_hex("B").unwrap(); + assert_eq!(F::to_hex(&num), "B"); + } } diff --git a/math/src/field/fields/mod.rs b/math/src/field/fields/mod.rs index 33c39d8166..48b94e9010 100644 --- a/math/src/field/fields/mod.rs +++ b/math/src/field/fields/mod.rs @@ -5,7 +5,16 @@ pub mod mersenne31; pub mod montgomery_backed_prime_fields; /// Implementation of the Goldilocks Prime field (p = 2^448 - 2^224 - 1) pub mod p448_goldilocks_prime_field; +/// Implemenation of Pallas field +pub mod pallas_field; /// Implementation of the u64 Goldilocks Prime field (p = 2^64 - 2^32 + 1) pub mod u64_goldilocks_field; /// Implementation of prime fields over 64 bit unsigned integers. pub mod u64_prime_field; + +/// Winterfell and miden field compatibility +#[cfg(feature = "winter_compatibility")] +pub mod winterfell; + +/// Implemenation of Vesta Prime field (p = 2^254 + 45560315531506369815346746415080538113) +pub mod vesta_field; diff --git a/math/src/field/fields/montgomery_backed_prime_fields.rs b/math/src/field/fields/montgomery_backed_prime_fields.rs index 01e3e59059..e2e0f66a45 100644 --- a/math/src/field/fields/montgomery_backed_prime_fields.rs +++ b/math/src/field/fields/montgomery_backed_prime_fields.rs @@ -309,6 +309,11 @@ where &MontgomeryBackendPrimeField::::MU, )) } + + #[cfg(feature = "std")] + fn to_hex(x: &Self::BaseType) -> String { + Self::BaseType::to_hex(x) + } } impl FieldElement> where @@ -1129,6 +1134,35 @@ mod tests_u256_prime_fields { assert_eq!(a, b); } + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_1() { + let a = U256FP1Element::from_hex_unchecked("eb235f6144d9e91f4b14"); + let b = U256FP1Element::new(U256 { + limbs: [0, 0, 60195, 6872850209053821716], + }); + + assert_eq!(U256FP1Element::to_hex(&a), U256FP1Element::to_hex(&b)); + } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_2() { + let a = U256F29Element::from_hex_unchecked("1d"); + let b = U256F29Element::zero(); + + assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); + } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_3() { + let a = U256F29Element::from_hex_unchecked("aa"); + let b = U256F29Element::from(25); + + assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); + } + // Goldilocks #[derive(Clone, Debug)] struct GoldilocksModulus; diff --git a/math/src/field/fields/p448_goldilocks_prime_field.rs b/math/src/field/fields/p448_goldilocks_prime_field.rs index 30400988ba..5e927386c6 100644 --- a/math/src/field/fields/p448_goldilocks_prime_field.rs +++ b/math/src/field/fields/p448_goldilocks_prime_field.rs @@ -217,6 +217,11 @@ impl IsPrimeField for P448GoldilocksPrimeField { U56x8::from_hex(hex_string) } + #[cfg(feature = "std")] + fn to_hex(x: &U56x8) -> String { + U56x8::to_hex(x) + } + fn field_bit_size() -> usize { 448 } @@ -310,6 +315,15 @@ impl U56x8 { Ok(U56x8 { limbs: result }) } + + #[cfg(feature = "std")] + pub fn to_hex(&self) -> String { + let mut hex_string = String::new(); + for &limb in self.limbs.iter().rev() { + hex_string.push_str(&format!("{:014X}", limb)); + } + hex_string.trim_start_matches('0').to_string() + } } #[cfg(test)] @@ -435,4 +449,14 @@ mod tests { U56x8::from_hex("564a75b90ae34f8155d5821d7e9484").unwrap() ); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let mut limbs = [0u64; 8]; + limbs[0] = 15372427657916355716u64; + limbs[1] = 6217911673150459564u64; + let num = U56x8::from_hex("564A75B90AE34F8155D5821D7E9484").unwrap(); + assert_eq!(U56x8::to_hex(&num), "564A75B90AE34F8155D5821D7E9484") + } } diff --git a/math/src/field/fields/pallas_field.rs b/math/src/field/fields/pallas_field.rs new file mode 100644 index 0000000000..aeee1e74ee --- /dev/null +++ b/math/src/field/fields/pallas_field.rs @@ -0,0 +1,17 @@ +use crate::{ + field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + unsigned_integer::element::U256, +}; + +type PallasMontgomeryBackendPrimeField = MontgomeryBackendPrimeField; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MontgomeryConfigPallas255PrimeField; +impl IsModulus for MontgomeryConfigPallas255PrimeField { + const MODULUS: U256 = U256::from_hex_unchecked( + "40000000000000000000000000000000224698fc094cf91b992d30ed00000001", + ); +} + +pub type Pallas255PrimeField = + PallasMontgomeryBackendPrimeField; diff --git a/math/src/field/fields/u64_goldilocks_field.rs b/math/src/field/fields/u64_goldilocks_field.rs index 240eb71da4..bfbd7802cd 100644 --- a/math/src/field/fields/u64_goldilocks_field.rs +++ b/math/src/field/fields/u64_goldilocks_field.rs @@ -177,6 +177,11 @@ impl IsPrimeField for Goldilocks64Field { } u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u64) -> String { + format!("{:X}", x) + } } #[inline(always)] @@ -211,11 +216,9 @@ fn exp_power_of_2(base: &u64) -> u64 { res } -pub type Goldilocks64ExtensionField = QuadraticExtensionField; - -impl HasQuadraticNonResidue for Goldilocks64Field { - type BaseField = Goldilocks64Field; +pub type Goldilocks64ExtensionField = QuadraticExtensionField; +impl HasQuadraticNonResidue for Goldilocks64Field { // Verifiable in Sage with // `R. = GF(p)[]; assert (x^2 - 7).is_irreducible()` fn residue() -> FieldElement { @@ -475,4 +478,11 @@ mod tests { let b = F::from_base_type(1u64); assert_eq!(b, F::one()); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let num = F::from_hex("B").unwrap(); + assert_eq!(F::to_hex(&num), "B"); + } } diff --git a/math/src/field/fields/u64_prime_field.rs b/math/src/field/fields/u64_prime_field.rs index f3096740d6..b300464bda 100644 --- a/math/src/field/fields/u64_prime_field.rs +++ b/math/src/field/fields/u64_prime_field.rs @@ -102,6 +102,11 @@ impl IsPrimeField for U64PrimeField { u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u64) -> String { + format!("{:X}", x) + } } /// Represents an element in Fp. (E.g: 0, 1, 2 are the elements of F3) @@ -176,6 +181,20 @@ mod tests { assert_eq!(F::from_hex("0x1a").unwrap(), 26); } + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_1() { + let num = F::from_hex("B").unwrap(); + assert_eq!(F::to_hex(&num), "B"); + } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test_works_2() { + let num = F::from_hex("0x1a").unwrap(); + assert_eq!(F::to_hex(&num), "1A"); + } + #[test] fn bit_size_of_mod_13_field_is_4() { assert_eq!( diff --git a/math/src/field/fields/vesta_field.rs b/math/src/field/fields/vesta_field.rs new file mode 100644 index 0000000000..c880bcebab --- /dev/null +++ b/math/src/field/fields/vesta_field.rs @@ -0,0 +1,16 @@ +use crate::{ + field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, + unsigned_integer::element::U256, +}; + +type VestaMontgomeryBackendPrimeField = MontgomeryBackendPrimeField; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MontgomeryConfigVesta255PrimeField; +impl IsModulus for MontgomeryConfigVesta255PrimeField { + const MODULUS: U256 = U256::from_hex_unchecked( + "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", + ); +} + +pub type Vesta255PrimeField = VestaMontgomeryBackendPrimeField; diff --git a/math/src/field/fields/winterfell.rs b/math/src/field/fields/winterfell.rs new file mode 100644 index 0000000000..5a2bd89e14 --- /dev/null +++ b/math/src/field/fields/winterfell.rs @@ -0,0 +1,260 @@ +use core::ops::Add; + +use crate::{ + errors::ByteConversionError, + field::{ + element::FieldElement, + errors::FieldError, + traits::{IsFFTField, IsField, IsPrimeField, IsSubFieldOf}, + }, + traits::{ByteConversion, Serializable}, + unsigned_integer::element::U256, +}; +pub use miden_core::Felt; +use miden_core::QuadExtension; +pub use winter_math::fields::f128::BaseElement; +use winter_math::{ExtensionOf, FieldElement as IsWinterfellFieldElement, StarkField}; + +// Implementation of Lambdaworks' different field traits for Miden's base field element `Felt` and +// its quadratic extension `QuadFelt`. + +impl IsFFTField for Felt { + const TWO_ADICITY: u64 = ::TWO_ADICITY as u64; + const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = Felt::TWO_ADIC_ROOT_OF_UNITY; +} + +impl IsPrimeField for Felt { + type RepresentativeType = U256; + + fn representative(_a: &Self::BaseType) -> Self::RepresentativeType { + todo!() + } + + fn from_hex(_hex_string: &str) -> Result { + todo!() + } + + fn to_hex(_a: &Self::BaseType) -> String { + todo!() + } + + fn field_bit_size() -> usize { + 128 // TODO + } +} + +impl IsField for Felt { + type BaseType = Felt; + + fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a + *b + } + + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a * *b + } + + fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a - *b + } + + fn neg(a: &Self::BaseType) -> Self::BaseType { + -*a + } + + fn inv(a: &Self::BaseType) -> Result { + Ok((*a).inv()) + } + + fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a / *b + } + + fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { + *a == *b + } + + fn zero() -> Self::BaseType { + Self::BaseType::ZERO + } + + fn one() -> Self::BaseType { + Self::BaseType::ONE + } + + fn from_u64(x: u64) -> Self::BaseType { + Self::BaseType::from(x) + } + + fn from_base_type(x: Self::BaseType) -> Self::BaseType { + x + } +} + +impl Serializable for FieldElement { + fn serialize(&self) -> Vec { + Felt::elements_as_bytes(&[*self.value()]).to_vec() + } +} + +impl ByteConversion for Felt { + fn to_bytes_be(&self) -> Vec { + Felt::elements_as_bytes(&[*self]).to_vec() + } + + fn to_bytes_le(&self) -> Vec { + Felt::elements_as_bytes(&[*self]).to_vec() + } + + fn from_bytes_be(bytes: &[u8]) -> Result + where + Self: Sized, + { + unsafe { + let res = Felt::bytes_as_elements(bytes) + .map_err(|_| ByteConversionError::FromBEBytesError)?; + Ok(res[0]) + } + } + + fn from_bytes_le(bytes: &[u8]) -> Result + where + Self: Sized, + { + unsafe { + let res = Felt::bytes_as_elements(bytes) + .map_err(|_| ByteConversionError::FromBEBytesError)?; + Ok(res[0]) + } + } +} + +pub type QuadFelt = QuadExtension; + +impl ByteConversion for QuadFelt { + fn to_bytes_be(&self) -> Vec { + let [b0, b1] = self.to_base_elements(); + let mut bytes = b0.to_bytes_be(); + bytes.extend(&b1.to_bytes_be()); + bytes + } + + fn to_bytes_le(&self) -> Vec { + let [b0, b1] = self.to_base_elements(); + let mut bytes = b0.to_bytes_le(); + bytes.extend(&b1.to_bytes_be()); + bytes + } + + fn from_bytes_be(_bytes: &[u8]) -> Result + where + Self: Sized, + { + todo!() + } + + fn from_bytes_le(_bytes: &[u8]) -> Result + where + Self: Sized, + { + todo!() + } +} + +impl Serializable for FieldElement { + fn serialize(&self) -> Vec { + let [b0, b1] = self.value().to_base_elements(); + let mut bytes = b0.to_bytes_be(); + bytes.extend(&b1.to_bytes_be()); + bytes + } +} + +impl IsField for QuadFelt { + type BaseType = QuadFelt; + + fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a + *b + } + + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a * *b + } + + fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a - *b + } + + fn neg(a: &Self::BaseType) -> Self::BaseType { + -*a + } + + fn inv(a: &Self::BaseType) -> Result { + Ok((*a).inv()) + } + + fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + *a / *b + } + + fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { + *a == *b + } + + fn zero() -> Self::BaseType { + Self::BaseType::ZERO + } + + fn one() -> Self::BaseType { + Self::BaseType::ONE + } + + fn from_u64(x: u64) -> Self::BaseType { + Self::BaseType::from(x) + } + + fn from_base_type(x: Self::BaseType) -> Self::BaseType { + x + } +} + +impl IsSubFieldOf for Felt { + fn mul( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + b.mul_base(*a) + } + + fn add( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let [b0, b1] = b.to_base_elements(); + QuadFelt::new(b0.add(*a), b1) + } + + fn div( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let b_inv = b.inv(); + >::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let [b0, b1] = b.to_base_elements(); + QuadFelt::new(a.add(-(b0)), -b1) + } + + fn embed(a: Self::BaseType) -> ::BaseType { + QuadFelt::new(a, Felt::ZERO) + } + + fn to_subfield_vec(b: ::BaseType) -> Vec { + b.to_base_elements().to_vec() + } +} diff --git a/math/src/field/mod.rs b/math/src/field/mod.rs index 2fe6c42725..5cb5f2506d 100644 --- a/math/src/field/mod.rs +++ b/math/src/field/mod.rs @@ -1,5 +1,6 @@ /// Implementation of FieldElement, a generic element of a field. pub mod element; +pub mod errors; /// Implementation of quadratic extensions of fields. pub mod extensions; /// Implementation of particular cases of fields. @@ -8,5 +9,3 @@ pub mod fields; pub mod test_fields; /// Common behaviour for field elements. pub mod traits; - -pub mod errors; diff --git a/math/src/field/test_fields/u32_test_field.rs b/math/src/field/test_fields/u32_test_field.rs index d4dd139915..40de22bcd1 100644 --- a/math/src/field/test_fields/u32_test_field.rs +++ b/math/src/field/test_fields/u32_test_field.rs @@ -116,6 +116,11 @@ impl IsPrimeField for U32Field { u32::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u32) -> String { + format!("{:X}", x) + } } // 15 * 2^27 + 1; @@ -136,6 +141,13 @@ mod tests_u32_test_field { assert_eq!(U32TestField::from_hex("B").unwrap(), 11); } + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let num = U32TestField::from_hex("B").unwrap(); + assert_eq!(U32TestField::to_hex(&num), "B"); + } + #[test] fn bit_size_of_test_field_is_31() { assert_eq!( diff --git a/math/src/field/test_fields/u64_test_field.rs b/math/src/field/test_fields/u64_test_field.rs index 780924d2a4..f3cbc0dbe8 100644 --- a/math/src/field/test_fields/u64_test_field.rs +++ b/math/src/field/test_fields/u64_test_field.rs @@ -1,7 +1,11 @@ use crate::{ errors::CreationError, - field::errors::FieldError, - field::traits::{IsFFTField, IsField, IsPrimeField}, + field::{ + element::FieldElement, + extensions::quadratic::QuadraticExtensionField, + traits::{IsFFTField, IsField, IsPrimeField}, + }, + field::{errors::FieldError, extensions::quadratic::HasQuadraticNonResidue}, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -84,6 +88,11 @@ impl IsPrimeField for U64Field { u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) } + + #[cfg(feature = "std")] + fn to_hex(x: &u64) -> String { + format!("{:X}", x) + } } pub type U64TestField = U64Field<18446744069414584321>; @@ -94,9 +103,23 @@ impl IsFFTField for U64TestField { const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: u64 = 1753635133440165772; } +#[derive(Clone, Debug)] +pub struct TestNonResidue; +impl HasQuadraticNonResidue for TestNonResidue { + fn residue() -> FieldElement { + FieldElement::from(7) + } +} + +pub type U64TestFieldExtension = QuadraticExtensionField; + #[cfg(test)] mod tests_u64_test_field { - use crate::field::{test_fields::u64_test_field::U64TestField, traits::IsPrimeField}; + use crate::field::{ + element::FieldElement, + test_fields::u64_test_field::{U64TestField, U64TestFieldExtension}, + traits::IsPrimeField, + }; #[test] fn from_hex_for_b_is_11() { @@ -110,4 +133,22 @@ mod tests_u64_test_field { 64 ); } + + #[cfg(feature = "std")] + #[test] + fn test_to_subfield_vec() { + let a = FieldElement::::from(&[ + FieldElement::from(1), + FieldElement::from(3), + ]); + let b = a.to_subfield_vec::(); + assert_eq!(b, vec![FieldElement::from(1), FieldElement::from(3)]); + } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let num = U64TestField::from_hex("B").unwrap(); + assert_eq!(U64TestField::to_hex(&num), "B"); + } } diff --git a/math/src/field/traits.rs b/math/src/field/traits.rs index e30bc66a7b..9896259b4f 100644 --- a/math/src/field/traits.rs +++ b/math/src/field/traits.rs @@ -14,6 +14,52 @@ pub enum RootsConfig { BitReverseInversed, // same as above but exponents are negated. } +/// Represents the subfield relation between two fields. +pub trait IsSubFieldOf: IsField { + fn mul(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; + fn add(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; + fn div(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; + fn sub(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; + fn embed(a: Self::BaseType) -> F::BaseType; + #[cfg(feature = "std")] + fn to_subfield_vec(b: F::BaseType) -> Vec; +} + +impl IsSubFieldOf for F +where + F: IsField, +{ + #[inline(always)] + fn mul(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { + F::mul(a, b) + } + + #[inline(always)] + fn add(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { + F::add(a, b) + } + + #[inline(always)] + fn sub(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { + F::sub(a, b) + } + + #[inline(always)] + fn div(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { + F::div(a, b) + } + + #[inline(always)] + fn embed(a: Self::BaseType) -> F::BaseType { + a + } + + #[cfg(feature = "std")] + fn to_subfield_vec(b: F::BaseType) -> Vec { + vec![b] + } +} + /// Trait to define necessary parameters for FFT-friendly Fields. /// Two-Adic fields are ones whose order is of the form $2^n k + 1$. /// Here $n$ is usually called the *two-adicity* of the field. The @@ -22,7 +68,7 @@ pub enum RootsConfig { /// A two-adic primitive root of unity is a number w that satisfies w^(2^n) = 1 /// and w^(j) != 1 for every j below 2^n. With this primitive root we can generate /// any other root of unity we need to perform FFT. -pub trait IsFFTField: IsPrimeField { +pub trait IsFFTField: IsField { const TWO_ADICITY: u64; const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType; @@ -33,18 +79,16 @@ pub trait IsFFTField: IsPrimeField { } /// Returns a primitive root of unity of order $2^{order}$. - fn get_primitive_root_of_unity( - order: u64, - ) -> Result, FieldError> { + fn get_primitive_root_of_unity(order: u64) -> Result, FieldError> { let two_adic_primitive_root_of_unity = - FieldElement::new(F::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY); + FieldElement::new(Self::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY); if order == 0 { return Ok(FieldElement::one()); } - if order > F::TWO_ADICITY { + if order > Self::TWO_ADICITY { return Err(FieldError::RootOfUnityError(order)); } - let log_power = F::TWO_ADICITY - order; + let log_power = Self::TWO_ADICITY - order; let root = (0..log_power).fold(two_adic_primitive_root_of_unity, |acc, _| acc.square()); Ok(root) } @@ -160,6 +204,10 @@ pub trait IsPrimeField: IsField { /// Returns an `CreationError::InvalidHexString`if the value is not a hexstring fn from_hex(hex_string: &str) -> Result; + #[cfg(feature = "std")] + /// Creates a hexstring from a `FieldElement` without `0x`. + fn to_hex(a: &Self::BaseType) -> String; + /// Returns the number of bits of the max element of the field, as per field documentation, not internal representation. /// This is `log2(max FE)` rounded up fn field_bit_size() -> usize; diff --git a/math/src/polynomial.rs b/math/src/polynomial.rs index 940e49cfc8..cee58d339c 100644 --- a/math/src/polynomial.rs +++ b/math/src/polynomial.rs @@ -1,5 +1,5 @@ use super::field::element::FieldElement; -use crate::field::traits::IsField; +use crate::field::traits::{IsField, IsSubFieldOf}; use std::ops; /// Represents the polynomial c_0 + c_1 * X + c_2 * X^2 + ... + c_n * X^n @@ -90,12 +90,16 @@ impl Polynomial> { Ok(result) } - pub fn evaluate(&self, x: &FieldElement) -> FieldElement { + pub fn evaluate(&self, x: &FieldElement) -> FieldElement + where + E: IsField, + F: IsSubFieldOf, + { self.coefficients .iter() .rev() .fold(FieldElement::zero(), |acc, coeff| { - acc * x.to_owned() + coeff + coeff + acc * x.to_owned() }) } @@ -131,23 +135,6 @@ impl Polynomial> { self.coefficients().len() } - pub fn pad_with_zero_coefficients_to_length(pa: &mut Self, n: usize) { - pa.coefficients.resize(n, FieldElement::zero()); - } - - /// Pads polynomial representations with minimum number of zeros to match lengths. - pub fn pad_with_zero_coefficients(pa: &Self, pb: &Self) -> (Self, Self) { - let mut pa = pa.clone(); - let mut pb = pb.clone(); - - if pa.coefficients.len() > pb.coefficients.len() { - Self::pad_with_zero_coefficients_to_length(&mut pb, pa.coefficients.len()); - } else { - Self::pad_with_zero_coefficients_to_length(&mut pa, pb.coefficients.len()); - } - (pa, pb) - } - /// Computes quotient with `x - b` in place. pub fn ruffini_division_inplace(&mut self, b: &FieldElement) { let mut c = FieldElement::zero(); @@ -158,6 +145,25 @@ impl Polynomial> { self.coefficients.pop(); } + pub fn ruffini_division(&self, b: &FieldElement) -> Polynomial> + where + L: IsField, + F: IsSubFieldOf, + { + if let Some(c) = self.coefficients.last() { + let mut c = c.clone().to_extension(); + let mut coefficients = Vec::with_capacity(self.degree()); + for coeff in self.coefficients.iter().rev().skip(1) { + coefficients.push(c.clone()); + c = coeff + c * b; + } + coefficients = coefficients.into_iter().rev().collect(); + Polynomial::new(&coefficients) + } else { + Polynomial::zero() + } + } + /// Computes quotient and remainder of polynomial division. /// /// Output: (quotient, remainder) @@ -202,7 +208,7 @@ impl Polynomial> { } } - pub fn scale(&self, factor: &FieldElement) -> Self { + pub fn scale>(&self, factor: &FieldElement) -> Self { let scaled_coefficients = self .coefficients .iter() @@ -246,6 +252,42 @@ impl Polynomial> { } parts } + + pub fn to_extension(self) -> Polynomial> + where + F: IsSubFieldOf, + { + Polynomial { + coefficients: self + .coefficients + .into_iter() + .map(|x| x.to_extension::()) + .collect(), + } + } +} + +pub fn pad_with_zero_coefficients_to_length( + pa: &mut Polynomial>, + n: usize, +) { + pa.coefficients.resize(n, FieldElement::zero()); +} + +/// Pads polynomial representations with minimum number of zeros to match lengths. +pub fn pad_with_zero_coefficients>( + pa: &Polynomial>, + pb: &Polynomial>, +) -> (Polynomial>, Polynomial>) { + let mut pa = pa.clone(); + let mut pb = pb.clone(); + + if pa.coefficients.len() > pb.coefficients.len() { + pad_with_zero_coefficients_to_length(&mut pb, pa.coefficients.len()); + } else { + pad_with_zero_coefficients_to_length(&mut pa, pb.coefficients.len()); + } + (pa, pb) } pub fn compose( @@ -274,39 +316,55 @@ where .expect("xs and ys have equal length and xs are unique") } -impl ops::Add<&Polynomial>> for &Polynomial> { - type Output = Polynomial>; +impl ops::Add<&Polynomial>> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, a_polynomial: &Polynomial>) -> Self::Output { - let (pa, pb) = Polynomial::pad_with_zero_coefficients(self, a_polynomial); + fn add(self, a_polynomial: &Polynomial>) -> Self::Output { + let (pa, pb) = pad_with_zero_coefficients(self, a_polynomial); let iter_coeff_pa = pa.coefficients.iter(); let iter_coeff_pb = pb.coefficients.iter(); let new_coefficients = iter_coeff_pa.zip(iter_coeff_pb).map(|(x, y)| x + y); - let new_coefficients_vec = new_coefficients.collect::>>(); + let new_coefficients_vec = new_coefficients.collect::>>(); Polynomial::new(&new_coefficients_vec) } } -impl ops::Add>> for Polynomial> { - type Output = Polynomial>; +impl ops::Add>> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, a_polynomial: Polynomial>) -> Polynomial> { + fn add(self, a_polynomial: Polynomial>) -> Polynomial> { &self + &a_polynomial } } -impl ops::Add<&Polynomial>> for Polynomial> { - type Output = Polynomial>; +impl ops::Add<&Polynomial>> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, a_polynomial: &Polynomial>) -> Polynomial> { + fn add(self, a_polynomial: &Polynomial>) -> Polynomial> { &self + a_polynomial } } -impl ops::Add>> for &Polynomial> { - type Output = Polynomial>; +impl ops::Add>> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, a_polynomial: Polynomial>) -> Polynomial> { + fn add(self, a_polynomial: Polynomial>) -> Polynomial> { self + &a_polynomial } } @@ -332,39 +390,58 @@ impl ops::Neg for Polynomial> { } } -impl ops::Sub>> for Polynomial> { - type Output = Polynomial>; +impl ops::Sub<&Polynomial>> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, substrahend: Polynomial>) -> Polynomial> { - &self - &substrahend + fn sub(self, substrahend: &Polynomial>) -> Polynomial> { + self + (-substrahend) } } -impl ops::Sub<&Polynomial>> for Polynomial> { - type Output = Polynomial>; +impl ops::Sub>> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, substrahend: &Polynomial>) -> Polynomial> { - &self - substrahend + fn sub(self, substrahend: Polynomial>) -> Polynomial> { + &self - &substrahend } } -impl ops::Sub>> for &Polynomial> { - type Output = Polynomial>; +impl ops::Sub<&Polynomial>> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, substrahend: Polynomial>) -> Polynomial> { - self - &substrahend + fn sub(self, substrahend: &Polynomial>) -> Polynomial> { + &self - substrahend } } -impl ops::Sub<&Polynomial>> for &Polynomial> { - type Output = Polynomial>; +impl ops::Sub>> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, substrahend: &Polynomial>) -> Polynomial> { - self + (-substrahend) + fn sub(self, substrahend: Polynomial>) -> Polynomial> { + self - &substrahend } } -impl ops::Div>> for Polynomial> { +impl ops::Div>> for Polynomial> +where + F: IsField, +{ type Output = Polynomial>; fn div(self, dividend: Polynomial>) -> Polynomial> { @@ -402,14 +479,18 @@ impl ops::Mul<&Polynomial>> for Polynomial ops::Mul> for Polynomial> { - type Output = Polynomial>; +impl ops::Mul> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: FieldElement) -> Polynomial> { + fn mul(self, multiplicand: FieldElement) -> Polynomial> { let new_coefficients = self .coefficients .iter() - .map(|value| value * &multiplicand) + .map(|value| &multiplicand * value) .collect(); Polynomial { coefficients: new_coefficients, @@ -417,191 +498,283 @@ impl ops::Mul> for Polynomial> { } } -impl ops::Mul<&FieldElement> for &Polynomial> { - type Output = Polynomial>; +impl ops::Mul<&FieldElement> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: &FieldElement) -> Polynomial> { + fn mul(self, multiplicand: &FieldElement) -> Polynomial> { self.clone() * multiplicand.clone() } } -impl ops::Mul> for &Polynomial> { - type Output = Polynomial>; +impl ops::Mul> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: FieldElement) -> Polynomial> { + fn mul(self, multiplicand: FieldElement) -> Polynomial> { self * &multiplicand } } -impl ops::Mul<&FieldElement> for Polynomial> { - type Output = Polynomial>; +impl ops::Mul<&FieldElement> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: &FieldElement) -> Polynomial> { + fn mul(self, multiplicand: &FieldElement) -> Polynomial> { &self * multiplicand } } /* Multiplication field element at right */ -impl ops::Mul<&Polynomial>> for &FieldElement { - type Output = Polynomial>; +impl ops::Mul<&Polynomial>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { + fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { multiplicand * self } } -impl ops::Mul>> for &FieldElement { - type Output = Polynomial>; +impl ops::Mul>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: Polynomial>) -> Polynomial> { + fn mul(self, multiplicand: Polynomial>) -> Polynomial> { &multiplicand * self } } -impl ops::Mul<&Polynomial>> for FieldElement { - type Output = Polynomial>; +impl ops::Mul<&Polynomial>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { + fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { multiplicand * self } } -impl ops::Mul>> for FieldElement { - type Output = Polynomial>; +impl ops::Mul>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn mul(self, multiplicand: Polynomial>) -> Polynomial> { + fn mul(self, multiplicand: Polynomial>) -> Polynomial> { &multiplicand * &self } } /* Addition field element at left */ -impl ops::Add<&FieldElement> for &Polynomial> { - type Output = Polynomial>; +impl ops::Add<&FieldElement> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: &FieldElement) -> Polynomial> { + fn add(self, other: &FieldElement) -> Polynomial> { Polynomial::new_monomial(other.clone(), 0) + self } } -impl ops::Add> for Polynomial> { - type Output = Polynomial>; +impl ops::Add> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: FieldElement) -> Polynomial> { + fn add(self, other: FieldElement) -> Polynomial> { &self + &other } } -impl ops::Add> for &Polynomial> { - type Output = Polynomial>; +impl ops::Add> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: FieldElement) -> Polynomial> { + fn add(self, other: FieldElement) -> Polynomial> { self + &other } } -impl ops::Add<&FieldElement> for Polynomial> { - type Output = Polynomial>; +impl ops::Add<&FieldElement> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: &FieldElement) -> Polynomial> { + fn add(self, other: &FieldElement) -> Polynomial> { &self + other } } /* Addition field element at right */ -impl ops::Add<&Polynomial>> for &FieldElement { - type Output = Polynomial>; +impl ops::Add<&Polynomial>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: &Polynomial>) -> Polynomial> { + fn add(self, other: &Polynomial>) -> Polynomial> { Polynomial::new_monomial(self.clone(), 0) + other } } -impl ops::Add>> for FieldElement { - type Output = Polynomial>; +impl ops::Add>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: Polynomial>) -> Polynomial> { + fn add(self, other: Polynomial>) -> Polynomial> { &self + &other } } -impl ops::Add>> for &FieldElement { - type Output = Polynomial>; +impl ops::Add>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: Polynomial>) -> Polynomial> { + fn add(self, other: Polynomial>) -> Polynomial> { self + &other } } -impl ops::Add<&Polynomial>> for FieldElement { - type Output = Polynomial>; +impl ops::Add<&Polynomial>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn add(self, other: &Polynomial>) -> Polynomial> { + fn add(self, other: &Polynomial>) -> Polynomial> { &self + other } } /* Substraction field element at left */ -impl ops::Sub<&FieldElement> for &Polynomial> { - type Output = Polynomial>; +impl ops::Sub<&FieldElement> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: &FieldElement) -> Polynomial> { - self - Polynomial::new_monomial(other.clone(), 0) + fn sub(self, other: &FieldElement) -> Polynomial> { + -Polynomial::new_monomial(other.clone(), 0) + self } } -impl ops::Sub> for Polynomial> { - type Output = Polynomial>; +impl ops::Sub> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: FieldElement) -> Polynomial> { + fn sub(self, other: FieldElement) -> Polynomial> { &self - &other } } -impl ops::Sub> for &Polynomial> { - type Output = Polynomial>; +impl ops::Sub> for &Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: FieldElement) -> Polynomial> { + fn sub(self, other: FieldElement) -> Polynomial> { self - &other } } -impl ops::Sub<&FieldElement> for Polynomial> { - type Output = Polynomial>; +impl ops::Sub<&FieldElement> for Polynomial> +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: &FieldElement) -> Polynomial> { + fn sub(self, other: &FieldElement) -> Polynomial> { &self - other } } /* Substraction field element at right */ -impl ops::Sub<&Polynomial>> for &FieldElement { - type Output = Polynomial>; +impl ops::Sub<&Polynomial>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: &Polynomial>) -> Polynomial> { + fn sub(self, other: &Polynomial>) -> Polynomial> { Polynomial::new_monomial(self.clone(), 0) - other } } -impl ops::Sub>> for FieldElement { - type Output = Polynomial>; +impl ops::Sub>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: Polynomial>) -> Polynomial> { + fn sub(self, other: Polynomial>) -> Polynomial> { &self - &other } } -impl ops::Sub>> for &FieldElement { - type Output = Polynomial>; +impl ops::Sub>> for &FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: Polynomial>) -> Polynomial> { + fn sub(self, other: Polynomial>) -> Polynomial> { self - &other } } -impl ops::Sub<&Polynomial>> for FieldElement { - type Output = Polynomial>; +impl ops::Sub<&Polynomial>> for FieldElement +where + L: IsField, + F: IsSubFieldOf, +{ + type Output = Polynomial>; - fn sub(self, other: &Polynomial>) -> Polynomial> { + fn sub(self, other: &Polynomial>) -> Polynomial> { &self - other } } @@ -711,7 +884,7 @@ mod tests { let p2 = Polynomial::new(&[FE::new(3)]); assert_eq!(p2.coefficients, &[FE::new(3)]); - let (pp1, pp2) = Polynomial::pad_with_zero_coefficients(&p1, &p2); + let (pp1, pp2) = pad_with_zero_coefficients(&p1, &p2); assert_eq!(pp1, p1); assert_eq!(pp2.coefficients, &[FE::new(3), FE::new(0)]); } @@ -909,7 +1082,7 @@ mod tests { use proptest::prelude::*; proptest! { #[test] - fn ruffini_equals_division(p in any::>(), b in any::()) { + fn ruffini_inplace_equals_division(p in any::>(), b in any::()) { let p: Vec<_> = p.into_iter().map(FE::from).collect(); let mut p = Polynomial::new(&p); let b = FE::from(b); @@ -921,4 +1094,16 @@ mod tests { prop_assert_eq!(p, p_ref / m); } } + + proptest! { + #[test] + fn ruffini_inplace_equals_ruffini(p in any::>(), b in any::()) { + let p: Vec<_> = p.into_iter().map(FE::from).collect(); + let mut p = Polynomial::new(&p); + let b = FE::from(b); + let q = p.ruffini_division(&b); + p.ruffini_division_inplace(&b); + prop_assert_eq!(q, p); + } + } } diff --git a/math/src/unsigned_integer/element.rs b/math/src/unsigned_integer/element.rs index 70e111854f..e65e0ae86d 100644 --- a/math/src/unsigned_integer/element.rs +++ b/math/src/unsigned_integer/element.rs @@ -486,6 +486,16 @@ impl UnsignedInteger { UnsignedInteger { limbs: result } } + /// Creates a hexstring from a `FieldElement` without `0x`. + #[cfg(feature = "std")] + pub fn to_hex(&self) -> String { + let mut hex_string = String::new(); + for &limb in self.limbs.iter() { + hex_string.push_str(&format!("{:014X}", limb)); + } + hex_string.trim_start_matches('0').to_string() + } + pub const fn const_ne(a: &UnsignedInteger, b: &UnsignedInteger) -> bool { let mut i = 0; while i < NUM_LIMBS { @@ -2968,4 +2978,11 @@ mod tests_u256 { ) ); } + + #[cfg(feature = "std")] + #[test] + fn to_hex_test() { + let a = U256::from_hex_unchecked("390aa99bead76bc0093b1bc1a8101f5ce"); + assert_eq!(U256::to_hex(&a), "390AA99BEAD76BC0093B1BC1A8101F5CE") + } } diff --git a/provers/cairo/Cargo.toml b/provers/cairo/Cargo.toml index f503ef547c..f9fcdb58ec 100644 --- a/provers/cairo/Cargo.toml +++ b/provers/cairo/Cargo.toml @@ -23,7 +23,7 @@ thiserror = "1.0.38" log = "0.4.17" bincode = { version = "2.0.0-rc.2", tag = "v2.0.0-rc.2", git = "https://github.com/bincode-org/bincode.git", features= ['serde'] } # NOTE: For cairo 1 compatibility, add the `cairo-1-hints` feature. -cairo-vm = { git = "https://github.com/lambdaclass/cairo-vm", rev = "e763cef", default-features = false } +cairo-vm = { git = "https://github.com/lambdaclass/cairo-vm", rev = "e61ae177edb94e29470fed23bdda43329b16c057", default-features = false } sha3 = "0.10.6" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/provers/cairo/benches/criterion_verifier.rs b/provers/cairo/benches/criterion_verifier.rs index 50fefcd17e..de8379a53a 100644 --- a/provers/cairo/benches/criterion_verifier.rs +++ b/provers/cairo/benches/criterion_verifier.rs @@ -12,12 +12,17 @@ use stark_platinum_prover::proof::{ pub mod functions; -fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof, PublicInputs) { +fn load_proof_and_pub_inputs( + input_path: &str, +) -> ( + StarkProof, + PublicInputs, +) { let program_content = std::fs::read(input_path).unwrap(); let mut bytes = program_content.as_slice(); let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap()); bytes = &bytes[8..]; - let proof: StarkProof = + let proof: StarkProof = serde_cbor::from_slice(&bytes[0..proof_len]).unwrap(); bytes = &bytes[proof_len..]; diff --git a/provers/cairo/benches/criterion_verifier_70k.rs b/provers/cairo/benches/criterion_verifier_70k.rs index ab2cc6e69a..f4aafd58d1 100644 --- a/provers/cairo/benches/criterion_verifier_70k.rs +++ b/provers/cairo/benches/criterion_verifier_70k.rs @@ -13,12 +13,17 @@ use stark_platinum_prover::proof::{ pub mod functions; -fn load_proof_and_pub_inputs(input_path: &str) -> (StarkProof, PublicInputs) { +fn load_proof_and_pub_inputs( + input_path: &str, +) -> ( + StarkProof, + PublicInputs, +) { let program_content = std::fs::read(input_path).unwrap(); let mut bytes = program_content.as_slice(); let proof_len = usize::from_be_bytes(bytes[0..8].try_into().unwrap()); bytes = &bytes[8..]; - let proof: StarkProof = + let proof: StarkProof = serde_cbor::from_slice(&bytes[0..proof_len]).unwrap(); bytes = &bytes[proof_len..]; diff --git a/provers/cairo/src/air.rs b/provers/cairo/src/air.rs index 48b0b67ee0..f5e22c85b6 100644 --- a/provers/cairo/src/air.rs +++ b/provers/cairo/src/air.rs @@ -1,6 +1,4 @@ -use std::ops::Range; - -use cairo_vm::without_std::collections::HashMap; +use cairo_vm::{air_public_input::MemorySegmentAddresses, without_std::collections::HashMap}; use lambdaworks_math::{ errors::DeserializationError, field::{ @@ -126,46 +124,96 @@ pub const EXTRA_VAL: usize = 34; pub const RC_HOLES: usize = 35; // Auxiliary range check columns -pub const RANGE_CHECK_COL_1: usize = 36; -pub const RANGE_CHECK_COL_2: usize = 37; -pub const RANGE_CHECK_COL_3: usize = 38; -pub const RANGE_CHECK_COL_4: usize = 39; +pub const RANGE_CHECK_COL_1: usize = 0; +pub const RANGE_CHECK_COL_2: usize = 1; +pub const RANGE_CHECK_COL_3: usize = 2; +pub const RANGE_CHECK_COL_4: usize = 3; // Auxiliary memory columns -pub const MEMORY_ADDR_SORTED_0: usize = 40; -pub const MEMORY_ADDR_SORTED_1: usize = 41; -pub const MEMORY_ADDR_SORTED_2: usize = 42; -pub const MEMORY_ADDR_SORTED_3: usize = 43; -pub const MEMORY_ADDR_SORTED_4: usize = 44; - -pub const MEMORY_VALUES_SORTED_0: usize = 45; -pub const MEMORY_VALUES_SORTED_1: usize = 46; -pub const MEMORY_VALUES_SORTED_2: usize = 47; -pub const MEMORY_VALUES_SORTED_3: usize = 48; -pub const MEMORY_VALUES_SORTED_4: usize = 49; - -pub const PERMUTATION_ARGUMENT_COL_0: usize = 50; -pub const PERMUTATION_ARGUMENT_COL_1: usize = 51; -pub const PERMUTATION_ARGUMENT_COL_2: usize = 52; -pub const PERMUTATION_ARGUMENT_COL_3: usize = 53; -pub const PERMUTATION_ARGUMENT_COL_4: usize = 54; - -pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1: usize = 55; -pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_2: usize = 56; -pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_3: usize = 57; -pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4: usize = 58; +pub const MEMORY_ADDR_SORTED_0: usize = 4; +pub const MEMORY_ADDR_SORTED_1: usize = 5; +pub const MEMORY_ADDR_SORTED_2: usize = 6; +pub const MEMORY_ADDR_SORTED_3: usize = 7; +pub const MEMORY_ADDR_SORTED_4: usize = 8; + +pub const MEMORY_VALUES_SORTED_0: usize = 9; +pub const MEMORY_VALUES_SORTED_1: usize = 10; +pub const MEMORY_VALUES_SORTED_2: usize = 11; +pub const MEMORY_VALUES_SORTED_3: usize = 12; +pub const MEMORY_VALUES_SORTED_4: usize = 13; + +pub const PERMUTATION_ARGUMENT_COL_0: usize = 14; +pub const PERMUTATION_ARGUMENT_COL_1: usize = 15; +pub const PERMUTATION_ARGUMENT_COL_2: usize = 16; +pub const PERMUTATION_ARGUMENT_COL_3: usize = 17; +pub const PERMUTATION_ARGUMENT_COL_4: usize = 18; + +pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1: usize = 19; +pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_2: usize = 20; +pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_3: usize = 21; +pub const PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4: usize = 22; // Trace layout pub const MEM_P_TRACE_OFFSET: usize = 17; pub const MEM_A_TRACE_OFFSET: usize = 19; #[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] -pub enum MemorySegment { +pub enum SegmentName { RangeCheck, Output, + Program, + Execution, + Ecdsa, + Pedersen, +} + +impl From<&str> for SegmentName { + fn from(value: &str) -> Self { + match value { + "range_check" => SegmentName::RangeCheck, + "output" => SegmentName::Output, + "program" => SegmentName::Program, + "execution" => SegmentName::Execution, + "ecdsa" => SegmentName::Ecdsa, + "pedersen" => SegmentName::Pedersen, + n => panic!("Invalid segment name {n}"), + } + } +} + +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +pub struct Segment { + pub begin_addr: usize, + pub stop_ptr: usize, +} + +impl Segment { + pub fn new(begin_addr: u64, stop_ptr: u64) -> Self { + let begin_addr: usize = begin_addr.try_into().unwrap(); + let stop_ptr: usize = stop_ptr.try_into().unwrap(); + + stop_ptr.checked_sub(begin_addr).unwrap(); + + Self { + begin_addr, + stop_ptr, + } + } + pub fn segment_size(&self) -> usize { + self.stop_ptr - self.begin_addr - 1 + } +} + +impl From<&MemorySegmentAddresses> for Segment { + fn from(value: &MemorySegmentAddresses) -> Self { + Self { + begin_addr: value.begin_addr, + stop_ptr: value.stop_ptr, + } + } } -pub type MemorySegmentMap = HashMap>; +pub type MemorySegmentMap = HashMap; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PublicInputs { @@ -186,7 +234,6 @@ pub struct PublicInputs { pub memory_segments: MemorySegmentMap, pub public_memory: HashMap, pub num_steps: usize, // number of execution steps - pub codelen: usize, // length of the program segment } impl PublicInputs { @@ -215,7 +262,6 @@ impl PublicInputs { memory_segments: MemorySegmentMap::new(), public_memory, num_steps: register_states.steps(), - codelen, } } } @@ -249,12 +295,16 @@ impl Serializable for PublicInputs { let mut memory_segment_bytes = vec![]; for (segment, range) in self.memory_segments.iter() { let segment_type = match segment { - MemorySegment::RangeCheck => 0u8, - MemorySegment::Output => 1u8, + SegmentName::RangeCheck => 0u8, + SegmentName::Output => 1u8, + SegmentName::Program => 2u8, + SegmentName::Execution => 3u8, + SegmentName::Ecdsa => 4u8, + SegmentName::Pedersen => 5u8, }; memory_segment_bytes.extend(segment_type.to_be_bytes()); - memory_segment_bytes.extend(range.start.to_be_bytes()); - memory_segment_bytes.extend(range.end.to_be_bytes()); + memory_segment_bytes.extend(range.begin_addr.to_be_bytes()); + memory_segment_bytes.extend(range.stop_ptr.to_be_bytes()); } let memory_segment_length = self.memory_segments.len(); bytes.extend(memory_segment_length.to_be_bytes()); @@ -270,7 +320,6 @@ impl Serializable for PublicInputs { bytes.extend(public_memory_bytes); bytes.extend(self.num_steps.to_be_bytes()); - bytes.extend(self.codelen.to_be_bytes()); bytes } @@ -377,8 +426,12 @@ impl Deserializable for PublicInputs { return Err(DeserializationError::InvalidAmountOfBytes); } let segment_type = match bytes[0] { - 0 => MemorySegment::RangeCheck, - 1 => MemorySegment::Output, + 0u8 => SegmentName::RangeCheck, + 1u8 => SegmentName::Output, + 2u8 => SegmentName::Program, + 3u8 => SegmentName::Execution, + 4u8 => SegmentName::Ecdsa, + 5u8 => SegmentName::Pedersen, _ => return Err(DeserializationError::FieldFromBytesError), }; bytes = &bytes[1..]; @@ -398,7 +451,7 @@ impl Deserializable for PublicInputs { .map_err(|_| DeserializationError::InvalidAmountOfBytes)?, ); bytes = &bytes[8..]; - memory_segments.insert(segment_type, start..end); + memory_segments.insert(segment_type, Segment::new(start, end)); } let mut public_memory = HashMap::new(); @@ -434,14 +487,6 @@ impl Deserializable for PublicInputs { .map_err(|_| DeserializationError::InvalidAmountOfBytes)?, ); - let codelen = usize::from_be_bytes( - bytes - .get(0..8) - .ok_or(DeserializationError::InvalidAmountOfBytes)? - .try_into() - .map_err(|_| DeserializationError::InvalidAmountOfBytes)?, - ); - Ok(Self { pc_init, ap_init, @@ -453,7 +498,6 @@ impl Deserializable for PublicInputs { memory_segments, public_memory, num_steps, - codelen, }) } } @@ -487,10 +531,7 @@ fn add_pub_memory_in_public_input_section( let mut a_aux = addresses.to_owned(); let mut v_aux = values.to_owned(); - let output_range = public_input.memory_segments.get(&MemorySegment::Output); - - let pub_addrs = get_pub_memory_addrs(output_range, public_input); - let mut pub_addrs_iter = pub_addrs.iter(); + let mut pub_addrs = public_input.public_memory.iter(); // Iterate over addresses for (i, a) in a_aux.iter_mut().enumerate() { @@ -498,9 +539,9 @@ fn add_pub_memory_in_public_input_section( if a == &Felt252::zero() { // While there are public memory addresses left, overwrite the dummy // (addr, value) accesses with the real public memory pairs. - if let Some(pub_addr) = pub_addrs_iter.next() { + if let Some((pub_addr, pub_value)) = pub_addrs.next() { *a = *pub_addr; - v_aux[i] = *public_input.public_memory.get(pub_addr).unwrap(); + v_aux[i] = *pub_value; } else { // When there are no public memory pairs left to write, break the // loop and return the (addr, value) pairs with dummy accesses @@ -513,29 +554,6 @@ fn add_pub_memory_in_public_input_section( (a_aux, v_aux) } -/// Gets public memory addresses of a program. First, this function builds a `Vec` of `FieldElement`s, filling it -/// incrementally with addresses from `1` to `program_len - 1`, where `program_len` is the length of the program. -/// If the output builtin is used, `output_range` is `Some(...)` and this function adds incrementally to the resulting -/// `Vec` addresses from the start to the end of the unwrapped `output_range`. -fn get_pub_memory_addrs( - output_range: Option<&Range>, - public_input: &PublicInputs, -) -> Vec> { - let public_memory_len = public_input.public_memory.len() as u64; - - if let Some(output_range) = output_range { - let output_section = output_range.end - output_range.start; - let program_section = public_memory_len - output_section; - - (1..=program_section) - .map(FieldElement::from) - .chain(output_range.clone().map(FieldElement::from)) - .collect() - } else { - (1..=public_memory_len).map(FieldElement::from).collect() - } -} - fn sort_columns_by_memory_address( adresses: Vec, values: Vec, @@ -597,6 +615,7 @@ fn generate_range_check_permutation_argument_column( impl AIR for CairoAIR { type Field = Stark252PrimeField; + type FieldExtension = Stark252PrimeField; type RAPChallenges = CairoRAPChallenges; type PublicInputs = PublicInputs; @@ -768,9 +787,9 @@ impl AIR for CairoAIR { 23 } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, ) -> Vec> { @@ -800,15 +819,17 @@ impl AIR for CairoAIR { &self, rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let initial_pc = BoundaryConstraint::new(MEM_A_TRACE_OFFSET, 0, self.pub_inputs.pc_init); - let initial_ap = BoundaryConstraint::new(MEM_P_TRACE_OFFSET, 0, self.pub_inputs.ap_init); + let initial_pc = + BoundaryConstraint::new_main(MEM_A_TRACE_OFFSET, 0, self.pub_inputs.pc_init); + let initial_ap = + BoundaryConstraint::new_main(MEM_P_TRACE_OFFSET, 0, self.pub_inputs.ap_init); - let final_pc = BoundaryConstraint::new( + let final_pc = BoundaryConstraint::new_main( MEM_A_TRACE_OFFSET, self.pub_inputs.num_steps - 1, self.pub_inputs.pc_final, ); - let final_ap = BoundaryConstraint::new( + let final_ap = BoundaryConstraint::new_main( MEM_P_TRACE_OFFSET, self.pub_inputs.num_steps - 1, self.pub_inputs.ap_final, @@ -834,19 +855,19 @@ impl AIR for CairoAIR { * cumulative_product; let permutation_final_constraint = - BoundaryConstraint::new(PERMUTATION_ARGUMENT_COL_4, final_index, permutation_final); + BoundaryConstraint::new_aux(PERMUTATION_ARGUMENT_COL_4, final_index, permutation_final); let one: FieldElement = FieldElement::one(); let range_check_final_constraint = - BoundaryConstraint::new(PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4, final_index, one); + BoundaryConstraint::new_aux(PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4, final_index, one); - let range_check_min = BoundaryConstraint::new( + let range_check_min = BoundaryConstraint::new_aux( RANGE_CHECK_COL_1, 0, FieldElement::from(self.pub_inputs.range_check_min.unwrap() as u64), ); - let range_check_max = BoundaryConstraint::new( + let range_check_max = BoundaryConstraint::new_aux( RANGE_CHECK_COL_4, final_index, FieldElement::from(self.pub_inputs.range_check_max.unwrap() as u64), @@ -881,17 +902,29 @@ impl AIR for CairoAIR { fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } /// From the Cairo whitepaper, section 9.10 -fn compute_instr_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn compute_instr_constraints( + constraints: &mut [Felt252], + frame: &Frame, +) { // These constraints are only applied over elements of the same row. let curr = frame.get_evaluation_step(0); // Bit-prefixes constraints. // See section 9.4 of Cairo whitepaper https://eprint.iacr.org/2021/1063.pdf. let flags: Vec<&Felt252> = (0..16) - .map(|col_idx| curr.get_evaluation_element(0, col_idx)) + .map(|col_idx| curr.get_main_evaluation_element(0, col_idx)) .collect(); let one = Felt252::one(); @@ -946,10 +979,10 @@ fn compute_instr_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn compute_operand_constraints( + constraints: &mut [Felt252], + frame: &Frame, +) { // These constraints are only applied over elements of the same row. let curr = frame.get_evaluation_step(0); - let ap = curr.get_evaluation_element(0, FRAME_AP); - let fp = curr.get_evaluation_element(0, FRAME_FP); - let pc = curr.get_evaluation_element(0, FRAME_PC); + let ap = curr.get_main_evaluation_element(0, FRAME_AP); + let fp = curr.get_main_evaluation_element(0, FRAME_FP); + let pc = curr.get_main_evaluation_element(0, FRAME_PC); let dst_fp = into_bit_flag(curr, F_DST_FP); - let off_dst = curr.get_evaluation_element(0, OFF_DST); - let dst_addr = curr.get_evaluation_element(0, FRAME_DST_ADDR); + let off_dst = curr.get_main_evaluation_element(0, OFF_DST); + let dst_addr = curr.get_main_evaluation_element(0, FRAME_DST_ADDR); let op0_fp = into_bit_flag(curr, F_OP_0_FP); - let off_op0 = curr.get_evaluation_element(0, OFF_OP0); - let op0_addr = curr.get_evaluation_element(0, FRAME_OP0_ADDR); + let off_op0 = curr.get_main_evaluation_element(0, OFF_OP0); + let op0_addr = curr.get_main_evaluation_element(0, FRAME_OP0_ADDR); let op1_val = into_bit_flag(curr, F_OP_1_VAL); let op1_ap = into_bit_flag(curr, F_OP_1_AP); let op1_fp = into_bit_flag(curr, F_OP_1_FP); - let op0 = curr.get_evaluation_element(0, FRAME_OP0); - let off_op1 = curr.get_evaluation_element(0, OFF_OP1); - let op1_addr = curr.get_evaluation_element(0, FRAME_OP1_ADDR); + let op0 = curr.get_main_evaluation_element(0, FRAME_OP0); + let off_op1 = curr.get_main_evaluation_element(0, OFF_OP1); + let op1_addr = curr.get_main_evaluation_element(0, FRAME_OP1_ADDR); let one = Felt252::one(); let b15 = Felt252::from(2).pow(15u32); @@ -1011,37 +1047,43 @@ fn compute_operand_constraints(constraints: &mut [Felt252], frame: &Frame, element_idx: usize) -> Felt252 { - step.get_evaluation_element(0, element_idx) - - Felt252::from(2) * step.get_evaluation_element(0, element_idx + 1) +fn into_bit_flag( + step: &StepView, + element_idx: usize, +) -> Felt252 { + step.get_main_evaluation_element(0, element_idx) + - Felt252::from(2) * step.get_main_evaluation_element(0, element_idx + 1) } -fn compute_register_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn compute_register_constraints( + constraints: &mut [Felt252], + frame: &Frame, +) { let curr = frame.get_evaluation_step(0); let next = frame.get_evaluation_step(1); let one = Felt252::one(); let two = Felt252::from(2); - let ap = curr.get_evaluation_element(0, FRAME_AP); - let next_ap = next.get_evaluation_element(0, FRAME_AP); + let ap = curr.get_main_evaluation_element(0, FRAME_AP); + let next_ap = next.get_main_evaluation_element(0, FRAME_AP); let ap_add = into_bit_flag(curr, F_AP_ADD); - let res = curr.get_evaluation_element(0, FRAME_RES); + let res = curr.get_main_evaluation_element(0, FRAME_RES); let ap_one = into_bit_flag(curr, F_AP_ONE); let opc_ret = into_bit_flag(curr, F_OPC_RET); let opc_call = into_bit_flag(curr, F_OPC_CALL); - let dst = curr.get_evaluation_element(0, FRAME_DST); - let fp = curr.get_evaluation_element(0, FRAME_FP); - let next_fp = next.get_evaluation_element(0, FRAME_FP); + let dst = curr.get_main_evaluation_element(0, FRAME_DST); + let fp = curr.get_main_evaluation_element(0, FRAME_FP); + let next_fp = next.get_main_evaluation_element(0, FRAME_FP); - let t1 = curr.get_evaluation_element(0, FRAME_T1); + let t1 = curr.get_main_evaluation_element(0, FRAME_T1); let pc_jnz = into_bit_flag(curr, F_PC_JNZ); - let pc = curr.get_evaluation_element(0, FRAME_PC); - let next_pc = next.get_evaluation_element(0, FRAME_PC); + let pc = curr.get_main_evaluation_element(0, FRAME_PC); + let next_pc = next.get_main_evaluation_element(0, FRAME_PC); - let t0 = curr.get_evaluation_element(0, FRAME_T0); - let op1 = curr.get_evaluation_element(0, FRAME_OP1); + let t0 = curr.get_main_evaluation_element(0, FRAME_T0); + let op1 = curr.get_main_evaluation_element(0, FRAME_OP1); let pc_abs = into_bit_flag(curr, F_PC_ABS); let pc_rel = into_bit_flag(curr, F_PC_REL); @@ -1063,23 +1105,26 @@ fn compute_register_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn compute_opcode_constraints( + constraints: &mut [Felt252], + frame: &Frame, +) { let curr = frame.get_evaluation_step(0); let one = Felt252::one(); - let mul = curr.get_evaluation_element(0, FRAME_MUL); - let op0 = curr.get_evaluation_element(0, FRAME_OP0); - let op1 = curr.get_evaluation_element(0, FRAME_OP1); + let mul = curr.get_main_evaluation_element(0, FRAME_MUL); + let op0 = curr.get_main_evaluation_element(0, FRAME_OP0); + let op1 = curr.get_main_evaluation_element(0, FRAME_OP1); let res_add = into_bit_flag(curr, F_RES_ADD); let res_mul = into_bit_flag(curr, F_RES_MUL); let pc_jnz = into_bit_flag(curr, F_PC_JNZ); - let res = curr.get_evaluation_element(0, FRAME_RES); + let res = curr.get_main_evaluation_element(0, FRAME_RES); let opc_call = into_bit_flag(curr, F_OPC_CALL); - let dst = curr.get_evaluation_element(0, FRAME_DST); - let fp = curr.get_evaluation_element(0, FRAME_FP); - let pc = curr.get_evaluation_element(0, FRAME_PC); + let dst = curr.get_main_evaluation_element(0, FRAME_DST); + let fp = curr.get_main_evaluation_element(0, FRAME_FP); + let pc = curr.get_main_evaluation_element(0, FRAME_PC); let opc_aeq = into_bit_flag(curr, F_OPC_AEQ); @@ -1096,24 +1141,27 @@ fn compute_opcode_constraints(constraints: &mut [Felt252], frame: &Frame) { +fn memory_is_increasing( + constraints: &mut [Felt252], + frame: &Frame, +) { let curr = frame.get_evaluation_step(0); let next = frame.get_evaluation_step(1); let one = FieldElement::one(); - let mem_addr_sorted_0 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_0); - let mem_addr_sorted_1 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_1); - let mem_addr_sorted_2 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_2); - let mem_addr_sorted_3 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_3); - let mem_addr_sorted_4 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_4); - let next_mem_addr_sorted_0 = next.get_evaluation_element(0, MEMORY_ADDR_SORTED_0); + let mem_addr_sorted_0 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_0); + let mem_addr_sorted_1 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_1); + let mem_addr_sorted_2 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_2); + let mem_addr_sorted_3 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_3); + let mem_addr_sorted_4 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_4); + let next_mem_addr_sorted_0 = next.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_0); - let mem_val_sorted_0 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_0); - let mem_val_sorted_1 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_1); - let mem_val_sorted_2 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_2); - let mem_val_sorted_3 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_3); - let mem_val_sorted_4 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_4); - let next_mem_val_sorted_0 = next.get_evaluation_element(0, MEMORY_VALUES_SORTED_0); + let mem_val_sorted_0 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_0); + let mem_val_sorted_1 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_1); + let mem_val_sorted_2 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_2); + let mem_val_sorted_3 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_3); + let mem_val_sorted_4 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_4); + let next_mem_val_sorted_0 = next.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_0); constraints[MEMORY_INCREASING_0] = (mem_addr_sorted_0 - mem_addr_sorted_1) * (mem_addr_sorted_1 - mem_addr_sorted_0 - one); @@ -1148,7 +1196,7 @@ fn memory_is_increasing(constraints: &mut [Felt252], frame: &Frame, + frame: &Frame, rap_challenges: &CairoRAPChallenges, ) { let curr = frame.get_evaluation_step(0); @@ -1157,36 +1205,36 @@ fn permutation_argument( let z = &rap_challenges.z_memory; let alpha = &rap_challenges.alpha_memory; - let p0 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_0); - let next_p0 = next.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_0); - let p1 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_1); - let p2 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_2); - let p3 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_3); - let p4 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_COL_4); - - let next_ap0 = next.get_evaluation_element(0, MEMORY_ADDR_SORTED_0); - let ap1 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_1); - let ap2 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_2); - let ap3 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_3); - let ap4 = curr.get_evaluation_element(0, MEMORY_ADDR_SORTED_4); - - let next_vp0 = next.get_evaluation_element(0, MEMORY_VALUES_SORTED_0); - let vp1 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_1); - let vp2 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_2); - let vp3 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_3); - let vp4 = curr.get_evaluation_element(0, MEMORY_VALUES_SORTED_4); - - let next_a0 = next.get_evaluation_element(0, FRAME_PC); - let a1 = curr.get_evaluation_element(0, FRAME_DST_ADDR); - let a2 = curr.get_evaluation_element(0, FRAME_OP0_ADDR); - let a3 = curr.get_evaluation_element(0, FRAME_OP1_ADDR); - let a4 = curr.get_evaluation_element(0, EXTRA_ADDR); - - let next_v0 = next.get_evaluation_element(0, FRAME_INST); - let v1 = curr.get_evaluation_element(0, FRAME_DST); - let v2 = curr.get_evaluation_element(0, FRAME_OP0); - let v3 = curr.get_evaluation_element(0, FRAME_OP1); - let v4 = curr.get_evaluation_element(0, EXTRA_VAL); + let p0 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_0); + let next_p0 = next.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_0); + let p1 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_1); + let p2 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_2); + let p3 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_3); + let p4 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_COL_4); + + let next_ap0 = next.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_0); + let ap1 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_1); + let ap2 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_2); + let ap3 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_3); + let ap4 = curr.get_aux_evaluation_element(0, MEMORY_ADDR_SORTED_4); + + let next_vp0 = next.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_0); + let vp1 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_1); + let vp2 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_2); + let vp3 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_3); + let vp4 = curr.get_aux_evaluation_element(0, MEMORY_VALUES_SORTED_4); + + let next_a0 = next.get_main_evaluation_element(0, FRAME_PC); + let a1 = curr.get_main_evaluation_element(0, FRAME_DST_ADDR); + let a2 = curr.get_main_evaluation_element(0, FRAME_OP0_ADDR); + let a3 = curr.get_main_evaluation_element(0, FRAME_OP1_ADDR); + let a4 = curr.get_main_evaluation_element(0, EXTRA_ADDR); + + let next_v0 = next.get_main_evaluation_element(0, FRAME_INST); + let v1 = curr.get_main_evaluation_element(0, FRAME_DST); + let v2 = curr.get_main_evaluation_element(0, FRAME_OP0); + let v3 = curr.get_main_evaluation_element(0, FRAME_OP1); + let v4 = curr.get_main_evaluation_element(0, EXTRA_VAL); constraints[PERMUTATION_ARGUMENT_0] = (z - (ap1 + alpha * vp1)) * p1 - (z - (a1 + alpha * v1)) * p0; @@ -1202,7 +1250,7 @@ fn permutation_argument( fn permutation_argument_range_check( constraints: &mut [Felt252], - frame: &Frame, + frame: &Frame, rap_challenges: &CairoRAPChallenges, ) { let curr = frame.get_evaluation_step(0); @@ -1210,11 +1258,11 @@ fn permutation_argument_range_check( let one = FieldElement::one(); let z = &rap_challenges.z_range_check; - let rc_col_1 = curr.get_evaluation_element(0, RANGE_CHECK_COL_1); - let rc_col_2 = curr.get_evaluation_element(0, RANGE_CHECK_COL_2); - let rc_col_3 = curr.get_evaluation_element(0, RANGE_CHECK_COL_3); - let rc_col_4 = curr.get_evaluation_element(0, RANGE_CHECK_COL_4); - let next_rc_col_1 = next.get_evaluation_element(0, RANGE_CHECK_COL_1); + let rc_col_1 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_1); + let rc_col_2 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_2); + let rc_col_3 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_3); + let rc_col_4 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_4); + let next_rc_col_1 = next.get_aux_evaluation_element(0, RANGE_CHECK_COL_1); constraints[RANGE_CHECK_INCREASING_0] = (rc_col_1 - rc_col_2) * (rc_col_2 - rc_col_1 - one); constraints[RANGE_CHECK_INCREASING_1] = (rc_col_2 - rc_col_3) * (rc_col_3 - rc_col_2 - one); @@ -1222,21 +1270,21 @@ fn permutation_argument_range_check( constraints[RANGE_CHECK_INCREASING_3] = (rc_col_4 - next_rc_col_1) * (next_rc_col_1 - rc_col_4 - one); - let p0 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1); - let next_p0 = next.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1); - let p1 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_2); - let p2 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_3); - let p3 = curr.get_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4); + let p0 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1); + let next_p0 = next.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_1); + let p1 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_2); + let p2 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_3); + let p3 = curr.get_aux_evaluation_element(0, PERMUTATION_ARGUMENT_RANGE_CHECK_COL_4); - let next_ap0 = next.get_evaluation_element(0, RANGE_CHECK_COL_1); - let ap1 = curr.get_evaluation_element(0, RANGE_CHECK_COL_2); - let ap2 = curr.get_evaluation_element(0, RANGE_CHECK_COL_3); - let ap3 = curr.get_evaluation_element(0, RANGE_CHECK_COL_4); + let next_ap0 = next.get_aux_evaluation_element(0, RANGE_CHECK_COL_1); + let ap1 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_2); + let ap2 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_3); + let ap3 = curr.get_aux_evaluation_element(0, RANGE_CHECK_COL_4); - let a0_next = next.get_evaluation_element(0, OFF_DST); - let a1 = curr.get_evaluation_element(0, OFF_OP0); - let a2 = curr.get_evaluation_element(0, OFF_OP1); - let a3 = curr.get_evaluation_element(0, RC_HOLES); + let a0_next = next.get_main_evaluation_element(0, OFF_DST); + let a1 = curr.get_main_evaluation_element(0, OFF_OP0); + let a2 = curr.get_main_evaluation_element(0, OFF_OP1); + let a3 = curr.get_main_evaluation_element(0, RC_HOLES); constraints[RANGE_CHECK_0] = (z - ap1) * p1 - (z - a1) * p0; constraints[RANGE_CHECK_1] = (z - ap2) * p2 - (z - a2) * p1; @@ -1244,7 +1292,7 @@ fn permutation_argument_range_check( constraints[RANGE_CHECK_3] = (z - next_ap0) * next_p0 - (z - a0_next) * p3; } -fn frame_inst_size(step: &StepView) -> Felt252 { +fn frame_inst_size(step: &StepView) -> Felt252 { let op1_val = into_bit_flag(step, F_OP_1_VAL); op1_val + Felt252::one() } @@ -1256,8 +1304,8 @@ pub fn generate_cairo_proof( trace: &TraceTable, pub_input: &PublicInputs, proof_options: &ProofOptions, -) -> Result, ProvingError> { - Prover::prove::( +) -> Result, ProvingError> { + Prover::::prove( trace, pub_input, proof_options, @@ -1269,11 +1317,11 @@ pub fn generate_cairo_proof( /// concrete types. /// The field is set to Stark252PrimeField and the AIR to CairoAIR. pub fn verify_cairo_proof( - proof: &StarkProof, + proof: &StarkProof, pub_input: &PublicInputs, proof_options: &ProofOptions, ) -> bool { - Verifier::verify::( + Verifier::::verify( proof, pub_input, proof_options, @@ -1287,181 +1335,6 @@ mod test { use super::*; use lambdaworks_math::field::element::FieldElement; - #[test] - fn test_build_auxiliary_trace_add_program_in_public_input_section_works() { - let dummy_public_input = PublicInputs { - pc_init: FieldElement::zero(), - ap_init: FieldElement::zero(), - fp_init: FieldElement::zero(), - pc_final: FieldElement::zero(), - ap_final: FieldElement::zero(), - public_memory: HashMap::from([ - (FieldElement::one(), FieldElement::from(10)), - (FieldElement::from(2), FieldElement::from(20)), - ]), - range_check_max: None, - range_check_min: None, - num_steps: 1, - memory_segments: MemorySegmentMap::new(), - codelen: 3, - }; - - let a = vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::zero(), - ]; - let v = vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ]; - let (ap, vp) = add_pub_memory_in_public_input_section(&a, &v, &dummy_public_input); - assert_eq!( - ap, - vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - FieldElement::from(2), - ] - ); - assert_eq!( - vp, - vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(10), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(20), - ] - ); - } - - #[test] - fn test_build_auxiliary_trace_add_program_with_output_in_public_input_section_works() { - let dummy_public_input = PublicInputs { - pc_init: FieldElement::zero(), - ap_init: FieldElement::zero(), - fp_init: FieldElement::zero(), - pc_final: FieldElement::zero(), - ap_final: FieldElement::zero(), - public_memory: HashMap::from([ - (FieldElement::one(), FieldElement::from(10)), - (FieldElement::from(2), FieldElement::from(20)), - (FieldElement::from(20), FieldElement::from(40)), - ]), - range_check_max: None, - range_check_min: None, - num_steps: 1, - memory_segments: MemorySegmentMap::from([(MemorySegment::Output, 20..21)]), - codelen: 3, - }; - - let a = vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - ]; - - let v = vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ]; - - let (ap, vp) = add_pub_memory_in_public_input_section(&a, &v, &dummy_public_input); - assert_eq!( - ap, - vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::from(2), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::one(), - FieldElement::from(20), - ] - ); - assert_eq!( - vp, - vec![ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(10), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(20), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(40), - ] - ); - } - #[test] fn test_build_auxiliary_trace_sort_columns_by_memory_address() { let a = vec![ @@ -1560,7 +1433,7 @@ mod prop_test { Felt252, }; - use super::{MemorySegment, MemorySegmentMap, PublicInputs}; + use super::{MemorySegmentMap, PublicInputs, Segment, SegmentName}; prop_compose! { fn some_felt()(base in any::(), exponent in any::()) -> Felt252 { @@ -1579,10 +1452,9 @@ mod prop_test { range_check_max in proptest::option::of(any::()), range_check_min in proptest::option::of(any::()), num_steps in any::(), - codelen in any::(), ) -> PublicInputs { let public_memory = public_memory.iter().map(|(k, v)| (Felt252::from(*k), Felt252::from(*v))).collect(); - let memory_segments = MemorySegmentMap::from([(MemorySegment::Output, 10u64..16u64), (MemorySegment::RangeCheck, 20u64..71u64)]); + let memory_segments = MemorySegmentMap::from([(SegmentName::Output, Segment::new(10u64, 16u64)), (SegmentName::RangeCheck, Segment::new(20u64, 71u64))]); PublicInputs { pc_init, ap_init, @@ -1594,7 +1466,6 @@ mod prop_test { range_check_min, num_steps, memory_segments, - codelen, } } } @@ -1639,7 +1510,8 @@ mod prop_test { // At this point, the verifier only knows about the serialized proof, the proof options // and the public inputs. - let proof: StarkProof = serde_cbor::from_slice(&proof_bytes).unwrap(); + let proof: StarkProof = + serde_cbor::from_slice(&proof_bytes).unwrap(); // The proof is verified successfully. assert!(verify_cairo_proof(&proof, &pub_inputs, &proof_options)); diff --git a/provers/cairo/src/execution_trace.rs b/provers/cairo/src/execution_trace.rs index c67eb7cfff..075aa86d70 100644 --- a/provers/cairo/src/execution_trace.rs +++ b/provers/cairo/src/execution_trace.rs @@ -9,13 +9,15 @@ use super::{ }, register_states::RegisterStates, }; +use crate::air::{EXTRA_ADDR, RC_HOLES}; use crate::{ air::{ - PublicInputs, EXTRA_ADDR, FRAME_DST_ADDR, FRAME_OP0_ADDR, FRAME_OP1_ADDR, FRAME_PC, - OFF_DST, OFF_OP0, OFF_OP1, RC_HOLES, + PublicInputs, FRAME_DST_ADDR, FRAME_OP0_ADDR, FRAME_OP1_ADDR, FRAME_PC, OFF_DST, OFF_OP0, + OFF_OP1, }, Felt252, }; +use cairo_vm::without_std::collections::HashMap; use lambdaworks_math::{ field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, unsigned_integer::element::UnsignedInteger, @@ -55,11 +57,15 @@ pub fn build_main_trace( address_cols.sort_by_key(|x| x.representative()); let (rc_holes, rc_min, rc_max) = get_rc_holes(&main_trace, &[OFF_DST, OFF_OP0, OFF_OP1]); - public_input.range_check_min = Some(rc_min); - public_input.range_check_max = Some(rc_max); + + // this will avaluate to true if the public inputs weren't obtained from the run_program() function + if public_input.range_check_min.is_none() && public_input.range_check_max.is_none() { + public_input.range_check_min = Some(rc_min); + public_input.range_check_max = Some(rc_max); + } fill_rc_holes(&mut main_trace, &rc_holes); - let memory_holes = get_memory_holes(&address_cols, public_input.codelen); + let memory_holes = get_memory_holes(&address_cols, &public_input.public_memory); if !memory_holes.is_empty() { fill_memory_holes(&mut main_trace, &memory_holes); @@ -152,8 +158,11 @@ fn fill_rc_holes(trace: &mut CairoTraceTable, holes: &[Felt252]) { /// # Arguments /// /// * `sorted_addrs` - Vector of sorted memory addresses. -/// * `codelen` - the length of the Cairo program instructions. -fn get_memory_holes(sorted_addrs: &[Felt252], codelen: usize) -> Vec { +/// * `pub_memory` - The public memory of the executed program. +fn get_memory_holes( + sorted_addrs: &[Felt252], + pub_memory: &HashMap, +) -> Vec { let mut memory_holes = Vec::new(); let mut prev_addr = &sorted_addrs[0]; @@ -163,14 +172,11 @@ fn get_memory_holes(sorted_addrs: &[Felt252], codelen: usize) -> Vec { // If the candidate memory hole has an address belonging to the program segment (public // memory), that is not accounted here since public memory is added in a posterior step of // the protocol. - if addr_diff != Felt252::one() - && addr_diff != Felt252::zero() - && addr.representative() > (codelen as u64).into() - { + if addr_diff != Felt252::one() && addr_diff != Felt252::zero() { let mut hole_addr = prev_addr + Felt252::one(); while hole_addr.representative() < addr.representative() { - if hole_addr.representative() > (codelen as u64).into() { + if !pub_memory.contains_key(&hole_addr) { memory_holes.push(hole_addr); } hole_addr += Felt252::one(); @@ -650,16 +656,16 @@ mod test { } #[test] - fn test_get_memory_holes_no_codelen() { + fn test_get_memory_holes_empty_pub_memory() { // We construct a sorted addresses list [1, 2, 3, 6, 7, 8, 9, 13, 14, 15], and - // set codelen = 0. With this value of codelen, any holes present between + // an empty public memory. This way, any holes present between // the min and max addresses should be returned by the function. let mut addrs: Vec = (1..4).map(Felt252::from).collect(); let addrs_extension: Vec = (6..10).map(Felt252::from).collect(); addrs.extend_from_slice(&addrs_extension); let addrs_extension: Vec = (13..16).map(Felt252::from).collect(); addrs.extend_from_slice(&addrs_extension); - let codelen = 0; + let pub_memory = HashMap::new(); let expected_memory_holes = vec![ Felt252::from(4), @@ -668,7 +674,7 @@ mod test { Felt252::from(11), Felt252::from(12), ]; - let calculated_memory_holes = get_memory_holes(&addrs, codelen); + let calculated_memory_holes = get_memory_holes(&addrs, &pub_memory); assert_eq!(expected_memory_holes, calculated_memory_holes); } @@ -676,15 +682,20 @@ mod test { #[test] fn test_get_memory_holes_inside_program_section() { // We construct a sorted addresses list [1, 2, 3, 8, 9] and we - // set a codelen of 9. Since all the holes will be inside the + // set public memory from address 1 to 9. Since all the holes will be inside the // program segment (meaning from addresses 1 to 9), the function // should not return any of them. let mut addrs: Vec = (1..4).map(Felt252::from).collect(); let addrs_extension: Vec = (8..10).map(Felt252::from).collect(); addrs.extend_from_slice(&addrs_extension); - let codelen = 9; - let calculated_memory_holes = get_memory_holes(&addrs, codelen); + let mut pub_memory = HashMap::new(); + (1..=9).for_each(|k| { + let addr = Felt252::from(k); + pub_memory.insert(addr, addr * Felt252::from(2)); + }); + + let calculated_memory_holes = get_memory_holes(&addrs, &pub_memory); let expected_memory_holes: Vec = Vec::new(); assert_eq!(expected_memory_holes, calculated_memory_holes); @@ -693,15 +704,20 @@ mod test { #[test] fn test_get_memory_holes_outside_program_section() { // We construct a sorted addresses list [1, 2, 3, 8, 9] and we - // set a codelen of 6. The holes found inside the program section, + // set public memory from addresses 1 to 6. The holes found inside the program section, // i.e. in the address range between 1 to 6, should not be returned. // So addresses 4, 5 and 6 will no be returned, only address 7. let mut addrs: Vec = (1..4).map(Felt252::from).collect(); let addrs_extension: Vec = (8..10).map(Felt252::from).collect(); addrs.extend_from_slice(&addrs_extension); - let codelen = 6; - let calculated_memory_holes = get_memory_holes(&addrs, codelen); + let mut pub_memory = HashMap::new(); + (1..=6).for_each(|k| { + let addr = Felt252::from(k); + pub_memory.insert(addr, addr * Felt252::from(2)); + }); + + let calculated_memory_holes = get_memory_holes(&addrs, &pub_memory); let expected_memory_holes = vec![Felt252::from(7)]; assert_eq!(expected_memory_holes, calculated_memory_holes); diff --git a/provers/cairo/src/main.rs b/provers/cairo/src/main.rs index b0ae341b4c..3e1c421823 100644 --- a/provers/cairo/src/main.rs +++ b/provers/cairo/src/main.rs @@ -108,7 +108,10 @@ fn try_compile(program_path: &String, out_file_path: &String) -> Result<(), Erro fn generate_proof( input_path: &String, proof_options: &ProofOptions, -) -> Option<(StarkProof, PublicInputs)> { +) -> Option<( + StarkProof, + PublicInputs, +)> { let timer = Instant::now(); let Ok(program_content) = std::fs::read(input_path) else { @@ -145,7 +148,10 @@ fn generate_proof_from_trace( trace_bin_path: &str, memory_bin_path: &str, proof_options: &ProofOptions, -) -> Option<(StarkProof, PublicInputs)> { +) -> Option<( + StarkProof, + PublicInputs, +)> { // ## Generating the prover args let timer = Instant::now(); let Ok((main_trace, pub_inputs)) = @@ -172,7 +178,7 @@ fn generate_proof_from_trace( } fn verify_proof( - proof: StarkProof, + proof: StarkProof, pub_inputs: PublicInputs, proof_options: &ProofOptions, ) -> bool { @@ -192,7 +198,7 @@ fn verify_proof( } fn write_proof( - proof: StarkProof, + proof: StarkProof, pub_inputs: PublicInputs, proof_path: String, ) { diff --git a/provers/cairo/src/runner/run.rs b/provers/cairo/src/runner/run.rs index 24df6f73f0..45b22b3c0b 100644 --- a/provers/cairo/src/runner/run.rs +++ b/provers/cairo/src/runner/run.rs @@ -1,8 +1,9 @@ -use crate::air::PublicInputs; +use crate::air::{PublicInputs, Segment, SegmentName}; use crate::cairo_layout::CairoLayout; use crate::cairo_mem::CairoMemory; use crate::execution_trace::build_main_trace; use crate::register_states::RegisterStates; +use crate::Felt252; use super::vec_writer::VecWriter; use cairo_vm::cairo_run::{self, EncodeTraceError}; @@ -13,6 +14,7 @@ use cairo_vm::vm::errors::{ cairo_run_errors::CairoRunError, trace_errors::TraceError, vm_errors::VirtualMachineError, }; +use cairo_vm::without_std::collections::HashMap; use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; use stark_platinum_prover::trace::TraceTable; @@ -79,7 +81,7 @@ pub fn run_program( entrypoint_function: Option<&str>, layout: CairoLayout, program_content: &[u8], -) -> Result<(RegisterStates, CairoMemory, usize), Error> { +) -> Result<(RegisterStates, CairoMemory, PublicInputs), Error> { // default value for entrypoint is "main" let entrypoint = entrypoint_function.unwrap_or("main"); @@ -92,6 +94,7 @@ pub fn run_program( layout: layout.as_str(), proof_mode: true, secure_run: None, + disable_trace_padding: false, }; let (runner, vm) = @@ -122,22 +125,46 @@ pub fn run_program( let cairo_mem = CairoMemory::from_bytes_le(&memory_vec).unwrap(); let register_states = RegisterStates::from_bytes_le(&trace_vec).unwrap(); - let data_len = runner.get_program().data_len(); + let vm_pub_inputs = runner.get_air_public_input(&vm).unwrap(); + + let mut pub_memory: HashMap = HashMap::new(); + vm_pub_inputs.public_memory.iter().for_each(|mem_cell| { + let addr = Felt252::from(mem_cell.address as u64); + let value = Felt252::from_hex_unchecked(&mem_cell.value.as_ref().unwrap().to_str_radix(16)); + pub_memory.insert(addr, value); + }); + + let mut memory_segments: HashMap = HashMap::new(); + vm_pub_inputs.memory_segments.iter().for_each(|(k, v)| { + memory_segments.insert(SegmentName::from(*k), Segment::from(v)); + }); + + let num_steps = register_states.steps(); + let public_inputs = PublicInputs { + pc_init: Felt252::from(register_states.rows[0].pc), + ap_init: Felt252::from(register_states.rows[0].ap), + fp_init: Felt252::from(register_states.rows[0].fp), + pc_final: Felt252::from(register_states.rows[num_steps - 1].pc), + ap_final: Felt252::from(register_states.rows[num_steps - 1].ap), + range_check_min: Some(vm_pub_inputs.rc_min as u16), + range_check_max: Some(vm_pub_inputs.rc_max as u16), + memory_segments, + public_memory: pub_memory, + num_steps, + }; - Ok((register_states, cairo_mem, data_len)) + Ok((register_states, cairo_mem, public_inputs)) } pub fn generate_prover_args( program_content: &[u8], layout: CairoLayout, ) -> Result<(TraceTable, PublicInputs), Error> { - let (register_states, memory, program_size) = run_program(None, layout, program_content)?; - - let mut pub_inputs = PublicInputs::from_regs_and_mem(®ister_states, &memory, program_size); + let (register_states, memory, mut public_inputs) = run_program(None, layout, program_content)?; - let main_trace = build_main_trace(®ister_states, &memory, &mut pub_inputs); + let main_trace = build_main_trace(®ister_states, &memory, &mut public_inputs); - Ok((main_trace, pub_inputs)) + Ok((main_trace, public_inputs)) } pub fn generate_prover_args_from_trace( diff --git a/provers/cairo/src/tests/integration_tests.rs b/provers/cairo/src/tests/integration_tests.rs index 0744c22314..79f051830e 100644 --- a/provers/cairo/src/tests/integration_tests.rs +++ b/provers/cairo/src/tests/integration_tests.rs @@ -52,16 +52,19 @@ fn test_verifier_rejects_wrong_authentication_paths() { // Change order of authentication path hashes let query = 0; - let merkle_tree = 0; - let mut original_path = proof.deep_poly_openings[query].lde_trace_merkle_proofs[merkle_tree] + let mut original_path = proof.deep_poly_openings[query] + .main_trace_polys + .proof .merkle_path .clone(); original_path.swap(0, 1); // For the test to make sense, we have to make sure // that the two hashes are different. assert_ne!(original_path[0], original_path[1]); - proof.deep_poly_openings[query].lde_trace_merkle_proofs[merkle_tree].merkle_path = - original_path; + proof.deep_poly_openings[query] + .main_trace_polys + .proof + .merkle_path = original_path; // Verifier should reject the proof assert!(!verify_cairo_proof(&proof, &pub_inputs, &proof_options)); @@ -141,7 +144,7 @@ fn check_simple_cairo_trace_evaluates_to_zero() { let program_content = std::fs::read(cairo0_program_path("simple_program.json")).unwrap(); let (main_trace, public_input) = generate_prover_args(&program_content, CairoLayout::Plain).unwrap(); - let mut trace_polys = main_trace.compute_trace_polys(); + let mut trace_polys = main_trace.compute_trace_polys::(); let mut transcript = StoneProverTranscript::new(&[]); let proof_options = ProofOptions::default_test_options(); @@ -149,7 +152,7 @@ fn check_simple_cairo_trace_evaluates_to_zero() { let rap_challenges = cairo_air.build_rap_challenges(&mut transcript); let aux_trace = cairo_air.build_auxiliary_trace(&main_trace, &rap_challenges); - let aux_polys = aux_trace.compute_trace_polys(); + let aux_polys = aux_trace.compute_trace_polys::(); trace_polys.extend_from_slice(&aux_polys); @@ -158,6 +161,7 @@ fn check_simple_cairo_trace_evaluates_to_zero() { assert!(validate_trace( &cairo_air, &trace_polys, + &aux_polys, &domain, &rap_challenges )); @@ -182,7 +186,8 @@ fn deserialize_and_verify() { // At this point, the verifier only knows about the serialized proof, the proof options // and the public inputs. - let proof: StarkProof = serde_cbor::from_slice(&proof_bytes).unwrap(); + let proof: StarkProof = + serde_cbor::from_slice(&proof_bytes).unwrap(); // The proof is verified successfully. assert!(verify_cairo_proof(&proof, &pub_inputs, &proof_options)); diff --git a/provers/cairo/src/wasm_wrappers.rs b/provers/cairo/src/wasm_wrappers.rs index de785e3a33..bb850fdc75 100644 --- a/provers/cairo/src/wasm_wrappers.rs +++ b/provers/cairo/src/wasm_wrappers.rs @@ -10,7 +10,7 @@ use std::collections::HashMap; use wasm_bindgen::prelude::wasm_bindgen; #[wasm_bindgen] -pub struct Stark252PrimeFieldProof(StarkProof); +pub struct Stark252PrimeFieldProof(StarkProof); #[wasm_bindgen] #[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash)] @@ -45,7 +45,7 @@ pub fn verify_cairo_proof_wasm(proof_bytes: &[u8], proof_options: &ProofOptions) return false; }; - Verifier::verify::( + Verifier::::verify( &proof, &pub_inputs, proof_options, diff --git a/provers/cairo/tests/wasm.rs b/provers/cairo/tests/wasm.rs index 909620ea68..0fd44a5e9f 100644 --- a/provers/cairo/tests/wasm.rs +++ b/provers/cairo/tests/wasm.rs @@ -19,1205 +19,1207 @@ fn test_prove_cairo1_program_wasm() { // Test case is fibo5, with default test options #[cfg(feature = "wasm")] -static PROOF: [u8; 25396] = [ - 195, 90, 0, 0, 64, 2, 252, 245, 204, 166, 166, 99, 192, 158, 197, 112, 127, 229, 91, 155, 221, - 204, 192, 231, 39, 132, 16, 113, 234, 72, 205, 118, 5, 214, 64, 133, 109, 72, 221, 209, 118, - 219, 43, 25, 209, 138, 192, 61, 240, 47, 23, 58, 20, 98, 32, 220, 185, 74, 100, 168, 105, 233, - 128, 164, 181, 241, 136, 114, 32, 118, 118, 32, 1, 69, 175, 164, 230, 156, 45, 26, 157, 26, - 149, 15, 8, 187, 118, 184, 79, 28, 9, 128, 92, 187, 64, 32, 106, 47, 176, 82, 200, 128, 34, - 224, 32, 5, 6, 92, 203, 162, 164, 163, 162, 68, 16, 0, 82, 11, 135, 230, 228, 7, 226, 171, 88, - 92, 95, 196, 6, 255, 18, 101, 66, 114, 123, 229, 195, 32, 2, 16, 138, 53, 88, 63, 13, 207, 100, - 96, 215, 81, 252, 113, 252, 104, 146, 133, 103, 81, 255, 81, 71, 237, 60, 226, 49, 50, 43, 99, - 201, 28, 32, 4, 17, 93, 58, 198, 31, 191, 184, 221, 129, 28, 32, 181, 119, 68, 155, 9, 145, 17, - 96, 129, 132, 185, 193, 117, 34, 81, 34, 35, 249, 18, 252, 32, 5, 43, 221, 50, 152, 189, 122, - 229, 150, 52, 80, 184, 111, 92, 174, 115, 114, 150, 148, 107, 18, 60, 247, 11, 163, 214, 67, - 193, 177, 218, 87, 190, 32, 4, 105, 167, 227, 252, 176, 234, 185, 120, 85, 181, 60, 107, 207, - 4, 173, 11, 46, 224, 195, 53, 199, 203, 144, 17, 244, 206, 39, 42, 200, 47, 66, 32, 0, 181, - 103, 37, 5, 59, 182, 223, 220, 236, 121, 39, 67, 114, 252, 125, 59, 76, 247, 72, 25, 176, 7, - 148, 65, 41, 243, 220, 21, 207, 164, 117, 32, 4, 90, 179, 146, 130, 157, 219, 120, 110, 118, - 60, 147, 161, 185, 126, 62, 157, 166, 123, 164, 12, 216, 3, 202, 32, 148, 249, 238, 10, 231, - 210, 59, 32, 6, 41, 232, 112, 140, 202, 118, 51, 220, 123, 53, 166, 57, 49, 28, 15, 38, 91, 82, - 198, 90, 129, 41, 61, 131, 232, 94, 55, 48, 14, 125, 77, 32, 4, 194, 222, 149, 32, 43, 20, 102, - 178, 214, 19, 169, 65, 198, 17, 117, 26, 67, 131, 15, 155, 186, 106, 26, 94, 12, 140, 5, 134, - 218, 231, 218, 32, 2, 85, 140, 87, 77, 176, 112, 174, 22, 191, 211, 127, 99, 48, 55, 105, 151, - 207, 3, 10, 37, 61, 81, 188, 143, 3, 180, 142, 143, 9, 213, 218, 32, 4, 98, 89, 68, 108, 101, - 194, 71, 23, 92, 110, 131, 242, 69, 47, 57, 180, 92, 230, 84, 85, 144, 77, 103, 228, 111, 188, - 25, 126, 144, 73, 198, 32, 5, 217, 198, 79, 35, 57, 220, 145, 137, 0, 69, 236, 207, 235, 178, - 184, 77, 12, 119, 13, 56, 244, 30, 218, 17, 70, 236, 98, 26, 145, 76, 89, 32, 3, 95, 135, 88, - 10, 176, 51, 90, 130, 39, 75, 205, 113, 71, 208, 101, 151, 242, 41, 224, 203, 88, 169, 131, 75, - 74, 119, 160, 27, 34, 208, 2, 32, 1, 172, 82, 83, 80, 211, 162, 28, 102, 83, 189, 67, 32, 248, - 69, 34, 163, 129, 41, 228, 185, 193, 124, 26, 25, 67, 29, 16, 56, 43, 252, 48, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 6, 189, - 193, 243, 52, 81, 38, 25, 72, 47, 0, 133, 5, 229, 118, 64, 187, 49, 202, 147, 210, 244, 196, - 91, 175, 180, 125, 21, 87, 52, 81, 17, 32, 1, 118, 240, 91, 187, 247, 137, 62, 204, 160, 34, - 69, 36, 164, 226, 89, 159, 103, 4, 158, 242, 71, 90, 93, 103, 60, 105, 87, 92, 91, 123, 30, 32, - 4, 43, 52, 172, 230, 228, 231, 96, 63, 36, 144, 247, 138, 100, 46, 246, 150, 60, 68, 158, 219, - 36, 214, 103, 6, 239, 247, 7, 52, 24, 218, 9, 32, 3, 163, 22, 47, 83, 153, 215, 94, 53, 57, - 143, 169, 171, 180, 153, 157, 66, 55, 131, 123, 2, 192, 150, 17, 111, 186, 221, 143, 48, 35, - 244, 242, 32, 0, 144, 33, 48, 13, 46, 114, 107, 137, 14, 254, 113, 127, 77, 11, 39, 212, 239, - 245, 171, 41, 79, 51, 137, 181, 189, 229, 11, 168, 157, 251, 107, 32, 6, 51, 197, 14, 31, 202, - 121, 32, 167, 6, 206, 121, 188, 245, 252, 158, 176, 142, 238, 31, 182, 47, 60, 167, 68, 233, - 37, 205, 114, 180, 86, 55, 32, 7, 33, 56, 227, 151, 110, 73, 248, 224, 59, 109, 17, 6, 26, 181, - 171, 21, 142, 226, 63, 66, 255, 227, 80, 55, 35, 72, 114, 99, 107, 177, 93, 32, 7, 104, 89, - 220, 165, 175, 33, 10, 220, 251, 253, 78, 192, 35, 189, 228, 111, 67, 115, 157, 116, 98, 159, - 61, 118, 155, 0, 253, 90, 12, 45, 241, 32, 3, 18, 47, 200, 117, 54, 56, 97, 85, 139, 95, 67, - 221, 137, 8, 202, 15, 118, 202, 38, 64, 246, 80, 193, 62, 112, 39, 124, 124, 144, 104, 250, 32, - 5, 104, 8, 179, 151, 138, 149, 187, 225, 64, 223, 159, 128, 69, 133, 147, 44, 104, 233, 136, - 112, 175, 192, 241, 13, 172, 175, 159, 138, 2, 132, 234, 32, 7, 203, 195, 14, 157, 122, 116, - 235, 253, 61, 252, 129, 53, 255, 76, 116, 207, 26, 125, 222, 236, 191, 197, 166, 104, 107, 2, - 149, 255, 18, 239, 74, 32, 7, 147, 21, 198, 192, 30, 44, 11, 201, 224, 230, 96, 25, 167, 80, - 82, 17, 49, 75, 119, 139, 149, 156, 245, 163, 197, 93, 38, 177, 80, 13, 157, 32, 6, 184, 189, - 253, 114, 33, 247, 141, 107, 8, 7, 78, 107, 66, 116, 202, 145, 242, 219, 174, 196, 30, 81, 64, - 103, 188, 133, 217, 34, 249, 234, 180, 32, 2, 106, 209, 168, 116, 191, 232, 243, 186, 106, 91, - 167, 149, 20, 92, 144, 40, 49, 126, 228, 66, 66, 249, 215, 231, 57, 210, 11, 42, 28, 169, 147, - 32, 0, 78, 167, 203, 82, 40, 9, 47, 24, 125, 93, 240, 31, 187, 240, 164, 203, 206, 62, 96, 46, - 174, 246, 33, 101, 81, 206, 200, 72, 183, 49, 201, 32, 6, 162, 179, 8, 122, 123, 106, 129, 182, - 53, 161, 29, 133, 189, 198, 254, 110, 62, 113, 58, 127, 153, 124, 9, 52, 69, 101, 246, 139, - 164, 12, 247, 32, 2, 19, 82, 4, 150, 144, 210, 229, 125, 20, 176, 92, 95, 24, 12, 245, 76, 4, - 19, 188, 135, 175, 208, 26, 8, 248, 203, 34, 107, 33, 110, 118, 32, 0, 138, 96, 0, 39, 184, - 153, 134, 24, 215, 216, 120, 86, 21, 69, 207, 179, 145, 149, 205, 201, 122, 38, 171, 254, 99, - 79, 22, 171, 63, 7, 139, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, - 225, 32, 3, 214, 98, 213, 77, 223, 166, 113, 182, 177, 112, 202, 142, 120, 123, 210, 135, 65, - 231, 135, 220, 154, 101, 199, 52, 61, 65, 115, 101, 35, 238, 99, 32, 2, 237, 114, 68, 33, 120, - 121, 132, 101, 51, 184, 194, 74, 174, 1, 174, 42, 188, 230, 208, 206, 200, 252, 84, 118, 66, - 184, 50, 103, 137, 41, 129, 32, 2, 237, 114, 68, 33, 120, 121, 132, 101, 51, 184, 194, 74, 174, - 1, 174, 42, 188, 230, 208, 206, 200, 252, 84, 118, 66, 184, 50, 103, 137, 41, 129, 32, 2, 237, - 114, 68, 33, 120, 121, 132, 101, 51, 184, 194, 74, 174, 1, 174, 42, 188, 230, 208, 206, 200, - 252, 84, 118, 66, 184, 50, 103, 137, 41, 129, 32, 1, 226, 215, 61, 226, 138, 117, 212, 67, 55, - 36, 119, 189, 63, 52, 219, 125, 67, 232, 6, 109, 108, 224, 26, 24, 146, 21, 209, 121, 15, 248, - 44, 32, 0, 216, 17, 240, 131, 204, 175, 224, 176, 51, 191, 145, 234, 169, 170, 87, 47, 43, 20, - 84, 220, 133, 146, 169, 41, 125, 222, 242, 152, 108, 151, 95, 32, 3, 57, 147, 234, 249, 175, - 79, 245, 245, 16, 169, 45, 136, 115, 233, 195, 211, 149, 34, 194, 187, 199, 46, 90, 227, 120, - 141, 130, 128, 116, 103, 53, 32, 7, 0, 79, 88, 214, 180, 186, 237, 42, 157, 107, 42, 45, 82, - 99, 141, 140, 126, 4, 109, 84, 185, 23, 69, 119, 115, 28, 24, 237, 136, 136, 241, 32, 3, 254, - 203, 18, 120, 206, 98, 46, 204, 233, 191, 1, 152, 109, 82, 172, 172, 211, 63, 121, 14, 229, 7, - 157, 161, 255, 165, 27, 216, 75, 205, 186, 32, 3, 214, 241, 66, 54, 25, 57, 108, 78, 82, 195, - 134, 49, 188, 92, 61, 13, 157, 93, 84, 158, 24, 199, 65, 172, 179, 216, 196, 16, 113, 29, 105, - 32, 6, 70, 236, 249, 54, 22, 79, 2, 167, 17, 252, 132, 172, 193, 128, 127, 194, 113, 30, 197, - 159, 252, 77, 167, 56, 5, 21, 110, 26, 36, 38, 111, 32, 3, 75, 51, 219, 254, 96, 14, 192, 79, - 152, 164, 58, 183, 95, 77, 254, 10, 118, 248, 198, 242, 87, 96, 52, 208, 93, 197, 182, 103, - 237, 160, 66, 32, 4, 52, 60, 115, 181, 80, 56, 95, 29, 219, 97, 146, 147, 177, 176, 139, 153, - 80, 118, 74, 70, 33, 97, 32, 120, 72, 28, 237, 43, 120, 155, 233, 32, 0, 249, 250, 14, 170, - 185, 222, 147, 79, 238, 104, 251, 77, 90, 166, 150, 185, 116, 18, 203, 11, 213, 68, 250, 195, - 84, 198, 9, 172, 218, 65, 182, 32, 7, 115, 163, 106, 160, 150, 22, 133, 129, 181, 46, 47, 126, - 169, 161, 49, 60, 71, 42, 148, 116, 227, 131, 35, 110, 111, 100, 59, 115, 33, 75, 42, 32, 0, - 58, 58, 221, 103, 10, 95, 45, 209, 245, 41, 188, 148, 227, 124, 160, 141, 25, 194, 197, 236, - 19, 233, 77, 139, 122, 76, 248, 170, 38, 210, 157, 32, 2, 252, 198, 106, 130, 53, 131, 32, 61, - 31, 98, 220, 97, 5, 32, 233, 76, 207, 227, 54, 80, 52, 45, 57, 238, 210, 189, 64, 95, 231, 189, - 248, 32, 2, 13, 52, 92, 65, 79, 152, 137, 74, 116, 221, 34, 151, 75, 117, 203, 252, 151, 128, - 74, 139, 171, 5, 216, 61, 204, 135, 82, 158, 237, 107, 62, 32, 2, 63, 108, 142, 195, 144, 229, - 145, 69, 69, 97, 143, 241, 148, 139, 215, 254, 37, 80, 89, 242, 130, 34, 54, 198, 74, 104, 254, - 62, 119, 44, 40, 32, 0, 238, 209, 167, 184, 109, 75, 19, 28, 176, 178, 28, 103, 113, 68, 83, - 199, 192, 156, 120, 76, 9, 150, 48, 101, 100, 71, 37, 68, 75, 213, 80, 32, 7, 191, 157, 104, - 18, 12, 93, 97, 172, 229, 160, 153, 151, 14, 208, 157, 193, 90, 70, 255, 50, 212, 163, 199, - 205, 32, 159, 208, 177, 157, 244, 108, 32, 2, 220, 213, 133, 63, 179, 204, 62, 190, 103, 62, - 69, 247, 105, 172, 105, 76, 195, 231, 181, 173, 151, 115, 217, 6, 140, 52, 92, 242, 233, 56, - 206, 32, 0, 104, 29, 152, 213, 126, 239, 0, 38, 175, 105, 151, 57, 171, 178, 158, 138, 196, 44, - 50, 199, 79, 145, 20, 91, 195, 201, 178, 168, 208, 74, 66, 32, 5, 41, 154, 12, 120, 155, 85, - 233, 71, 190, 148, 249, 239, 90, 207, 179, 156, 187, 244, 214, 206, 98, 73, 93, 79, 115, 247, - 144, 42, 241, 116, 110, 32, 2, 124, 110, 226, 43, 123, 27, 189, 253, 5, 75, 19, 30, 120, 11, - 20, 188, 190, 47, 112, 84, 176, 50, 214, 77, 237, 206, 26, 31, 14, 124, 57, 32, 4, 200, 126, - 113, 162, 228, 55, 163, 247, 228, 10, 179, 147, 112, 138, 74, 118, 57, 174, 170, 193, 115, 169, - 248, 144, 88, 140, 78, 32, 189, 103, 119, 32, 6, 0, 103, 143, 116, 149, 145, 221, 249, 53, 131, - 199, 38, 12, 172, 230, 147, 202, 66, 169, 168, 125, 138, 207, 63, 176, 238, 193, 182, 167, 76, - 138, 32, 7, 186, 82, 178, 133, 47, 204, 198, 203, 64, 139, 47, 18, 1, 50, 124, 224, 139, 146, - 55, 157, 186, 193, 138, 62, 245, 73, 17, 103, 166, 85, 233, 32, 3, 134, 226, 23, 212, 143, 109, - 160, 153, 182, 253, 222, 173, 177, 85, 115, 128, 241, 236, 228, 189, 157, 174, 207, 136, 217, - 42, 61, 129, 55, 226, 147, 32, 0, 69, 208, 210, 93, 92, 46, 214, 30, 252, 59, 64, 200, 26, 116, - 173, 219, 61, 84, 98, 203, 171, 105, 109, 106, 83, 142, 105, 204, 164, 238, 49, 32, 4, 34, 232, - 105, 46, 174, 23, 115, 143, 126, 29, 160, 100, 13, 58, 86, 237, 158, 170, 49, 101, 213, 180, - 182, 181, 41, 199, 52, 230, 82, 119, 25, 32, 1, 98, 187, 27, 229, 201, 158, 39, 193, 211, 70, - 93, 222, 26, 5, 208, 115, 210, 21, 161, 69, 138, 38, 126, 136, 225, 143, 243, 88, 62, 176, 112, - 32, 0, 136, 160, 207, 160, 36, 211, 218, 149, 134, 82, 207, 185, 98, 155, 93, 221, 150, 244, - 137, 116, 252, 185, 45, 91, 150, 252, 187, 170, 150, 153, 240, 32, 7, 63, 179, 154, 70, 15, 13, - 52, 159, 28, 115, 234, 234, 22, 169, 0, 212, 185, 230, 170, 250, 100, 55, 218, 163, 214, 84, - 170, 136, 8, 142, 97, 32, 5, 217, 71, 77, 19, 95, 20, 76, 44, 72, 163, 176, 19, 211, 22, 142, - 186, 67, 122, 192, 46, 216, 204, 50, 83, 63, 159, 254, 166, 25, 231, 229, 32, 4, 186, 73, 62, - 240, 185, 64, 8, 8, 150, 199, 126, 108, 204, 90, 194, 239, 215, 28, 85, 150, 213, 221, 96, 0, - 34, 1, 166, 81, 19, 68, 70, 32, 2, 210, 221, 158, 235, 53, 247, 87, 138, 233, 254, 149, 50, 49, - 168, 161, 96, 16, 247, 56, 52, 79, 94, 34, 150, 175, 91, 146, 23, 83, 120, 217, 32, 0, 186, - 181, 182, 196, 13, 142, 25, 191, 137, 54, 216, 69, 44, 60, 245, 173, 11, 60, 36, 172, 198, 251, - 52, 121, 164, 90, 33, 240, 191, 49, 80, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 185, 147, 123, 240, 148, 248, 86, 34, 198, - 131, 175, 51, 114, 107, 43, 204, 245, 218, 82, 25, 242, 116, 139, 149, 210, 133, 242, 17, 38, - 154, 101, 32, 5, 218, 137, 59, 56, 73, 168, 249, 28, 43, 152, 111, 178, 167, 240, 89, 76, 56, - 133, 242, 59, 62, 182, 190, 134, 62, 32, 140, 80, 247, 123, 199, 32, 6, 223, 154, 95, 147, 217, - 149, 32, 138, 151, 234, 22, 143, 159, 75, 217, 151, 154, 104, 108, 248, 56, 234, 28, 33, 72, - 69, 29, 126, 0, 37, 149, 32, 4, 252, 175, 102, 234, 233, 155, 13, 27, 209, 72, 177, 22, 12, 92, - 3, 255, 71, 203, 167, 25, 82, 233, 129, 235, 164, 97, 138, 157, 15, 224, 227, 32, 3, 4, 73, 26, - 107, 250, 0, 254, 2, 39, 152, 224, 93, 86, 160, 54, 98, 205, 34, 55, 115, 248, 65, 5, 85, 167, - 168, 100, 19, 108, 30, 164, 32, 4, 151, 70, 150, 32, 58, 243, 110, 26, 52, 132, 144, 9, 93, - 202, 40, 220, 116, 111, 0, 233, 209, 144, 123, 244, 43, 48, 125, 56, 200, 235, 3, 32, 7, 127, - 159, 97, 231, 114, 184, 127, 220, 238, 3, 123, 44, 24, 215, 185, 40, 149, 223, 63, 86, 149, - 183, 68, 62, 189, 105, 75, 144, 182, 156, 146, 32, 1, 242, 152, 102, 62, 90, 51, 174, 48, 78, - 109, 8, 202, 131, 33, 217, 222, 27, 231, 139, 119, 143, 128, 121, 106, 58, 190, 45, 205, 223, - 41, 116, 32, 5, 21, 67, 101, 92, 112, 182, 22, 84, 9, 131, 189, 140, 148, 40, 249, 183, 170, - 232, 249, 193, 46, 33, 133, 62, 45, 2, 149, 238, 64, 77, 65, 32, 1, 65, 52, 206, 95, 122, 14, - 30, 85, 227, 58, 109, 222, 186, 108, 71, 216, 221, 98, 221, 152, 108, 13, 1, 198, 23, 41, 8, - 233, 179, 120, 47, 32, 5, 107, 85, 58, 191, 186, 3, 85, 152, 193, 223, 228, 85, 180, 89, 225, - 70, 245, 252, 171, 158, 61, 139, 21, 175, 12, 135, 94, 163, 213, 207, 251, 32, 6, 9, 93, 104, - 192, 1, 153, 140, 83, 107, 120, 46, 208, 95, 169, 26, 156, 9, 162, 164, 84, 58, 55, 227, 90, - 131, 153, 24, 187, 111, 161, 118, 32, 2, 237, 53, 11, 10, 229, 176, 24, 193, 157, 67, 178, 202, - 93, 172, 211, 237, 254, 241, 23, 16, 173, 146, 57, 212, 151, 174, 67, 93, 164, 126, 251, 32, 1, - 87, 224, 55, 128, 255, 224, 195, 110, 136, 225, 75, 201, 124, 141, 192, 88, 120, 188, 94, 7, - 157, 228, 197, 153, 139, 73, 12, 237, 6, 108, 164, 32, 3, 197, 13, 245, 114, 224, 240, 118, 54, - 233, 73, 69, 151, 129, 83, 66, 82, 186, 10, 37, 0, 141, 139, 235, 184, 132, 151, 36, 129, 135, - 39, 182, 32, 7, 142, 104, 154, 189, 91, 35, 23, 112, 29, 208, 112, 13, 193, 77, 50, 121, 90, - 210, 166, 219, 18, 52, 194, 6, 215, 110, 80, 233, 244, 215, 99, 32, 5, 207, 93, 57, 54, 120, - 65, 123, 75, 235, 62, 188, 189, 109, 243, 93, 211, 196, 197, 33, 48, 55, 35, 99, 162, 4, 2, - 159, 153, 47, 127, 162, 32, 6, 23, 123, 3, 202, 203, 176, 30, 138, 218, 100, 205, 139, 159, 0, - 136, 166, 43, 52, 40, 249, 161, 79, 73, 173, 139, 128, 250, 62, 195, 20, 91, 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, - 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, 5, 43, 135, 220, 4, 20, 37, 137, - 206, 211, 41, 137, 149, 46, 214, 18, 210, 240, 36, 221, 209, 207, 124, 172, 250, 245, 28, 8, - 16, 17, 187, 180, 32, 7, 130, 204, 117, 190, 204, 151, 230, 31, 62, 230, 126, 81, 28, 157, 207, - 95, 23, 237, 231, 38, 172, 12, 111, 181, 5, 127, 152, 18, 165, 115, 130, 32, 7, 130, 204, 117, - 190, 204, 151, 230, 31, 62, 230, 126, 81, 28, 157, 207, 95, 23, 237, 231, 38, 172, 12, 111, - 181, 5, 127, 152, 18, 165, 115, 130, 32, 7, 130, 204, 117, 190, 204, 151, 230, 31, 62, 230, - 126, 81, 28, 157, 207, 95, 23, 237, 231, 38, 172, 12, 111, 181, 5, 127, 152, 18, 165, 115, 130, - 32, 0, 195, 183, 176, 27, 81, 155, 238, 186, 44, 253, 150, 225, 28, 116, 121, 199, 189, 132, - 56, 156, 128, 223, 126, 144, 120, 103, 174, 137, 206, 189, 111, 32, 2, 145, 177, 146, 33, 101, - 216, 108, 43, 10, 18, 211, 241, 152, 22, 149, 126, 107, 68, 164, 7, 134, 3, 2, 215, 228, 235, - 174, 85, 206, 109, 77, 32, 6, 42, 222, 248, 190, 214, 90, 144, 190, 236, 180, 216, 193, 202, - 174, 71, 122, 123, 252, 57, 144, 230, 252, 155, 241, 80, 13, 96, 156, 255, 10, 149, 32, 4, 68, - 218, 0, 92, 96, 138, 255, 100, 92, 49, 199, 172, 79, 217, 242, 58, 70, 1, 52, 225, 60, 57, 54, - 248, 63, 191, 93, 156, 135, 226, 39, 32, 4, 214, 168, 127, 209, 102, 94, 75, 225, 155, 110, 99, - 132, 166, 120, 148, 232, 208, 173, 212, 19, 37, 165, 201, 253, 171, 249, 216, 200, 161, 1, 118, - 32, 1, 249, 128, 72, 132, 159, 184, 205, 8, 57, 62, 104, 105, 189, 225, 133, 37, 48, 39, 89, - 120, 170, 242, 12, 186, 107, 243, 164, 185, 0, 77, 36, 32, 0, 13, 57, 220, 38, 101, 66, 119, - 46, 127, 223, 61, 100, 178, 87, 192, 77, 25, 10, 210, 231, 197, 254, 80, 117, 73, 176, 223, - 140, 183, 200, 92, 32, 1, 247, 151, 10, 233, 114, 55, 162, 88, 223, 209, 2, 61, 14, 167, 107, - 180, 113, 242, 18, 7, 25, 221, 113, 92, 190, 220, 102, 255, 242, 253, 245, 32, 5, 76, 150, 31, - 58, 213, 156, 176, 156, 225, 222, 111, 214, 3, 218, 248, 193, 118, 209, 180, 2, 68, 230, 241, - 140, 193, 151, 9, 163, 163, 43, 11, 32, 0, 77, 187, 34, 20, 17, 148, 12, 87, 131, 112, 102, 80, - 222, 102, 242, 240, 112, 89, 79, 156, 67, 118, 10, 172, 99, 123, 154, 244, 220, 81, 153, 32, 2, - 216, 81, 197, 140, 173, 120, 115, 163, 163, 34, 123, 36, 16, 140, 65, 231, 37, 111, 83, 253, - 186, 1, 139, 142, 116, 78, 174, 208, 245, 154, 234, 32, 0, 177, 26, 4, 194, 77, 16, 27, 211, - 125, 83, 64, 213, 247, 142, 22, 125, 246, 5, 182, 49, 2, 220, 228, 108, 80, 182, 115, 235, 16, - 39, 211, 32, 5, 57, 201, 227, 165, 230, 95, 132, 210, 221, 23, 250, 28, 71, 18, 136, 147, 136, - 122, 8, 223, 83, 100, 147, 35, 42, 136, 58, 38, 139, 194, 0, 32, 4, 115, 70, 70, 174, 178, 124, - 229, 36, 99, 50, 21, 100, 32, 183, 2, 255, 67, 133, 136, 66, 30, 152, 96, 99, 44, 4, 34, 65, - 147, 109, 184, 32, 4, 143, 181, 126, 212, 37, 1, 42, 209, 89, 50, 94, 11, 131, 138, 135, 221, - 74, 225, 13, 130, 77, 73, 114, 127, 197, 70, 196, 14, 198, 225, 232, 32, 0, 24, 103, 10, 43, - 27, 212, 36, 177, 105, 91, 134, 11, 46, 39, 39, 123, 210, 251, 145, 42, 115, 132, 138, 227, - 247, 39, 228, 116, 72, 27, 137, 32, 6, 104, 97, 213, 61, 114, 233, 56, 174, 178, 84, 22, 186, - 230, 25, 223, 196, 83, 104, 120, 126, 76, 29, 16, 82, 94, 41, 14, 157, 22, 212, 120, 32, 0, 89, - 100, 137, 84, 109, 95, 236, 37, 138, 245, 70, 30, 74, 169, 13, 59, 220, 138, 10, 234, 217, 15, - 149, 234, 11, 106, 31, 132, 215, 240, 40, 32, 6, 219, 186, 252, 174, 179, 10, 70, 25, 43, 128, - 126, 110, 32, 219, 105, 185, 110, 3, 117, 66, 98, 185, 125, 111, 89, 242, 0, 41, 101, 237, 250, - 59, 2, 142, 187, 62, 229, 132, 158, 36, 16, 12, 173, 185, 194, 45, 100, 229, 28, 116, 102, 80, - 139, 203, 122, 239, 107, 5, 71, 213, 187, 233, 15, 65, 88, 2, 32, 2, 123, 105, 208, 90, 184, - 94, 169, 135, 160, 75, 221, 76, 12, 83, 58, 150, 44, 222, 247, 43, 168, 111, 254, 9, 229, 218, - 136, 193, 38, 50, 125, 32, 5, 90, 248, 70, 158, 39, 21, 167, 219, 105, 15, 80, 73, 78, 54, 85, - 222, 25, 214, 217, 1, 139, 163, 182, 40, 119, 191, 90, 95, 181, 168, 20, 5, 131, 111, 215, 225, - 149, 191, 212, 165, 250, 230, 67, 228, 21, 196, 149, 169, 151, 70, 187, 6, 219, 108, 200, 212, - 22, 142, 43, 157, 233, 175, 113, 101, 60, 178, 7, 233, 231, 168, 175, 82, 151, 8, 75, 52, 126, - 226, 164, 124, 179, 40, 51, 25, 109, 186, 82, 217, 248, 241, 165, 152, 84, 38, 36, 56, 182, 46, - 199, 71, 214, 186, 180, 34, 107, 129, 218, 81, 189, 117, 202, 143, 38, 242, 1, 232, 103, 38, - 199, 181, 48, 25, 210, 62, 120, 250, 115, 109, 142, 248, 21, 108, 131, 209, 3, 237, 128, 8, 21, - 38, 229, 195, 183, 189, 201, 218, 168, 202, 146, 234, 61, 47, 232, 139, 2, 98, 207, 216, 127, - 65, 43, 167, 165, 74, 192, 62, 52, 12, 183, 22, 1, 198, 211, 171, 50, 119, 31, 106, 93, 37, 40, - 45, 109, 172, 52, 2, 219, 75, 97, 245, 132, 6, 32, 5, 19, 180, 59, 11, 93, 51, 113, 207, 95, - 59, 39, 22, 228, 23, 196, 80, 162, 247, 170, 137, 110, 239, 216, 103, 44, 162, 15, 54, 95, 242, - 81, 3, 5, 6, 42, 146, 143, 105, 189, 9, 36, 53, 22, 41, 199, 200, 239, 241, 49, 36, 135, 27, - 208, 91, 50, 76, 144, 231, 163, 95, 81, 141, 224, 94, 39, 129, 28, 167, 112, 37, 170, 195, 75, - 31, 3, 249, 44, 216, 93, 185, 212, 203, 185, 51, 96, 28, 107, 236, 26, 223, 67, 30, 161, 79, - 189, 112, 41, 2, 217, 55, 124, 187, 83, 130, 235, 229, 123, 191, 115, 112, 181, 1, 62, 43, 151, - 138, 52, 97, 44, 121, 130, 191, 82, 229, 145, 11, 253, 108, 37, 18, 164, 114, 39, 246, 25, 122, - 147, 124, 188, 15, 237, 244, 151, 73, 129, 251, 101, 39, 14, 172, 92, 177, 43, 79, 219, 230, - 83, 92, 76, 116, 193, 78, 54, 214, 107, 217, 53, 75, 236, 9, 29, 196, 3, 12, 221, 234, 66, 110, - 51, 235, 100, 249, 115, 196, 194, 90, 37, 87, 86, 210, 54, 43, 140, 49, 82, 110, 220, 98, 219, - 151, 74, 66, 63, 234, 88, 226, 184, 60, 116, 111, 157, 27, 198, 138, 102, 90, 221, 230, 21, 32, - 168, 220, 195, 132, 151, 8, 5, 154, 139, 158, 65, 145, 174, 202, 237, 94, 230, 150, 195, 181, - 63, 181, 67, 203, 138, 134, 253, 20, 115, 60, 121, 13, 105, 26, 163, 90, 87, 75, 23, 81, 47, - 160, 54, 183, 238, 223, 55, 102, 143, 95, 114, 242, 126, 232, 14, 52, 66, 88, 19, 234, 210, - 170, 76, 1, 241, 68, 12, 54, 95, 229, 8, 18, 220, 199, 100, 148, 155, 122, 223, 238, 14, 85, - 126, 160, 34, 92, 201, 111, 49, 1, 115, 33, 12, 59, 141, 24, 243, 35, 100, 238, 74, 71, 2, 134, - 178, 104, 173, 162, 236, 213, 249, 202, 164, 95, 107, 11, 56, 101, 109, 71, 98, 241, 182, 217, - 115, 170, 117, 39, 2, 37, 156, 103, 209, 110, 133, 30, 190, 131, 71, 14, 90, 255, 205, 61, 88, - 86, 55, 241, 231, 182, 211, 86, 246, 138, 91, 83, 4, 67, 162, 200, 215, 113, 231, 29, 89, 27, - 236, 4, 8, 97, 5, 233, 202, 248, 7, 241, 51, 89, 212, 117, 176, 184, 114, 90, 77, 25, 102, 24, - 189, 102, 222, 97, 171, 20, 187, 87, 250, 142, 75, 144, 147, 224, 60, 180, 110, 169, 108, 87, - 34, 58, 108, 150, 77, 56, 78, 69, 231, 41, 51, 214, 9, 85, 150, 104, 230, 145, 158, 115, 88, - 215, 168, 68, 172, 202, 195, 100, 183, 8, 248, 87, 224, 249, 36, 214, 116, 154, 201, 95, 128, - 208, 102, 44, 242, 180, 9, 138, 192, 150, 217, 18, 178, 102, 231, 213, 101, 49, 65, 103, 113, - 118, 42, 4, 74, 97, 34, 90, 19, 29, 120, 84, 150, 20, 219, 53, 125, 150, 188, 91, 33, 119, 96, - 191, 101, 209, 206, 167, 3, 12, 15, 199, 83, 63, 68, 148, 118, 143, 47, 180, 140, 68, 117, 103, - 124, 121, 228, 109, 185, 34, 133, 45, 48, 190, 51, 180, 94, 205, 33, 85, 223, 94, 97, 153, 163, - 118, 94, 200, 119, 214, 56, 226, 237, 184, 185, 3, 47, 174, 82, 226, 85, 207, 34, 218, 217, 79, - 247, 221, 132, 131, 238, 20, 142, 122, 7, 17, 56, 158, 86, 253, 71, 38, 234, 208, 24, 28, 219, - 192, 53, 2, 197, 117, 197, 222, 165, 125, 29, 80, 121, 46, 211, 128, 190, 245, 232, 2, 174, - 190, 237, 95, 171, 45, 71, 102, 4, 84, 162, 133, 201, 189, 45, 190, 155, 93, 153, 110, 55, 38, - 24, 17, 253, 255, 248, 218, 237, 159, 167, 246, 208, 150, 210, 243, 55, 236, 104, 72, 242, 85, - 241, 13, 150, 60, 152, 137, 135, 75, 168, 117, 8, 13, 98, 167, 249, 84, 182, 133, 143, 46, 153, - 242, 5, 32, 1, 74, 157, 151, 183, 156, 92, 133, 215, 203, 150, 24, 12, 64, 48, 163, 227, 184, - 206, 142, 139, 154, 66, 209, 87, 132, 11, 195, 40, 40, 190, 85, 32, 1, 217, 35, 239, 188, 188, - 192, 64, 184, 30, 245, 108, 186, 134, 120, 64, 17, 32, 181, 87, 206, 22, 158, 107, 0, 102, 101, - 213, 249, 61, 9, 232, 32, 1, 207, 238, 195, 141, 104, 219, 245, 189, 54, 228, 146, 14, 81, 70, - 45, 149, 48, 248, 144, 13, 3, 66, 227, 193, 193, 196, 32, 109, 79, 123, 127, 32, 0, 106, 40, - 50, 180, 62, 204, 17, 70, 227, 5, 189, 78, 38, 144, 100, 220, 143, 94, 39, 80, 8, 165, 207, - 115, 214, 112, 95, 249, 243, 89, 12, 32, 4, 20, 120, 140, 128, 32, 172, 217, 237, 97, 184, 6, - 77, 133, 94, 181, 191, 237, 48, 207, 60, 84, 223, 138, 203, 209, 88, 9, 95, 129, 248, 87, 5, 6, - 64, 75, 93, 238, 4, 192, 189, 0, 81, 57, 75, 239, 96, 171, 48, 220, 113, 119, 191, 183, 166, - 97, 79, 71, 187, 254, 136, 50, 43, 80, 111, 230, 2, 165, 29, 205, 69, 114, 231, 236, 231, 212, - 144, 47, 5, 228, 237, 157, 140, 166, 29, 37, 207, 36, 72, 224, 220, 137, 31, 196, 121, 6, 119, - 243, 50, 254, 104, 215, 44, 179, 199, 109, 91, 13, 72, 132, 212, 20, 92, 166, 50, 165, 21, 184, - 235, 49, 70, 59, 211, 114, 103, 141, 210, 158, 199, 36, 61, 50, 9, 64, 16, 157, 76, 170, 55, - 31, 126, 17, 30, 246, 105, 96, 151, 80, 219, 5, 50, 116, 121, 231, 29, 21, 122, 70, 54, 57, - 156, 236, 24, 240, 15, 118, 7, 150, 73, 202, 145, 220, 151, 32, 228, 40, 233, 188, 179, 172, - 202, 179, 168, 178, 79, 30, 249, 170, 119, 219, 21, 181, 208, 109, 82, 110, 220, 98, 219, 151, - 74, 66, 63, 234, 88, 226, 184, 60, 116, 111, 157, 27, 198, 138, 102, 90, 221, 230, 21, 32, 168, - 220, 195, 132, 151, 8, 5, 215, 248, 10, 120, 70, 46, 168, 36, 239, 94, 244, 162, 10, 232, 135, - 250, 235, 159, 24, 125, 250, 35, 91, 149, 51, 182, 98, 154, 217, 255, 96, 141, 122, 10, 164, - 59, 190, 4, 139, 80, 244, 120, 217, 166, 11, 127, 37, 199, 187, 152, 225, 197, 146, 30, 8, 167, - 135, 85, 53, 8, 91, 33, 49, 209, 177, 154, 9, 229, 51, 39, 128, 101, 111, 99, 196, 98, 31, 237, - 247, 243, 255, 240, 103, 195, 58, 103, 105, 181, 129, 127, 70, 56, 32, 139, 16, 63, 244, 187, - 244, 53, 159, 58, 150, 244, 180, 228, 32, 201, 165, 29, 165, 29, 46, 243, 27, 174, 71, 176, 71, - 137, 157, 185, 105, 162, 24, 41, 96, 163, 30, 190, 131, 71, 14, 90, 255, 205, 61, 88, 86, 55, - 241, 231, 182, 211, 86, 246, 138, 91, 83, 4, 67, 162, 200, 215, 113, 231, 29, 89, 27, 236, 4, - 44, 55, 157, 49, 94, 50, 241, 208, 158, 123, 172, 41, 145, 155, 118, 113, 169, 84, 191, 38, 7, - 191, 205, 246, 154, 110, 54, 29, 211, 142, 37, 69, 153, 27, 114, 237, 146, 206, 35, 153, 20, - 15, 225, 102, 160, 141, 228, 207, 168, 56, 158, 108, 123, 252, 39, 178, 247, 29, 105, 121, 164, - 1, 3, 59, 195, 44, 247, 30, 168, 201, 22, 128, 112, 164, 224, 249, 3, 150, 71, 41, 228, 182, - 63, 133, 124, 228, 35, 75, 14, 52, 255, 13, 35, 119, 204, 237, 101, 49, 65, 103, 113, 118, 42, - 4, 74, 97, 34, 90, 19, 29, 120, 84, 150, 20, 219, 53, 125, 150, 188, 91, 33, 119, 96, 191, 101, - 209, 206, 167, 3, 243, 187, 32, 47, 177, 239, 237, 95, 205, 58, 118, 26, 105, 43, 121, 6, 192, - 82, 22, 91, 43, 21, 2, 221, 4, 123, 238, 162, 216, 182, 14, 242, 55, 121, 241, 97, 136, 255, - 32, 59, 228, 246, 249, 127, 14, 186, 207, 237, 62, 133, 165, 197, 238, 174, 63, 193, 236, 41, - 244, 230, 135, 22, 233, 89, 122, 7, 17, 56, 158, 86, 253, 71, 38, 234, 208, 24, 28, 219, 192, - 53, 2, 197, 117, 197, 222, 165, 125, 29, 80, 121, 46, 211, 128, 190, 245, 232, 2, 66, 31, 173, - 142, 242, 226, 218, 51, 218, 239, 244, 186, 185, 7, 192, 174, 6, 64, 185, 189, 102, 230, 154, - 121, 222, 83, 93, 113, 161, 209, 208, 242, 208, 150, 210, 243, 55, 236, 104, 72, 242, 85, 241, - 13, 150, 60, 152, 137, 135, 75, 168, 117, 8, 13, 98, 167, 249, 84, 182, 133, 143, 46, 153, 242, - 5, 32, 5, 178, 3, 153, 123, 161, 110, 22, 246, 161, 16, 202, 79, 230, 29, 245, 66, 122, 78, - 201, 207, 76, 89, 189, 69, 208, 74, 14, 213, 208, 70, 119, 32, 4, 77, 21, 142, 151, 108, 12, - 73, 116, 205, 156, 82, 150, 200, 181, 246, 80, 56, 177, 6, 206, 80, 137, 239, 58, 223, 242, 80, - 176, 121, 97, 58, 32, 7, 30, 130, 186, 224, 209, 201, 161, 4, 201, 162, 132, 50, 244, 49, 53, - 58, 25, 231, 77, 159, 3, 219, 150, 214, 18, 48, 89, 39, 6, 253, 136, 32, 0, 54, 193, 58, 129, - 117, 159, 55, 195, 87, 173, 113, 30, 115, 217, 35, 33, 207, 15, 191, 135, 228, 83, 167, 39, 73, - 62, 112, 68, 9, 1, 150, 32, 4, 174, 89, 55, 121, 87, 136, 31, 33, 180, 244, 180, 251, 105, 77, - 59, 212, 61, 60, 110, 210, 110, 1, 71, 49, 208, 61, 220, 57, 227, 63, 209, 5, 6, 247, 13, 180, - 180, 158, 173, 253, 235, 243, 175, 214, 56, 254, 143, 193, 143, 39, 105, 86, 111, 165, 0, 95, - 33, 67, 58, 127, 165, 181, 92, 245, 245, 21, 180, 109, 23, 52, 21, 241, 87, 95, 12, 121, 0, - 171, 134, 79, 147, 78, 235, 143, 203, 127, 89, 113, 244, 16, 158, 84, 14, 19, 56, 111, 197, 59, - 112, 96, 156, 179, 119, 54, 45, 151, 105, 203, 11, 147, 83, 29, 143, 109, 14, 253, 202, 219, 0, - 36, 251, 111, 84, 56, 31, 204, 107, 221, 82, 122, 86, 151, 156, 205, 35, 12, 166, 246, 65, 45, - 17, 166, 246, 236, 196, 127, 148, 53, 93, 56, 227, 82, 118, 221, 90, 67, 8, 83, 161, 193, 206, - 226, 86, 133, 213, 224, 17, 2, 218, 146, 61, 58, 229, 173, 121, 253, 228, 83, 102, 129, 12, - 212, 84, 198, 181, 157, 44, 135, 95, 117, 204, 93, 31, 27, 108, 170, 108, 132, 143, 62, 226, - 84, 214, 99, 60, 118, 135, 230, 92, 139, 129, 62, 211, 204, 120, 144, 245, 165, 61, 171, 142, - 4, 134, 208, 66, 5, 246, 87, 134, 147, 55, 213, 225, 95, 208, 14, 125, 138, 160, 223, 40, 15, - 59, 150, 247, 215, 7, 118, 163, 215, 169, 17, 115, 125, 38, 134, 44, 10, 198, 98, 172, 189, 12, - 131, 140, 246, 245, 63, 217, 238, 52, 100, 80, 33, 104, 238, 105, 120, 58, 180, 248, 33, 119, - 75, 110, 169, 220, 127, 133, 4, 114, 142, 186, 36, 124, 122, 76, 229, 41, 174, 187, 167, 231, - 156, 4, 52, 114, 137, 156, 196, 134, 72, 34, 84, 79, 207, 128, 123, 188, 128, 103, 109, 58, - 227, 29, 235, 2, 179, 217, 189, 123, 155, 115, 18, 172, 21, 198, 228, 229, 39, 209, 136, 4, - 220, 30, 72, 152, 38, 64, 46, 99, 163, 18, 58, 134, 224, 244, 234, 207, 106, 89, 247, 123, 173, - 94, 233, 167, 80, 74, 197, 128, 149, 52, 100, 163, 208, 106, 208, 44, 71, 17, 161, 152, 1, 189, - 251, 4, 206, 196, 179, 98, 253, 74, 122, 97, 96, 155, 253, 208, 186, 219, 50, 188, 150, 18, - 196, 176, 59, 54, 208, 205, 0, 7, 222, 250, 247, 59, 2, 241, 111, 236, 132, 147, 136, 244, 23, - 243, 255, 104, 201, 209, 23, 26, 0, 63, 215, 105, 123, 202, 203, 52, 19, 40, 55, 192, 145, 144, - 155, 62, 28, 204, 243, 173, 86, 68, 46, 77, 210, 199, 157, 54, 4, 53, 203, 177, 125, 87, 13, - 190, 91, 171, 188, 211, 51, 67, 24, 66, 172, 240, 10, 112, 57, 234, 45, 208, 198, 35, 225, 94, - 4, 67, 168, 163, 9, 115, 213, 202, 241, 15, 82, 19, 28, 60, 37, 115, 163, 22, 157, 181, 91, - 179, 247, 24, 39, 224, 3, 249, 58, 62, 205, 43, 235, 252, 241, 205, 72, 94, 190, 238, 32, 242, - 169, 169, 62, 226, 207, 214, 41, 93, 168, 37, 137, 39, 12, 220, 62, 135, 34, 233, 239, 83, 191, - 72, 141, 52, 205, 215, 25, 177, 114, 235, 86, 108, 250, 216, 80, 19, 206, 41, 201, 178, 58, - 169, 124, 182, 9, 20, 222, 252, 16, 147, 40, 185, 95, 251, 15, 52, 220, 80, 64, 48, 110, 74, - 142, 151, 33, 95, 115, 137, 131, 180, 112, 225, 9, 153, 195, 124, 4, 104, 40, 97, 205, 2, 131, - 236, 59, 162, 100, 50, 120, 9, 165, 229, 53, 225, 35, 47, 250, 6, 153, 222, 165, 238, 183, 136, - 243, 54, 158, 248, 39, 243, 206, 154, 114, 150, 197, 4, 245, 21, 199, 46, 47, 153, 231, 22, 80, - 211, 28, 217, 26, 134, 122, 66, 79, 126, 134, 35, 186, 61, 21, 181, 213, 151, 94, 38, 137, 251, - 5, 32, 7, 60, 33, 40, 197, 19, 95, 219, 48, 49, 12, 197, 46, 185, 87, 27, 15, 58, 60, 126, 69, - 212, 159, 144, 120, 206, 151, 40, 28, 82, 215, 160, 32, 1, 91, 143, 225, 210, 13, 133, 79, 238, - 88, 182, 100, 108, 148, 153, 227, 240, 14, 35, 115, 120, 228, 147, 12, 67, 56, 137, 17, 65, 22, - 42, 244, 32, 5, 112, 78, 243, 219, 23, 105, 160, 148, 236, 114, 184, 173, 89, 241, 35, 117, 84, - 226, 73, 237, 176, 225, 162, 181, 188, 56, 187, 1, 110, 211, 34, 32, 4, 105, 252, 11, 132, 243, - 84, 26, 115, 115, 30, 185, 156, 33, 35, 142, 134, 211, 217, 34, 152, 233, 141, 222, 117, 214, - 75, 54, 88, 116, 170, 228, 32, 7, 59, 222, 227, 71, 114, 227, 250, 69, 27, 218, 4, 17, 238, 21, - 48, 63, 14, 163, 69, 9, 130, 202, 234, 103, 149, 126, 69, 180, 14, 214, 79, 3, 7, 143, 227, - 159, 164, 85, 246, 234, 50, 196, 91, 38, 46, 24, 101, 98, 89, 172, 100, 14, 251, 66, 137, 81, - 246, 211, 156, 208, 4, 122, 213, 140, 130, 53, 82, 202, 111, 213, 169, 243, 94, 112, 245, 75, - 185, 11, 236, 112, 194, 41, 200, 247, 216, 56, 153, 254, 12, 227, 169, 77, 8, 103, 81, 25, 187, - 88, 142, 120, 152, 104, 74, 247, 5, 9, 0, 23, 158, 66, 243, 222, 4, 163, 88, 57, 211, 75, 7, - 149, 187, 187, 81, 34, 2, 179, 160, 148, 228, 227, 113, 247, 198, 39, 137, 45, 114, 186, 121, - 135, 78, 55, 65, 177, 99, 102, 65, 28, 76, 48, 138, 171, 217, 51, 31, 154, 190, 174, 97, 168, - 117, 172, 237, 224, 114, 8, 103, 84, 1, 246, 211, 98, 97, 14, 58, 110, 46, 169, 251, 164, 131, - 193, 178, 14, 208, 74, 62, 157, 44, 103, 207, 112, 173, 216, 171, 182, 141, 100, 237, 193, 155, - 100, 252, 234, 227, 240, 128, 133, 102, 185, 99, 234, 200, 182, 96, 74, 192, 13, 209, 71, 115, - 104, 124, 179, 164, 223, 89, 216, 115, 42, 45, 247, 206, 176, 156, 12, 153, 45, 13, 251, 215, - 172, 248, 210, 214, 153, 245, 13, 166, 237, 50, 171, 170, 143, 159, 139, 197, 2, 32, 5, 13, - 111, 60, 39, 59, 146, 219, 116, 228, 231, 248, 193, 28, 12, 49, 93, 76, 134, 49, 58, 243, 125, - 164, 34, 169, 149, 164, 222, 107, 88, 131, 32, 1, 61, 210, 239, 49, 241, 173, 101, 137, 53, 46, - 255, 2, 205, 110, 136, 200, 30, 203, 225, 204, 208, 69, 74, 28, 171, 97, 96, 101, 206, 8, 61, - 2, 8, 246, 125, 95, 99, 25, 154, 3, 111, 151, 225, 16, 65, 107, 46, 18, 178, 23, 245, 118, 82, - 79, 97, 220, 72, 4, 165, 61, 87, 162, 27, 17, 109, 151, 203, 47, 152, 214, 200, 164, 98, 254, - 213, 134, 8, 241, 226, 104, 150, 244, 74, 115, 219, 255, 83, 218, 101, 111, 191, 131, 194, 62, - 167, 55, 72, 152, 27, 86, 66, 6, 244, 122, 121, 144, 46, 166, 66, 124, 203, 204, 235, 75, 61, - 217, 136, 85, 11, 213, 20, 21, 113, 177, 117, 29, 68, 188, 151, 243, 240, 99, 46, 3, 241, 18, - 179, 194, 224, 200, 241, 109, 149, 189, 60, 148, 99, 5, 35, 145, 128, 128, 21, 203, 239, 181, - 228, 240, 47, 135, 220, 34, 178, 167, 247, 106, 173, 158, 22, 63, 212, 201, 160, 117, 48, 148, - 208, 146, 236, 225, 34, 210, 178, 74, 27, 219, 146, 18, 246, 147, 54, 127, 158, 168, 87, 74, - 115, 111, 31, 193, 215, 116, 252, 153, 159, 179, 50, 108, 250, 181, 183, 86, 85, 81, 49, 50, - 182, 5, 156, 167, 125, 1, 203, 193, 96, 229, 23, 56, 185, 8, 183, 136, 50, 181, 158, 63, 15, - 141, 232, 151, 145, 44, 104, 52, 24, 145, 194, 228, 252, 233, 233, 188, 144, 1, 19, 65, 2, 229, - 146, 117, 67, 238, 190, 16, 96, 92, 169, 231, 87, 127, 81, 158, 229, 8, 37, 233, 213, 154, 139, - 101, 103, 128, 137, 232, 243, 179, 19, 171, 248, 8, 139, 146, 93, 32, 16, 112, 171, 228, 8, - 103, 32, 53, 33, 214, 236, 138, 223, 220, 8, 214, 104, 208, 254, 31, 143, 123, 12, 124, 21, 0, - 189, 144, 89, 215, 92, 202, 34, 161, 165, 176, 85, 85, 31, 163, 26, 106, 45, 165, 211, 109, 81, - 191, 141, 172, 192, 132, 37, 20, 17, 237, 17, 202, 4, 88, 20, 249, 223, 116, 21, 255, 35, 4, - 158, 8, 223, 44, 158, 133, 39, 80, 104, 164, 35, 21, 203, 9, 235, 52, 205, 135, 20, 82, 240, - 165, 60, 28, 223, 34, 9, 183, 92, 14, 152, 76, 254, 209, 126, 53, 54, 232, 220, 227, 8, 161, - 218, 16, 26, 241, 254, 53, 194, 219, 149, 106, 67, 173, 42, 36, 13, 233, 203, 206, 205, 110, - 99, 70, 226, 94, 86, 244, 125, 108, 41, 118, 254, 55, 55, 222, 22, 92, 112, 154, 65, 212, 142, - 59, 60, 3, 107, 208, 156, 230, 253, 184, 103, 128, 240, 14, 203, 86, 220, 97, 117, 207, 209, - 61, 122, 165, 43, 161, 50, 96, 156, 89, 156, 209, 198, 59, 100, 165, 195, 131, 9, 142, 113, - 217, 122, 61, 153, 59, 9, 163, 241, 6, 113, 196, 218, 94, 216, 173, 188, 168, 35, 191, 202, - 239, 105, 5, 128, 163, 172, 35, 193, 113, 25, 50, 185, 158, 146, 208, 222, 191, 50, 54, 152, - 19, 171, 153, 57, 88, 69, 45, 152, 185, 233, 13, 250, 36, 169, 142, 104, 60, 213, 67, 240, 88, - 59, 32, 5, 92, 52, 85, 123, 76, 75, 71, 12, 135, 142, 95, 97, 171, 243, 222, 13, 213, 213, 194, - 234, 62, 130, 171, 154, 52, 96, 191, 146, 113, 201, 190, 32, 6, 232, 75, 105, 216, 232, 185, - 94, 150, 42, 145, 240, 213, 165, 192, 92, 184, 241, 37, 164, 35, 140, 48, 101, 236, 184, 251, - 80, 232, 145, 232, 249, 32, 3, 235, 154, 88, 151, 144, 214, 105, 191, 102, 136, 162, 179, 198, - 105, 240, 157, 63, 163, 179, 114, 126, 210, 237, 254, 242, 159, 21, 244, 1, 147, 53, 32, 0, - 150, 104, 12, 176, 104, 99, 55, 78, 147, 229, 56, 248, 240, 151, 216, 170, 156, 13, 92, 146, - 202, 223, 224, 63, 168, 139, 76, 5, 57, 90, 230, 32, 0, 170, 80, 220, 20, 120, 44, 45, 93, 55, - 195, 241, 25, 149, 240, 181, 81, 166, 106, 76, 179, 9, 212, 201, 118, 245, 76, 241, 165, 67, - 56, 189, 32, 1, 85, 112, 183, 233, 88, 36, 146, 137, 205, 111, 188, 80, 159, 240, 177, 80, 126, - 150, 5, 22, 85, 15, 34, 36, 42, 99, 108, 36, 194, 127, 217, 32, 5, 139, 126, 223, 223, 52, 245, - 110, 171, 254, 45, 249, 136, 43, 63, 186, 150, 174, 200, 71, 151, 33, 156, 127, 123, 81, 146, - 182, 222, 117, 238, 38, 32, 2, 197, 191, 111, 239, 154, 122, 183, 85, 255, 22, 252, 196, 21, - 159, 221, 75, 87, 100, 35, 203, 144, 206, 63, 189, 168, 201, 91, 111, 58, 247, 19, 32, 6, 194, - 93, 215, 144, 192, 203, 65, 94, 79, 191, 179, 39, 253, 250, 72, 249, 230, 182, 24, 34, 4, 236, - 172, 146, 14, 251, 246, 95, 231, 32, 103, 32, 3, 215, 1, 13, 172, 227, 164, 234, 16, 137, 143, - 45, 85, 29, 235, 130, 1, 253, 33, 38, 96, 197, 145, 190, 238, 224, 229, 181, 31, 197, 91, 93, - 32, 5, 219, 228, 219, 47, 81, 157, 136, 93, 145, 1, 29, 241, 254, 232, 52, 150, 198, 117, 63, - 96, 191, 176, 228, 93, 80, 200, 0, 81, 235, 148, 9, 32, 0, 234, 250, 115, 49, 120, 81, 154, - 224, 95, 238, 150, 6, 89, 165, 154, 77, 149, 181, 180, 65, 183, 131, 205, 6, 188, 185, 91, 110, - 113, 217, 21, 32, 1, 134, 213, 164, 115, 155, 137, 177, 248, 14, 7, 56, 88, 252, 79, 118, 186, - 155, 96, 180, 62, 33, 1, 235, 252, 135, 49, 213, 91, 77, 212, 230, 32, 4, 75, 246, 46, 142, - 177, 76, 55, 7, 181, 195, 241, 227, 138, 157, 249, 28, 134, 159, 120, 190, 87, 198, 58, 245, - 173, 119, 125, 45, 238, 75, 203, 32, 7, 133, 121, 54, 224, 76, 52, 1, 55, 43, 22, 45, 183, 184, - 121, 86, 226, 126, 83, 194, 155, 104, 104, 170, 46, 17, 83, 7, 63, 64, 202, 195, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 4, - 31, 62, 119, 18, 164, 241, 141, 66, 145, 243, 171, 136, 61, 188, 88, 40, 210, 183, 88, 83, 208, - 116, 129, 226, 244, 177, 198, 19, 25, 45, 118, 32, 1, 114, 239, 57, 20, 92, 73, 237, 66, 30, - 83, 248, 67, 183, 249, 234, 180, 93, 182, 215, 20, 250, 193, 217, 193, 199, 35, 19, 44, 1, 91, - 159, 32, 4, 49, 166, 118, 44, 12, 6, 54, 161, 109, 72, 41, 136, 16, 88, 210, 152, 120, 181, 40, - 29, 13, 38, 112, 242, 37, 86, 68, 209, 135, 245, 55, 32, 7, 61, 155, 178, 150, 133, 53, 24, - 235, 254, 24, 71, 16, 31, 104, 40, 224, 201, 119, 35, 183, 123, 34, 101, 143, 97, 157, 227, 77, - 193, 0, 193, 32, 0, 187, 189, 126, 156, 164, 113, 130, 134, 78, 5, 158, 86, 41, 46, 155, 83, - 203, 137, 93, 50, 224, 115, 165, 133, 112, 244, 141, 185, 44, 15, 47, 32, 5, 36, 146, 245, 209, - 92, 113, 76, 84, 114, 97, 47, 11, 7, 67, 140, 141, 232, 35, 219, 22, 116, 112, 95, 62, 225, 3, - 128, 84, 248, 91, 159, 32, 1, 212, 83, 42, 236, 11, 67, 181, 186, 109, 82, 251, 101, 180, 212, - 26, 181, 161, 247, 8, 54, 113, 207, 228, 122, 64, 188, 52, 63, 125, 133, 138, 32, 4, 27, 234, - 167, 243, 234, 183, 150, 123, 113, 125, 158, 70, 205, 139, 217, 169, 251, 221, 245, 114, 182, - 0, 179, 28, 42, 92, 63, 185, 197, 47, 167, 32, 1, 24, 80, 227, 238, 250, 36, 61, 84, 245, 17, - 67, 221, 98, 82, 154, 205, 170, 128, 248, 246, 254, 116, 15, 222, 25, 217, 198, 116, 99, 208, - 211, 32, 7, 192, 92, 180, 117, 92, 216, 160, 116, 35, 248, 203, 238, 6, 99, 92, 17, 244, 201, - 231, 14, 109, 216, 110, 50, 162, 171, 5, 132, 110, 98, 55, 32, 0, 238, 155, 244, 186, 68, 76, - 110, 255, 84, 88, 29, 221, 9, 56, 245, 214, 127, 62, 193, 108, 230, 164, 147, 199, 218, 209, - 172, 100, 241, 13, 137, 32, 1, 76, 198, 2, 52, 208, 121, 145, 148, 183, 174, 0, 143, 238, 56, - 180, 89, 130, 176, 203, 192, 211, 160, 152, 28, 209, 27, 180, 15, 238, 79, 220, 32, 0, 94, 97, - 8, 176, 199, 131, 5, 185, 53, 220, 104, 126, 4, 17, 6, 87, 163, 169, 235, 231, 83, 169, 25, - 162, 180, 224, 31, 3, 157, 100, 2, 32, 1, 123, 221, 86, 254, 138, 203, 73, 10, 146, 70, 168, - 22, 115, 67, 195, 208, 22, 17, 67, 35, 225, 171, 161, 168, 9, 42, 244, 85, 124, 232, 117, 32, - 7, 237, 86, 214, 195, 3, 73, 238, 178, 102, 94, 206, 89, 8, 143, 249, 253, 139, 80, 210, 35, - 239, 18, 29, 13, 36, 128, 18, 196, 207, 70, 125, 32, 0, 171, 2, 8, 46, 127, 219, 0, 200, 218, - 195, 4, 85, 97, 149, 240, 23, 194, 86, 204, 82, 4, 10, 227, 168, 28, 247, 84, 221, 199, 166, - 96, 32, 2, 220, 195, 38, 166, 138, 57, 16, 158, 59, 177, 179, 224, 251, 151, 91, 173, 87, 129, - 167, 13, 167, 136, 26, 133, 56, 139, 58, 206, 196, 139, 85, 32, 4, 184, 14, 164, 191, 190, 79, - 1, 253, 83, 90, 74, 196, 40, 2, 255, 119, 198, 49, 4, 204, 196, 127, 255, 171, 19, 83, 229, 43, - 244, 89, 242, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, 7, 95, - 254, 110, 172, 185, 145, 29, 145, 41, 126, 136, 71, 179, 60, 206, 13, 172, 80, 87, 213, 98, - 140, 51, 123, 192, 128, 77, 225, 165, 29, 11, 32, 5, 79, 197, 111, 119, 238, 239, 226, 181, - 176, 201, 176, 69, 93, 192, 48, 239, 4, 245, 181, 0, 51, 111, 240, 229, 65, 40, 182, 91, 185, - 62, 155, 32, 5, 79, 197, 111, 119, 238, 239, 226, 181, 176, 201, 176, 69, 93, 192, 48, 239, 4, - 245, 181, 0, 51, 111, 240, 229, 65, 40, 182, 91, 185, 62, 155, 32, 5, 79, 197, 111, 119, 238, - 239, 226, 181, 176, 201, 176, 69, 93, 192, 48, 239, 4, 245, 181, 0, 51, 111, 240, 229, 65, 40, - 182, 91, 185, 62, 155, 32, 4, 252, 253, 219, 98, 235, 1, 204, 130, 73, 104, 130, 241, 14, 181, - 164, 188, 201, 84, 224, 141, 218, 53, 238, 51, 92, 56, 215, 112, 24, 63, 67, 32, 1, 221, 104, - 207, 214, 234, 23, 212, 72, 72, 223, 138, 254, 34, 254, 30, 157, 250, 247, 104, 201, 184, 9, - 214, 16, 211, 224, 179, 62, 98, 135, 71, 32, 1, 45, 45, 244, 41, 7, 107, 237, 73, 54, 146, 111, - 201, 76, 179, 1, 48, 39, 115, 194, 83, 98, 211, 128, 170, 255, 146, 68, 247, 75, 213, 123, 32, - 2, 39, 18, 130, 68, 27, 72, 204, 89, 15, 233, 51, 152, 239, 65, 126, 59, 35, 100, 95, 163, 17, - 146, 124, 174, 132, 194, 223, 243, 66, 87, 95, 32, 6, 242, 48, 144, 135, 136, 220, 80, 197, - 171, 151, 173, 56, 72, 145, 211, 42, 222, 63, 165, 46, 230, 181, 178, 147, 51, 13, 29, 117, 94, - 177, 134, 32, 0, 177, 205, 201, 82, 130, 135, 23, 113, 37, 195, 172, 83, 156, 70, 192, 252, - 139, 210, 192, 129, 54, 123, 138, 246, 227, 219, 195, 203, 135, 159, 10, 32, 4, 206, 172, 5, - 76, 142, 251, 69, 175, 249, 229, 48, 238, 251, 132, 18, 238, 83, 86, 73, 2, 75, 107, 243, 4, - 219, 195, 155, 244, 160, 34, 146, 32, 6, 77, 188, 199, 14, 153, 137, 235, 178, 108, 41, 52, - 190, 202, 212, 61, 117, 147, 67, 154, 154, 7, 11, 128, 7, 138, 26, 71, 208, 18, 238, 162, 32, - 4, 158, 18, 211, 94, 21, 126, 27, 164, 224, 57, 54, 219, 85, 164, 194, 4, 18, 186, 148, 140, - 225, 123, 20, 253, 226, 231, 242, 232, 59, 75, 114, 32, 7, 82, 225, 106, 213, 181, 251, 229, - 245, 141, 28, 102, 56, 227, 134, 147, 222, 88, 44, 147, 253, 87, 237, 98, 228, 245, 102, 115, - 129, 121, 115, 53, 32, 4, 21, 124, 52, 105, 105, 201, 201, 3, 166, 177, 113, 24, 188, 222, 104, - 194, 243, 143, 185, 47, 43, 212, 198, 106, 140, 225, 97, 59, 249, 182, 93, 32, 0, 139, 253, - 113, 84, 136, 231, 29, 244, 142, 97, 229, 189, 202, 247, 216, 105, 188, 90, 49, 54, 165, 107, - 228, 37, 127, 72, 153, 1, 159, 9, 235, 32, 0, 102, 63, 150, 77, 20, 175, 46, 241, 26, 185, 41, - 86, 238, 31, 2, 48, 126, 178, 249, 40, 187, 53, 117, 92, 100, 202, 232, 186, 129, 240, 170, 32, - 5, 51, 43, 225, 105, 117, 193, 186, 141, 109, 200, 150, 161, 146, 254, 97, 68, 22, 216, 227, - 179, 35, 99, 222, 229, 154, 148, 61, 20, 63, 19, 109, 32, 1, 28, 141, 93, 203, 14, 143, 10, 56, - 172, 211, 225, 75, 212, 98, 190, 69, 191, 38, 78, 106, 235, 248, 139, 142, 226, 121, 22, 153, - 87, 26, 22, 32, 3, 223, 197, 111, 201, 214, 99, 240, 7, 102, 65, 235, 52, 148, 98, 126, 137, - 140, 124, 86, 219, 38, 81, 2, 134, 140, 223, 87, 151, 91, 181, 254, 32, 4, 166, 152, 37, 97, - 106, 1, 182, 52, 191, 198, 51, 55, 199, 216, 50, 53, 76, 231, 180, 220, 136, 11, 247, 9, 143, - 192, 120, 126, 188, 52, 75, 32, 6, 246, 219, 170, 14, 210, 78, 50, 111, 150, 20, 187, 234, 96, - 189, 38, 248, 14, 39, 95, 179, 87, 36, 108, 25, 127, 125, 167, 25, 129, 57, 74, 32, 4, 125, - 171, 78, 47, 135, 249, 13, 169, 13, 166, 140, 79, 38, 18, 223, 11, 175, 204, 186, 223, 223, - 212, 20, 183, 6, 94, 227, 11, 224, 219, 165, 7, 30, 56, 95, 93, 65, 107, 32, 178, 223, 114, - 163, 90, 50, 171, 52, 10, 213, 111, 89, 246, 18, 76, 178, 252, 77, 219, 102, 185, 114, 197, 12, - 114, 192, 138, 45, 225, 107, 22, 106, 224, 41, 137, 103, 248, 223, 22, 39, 61, 244, 177, 198, - 228, 253, 97, 173, 154, 127, 57, 66, 129, 207, 106, 251, 250, 124, 108, 169, 249, 27, 120, 207, - 2, 180, 162, 185, 140, 51, 198, 145, 199, 171, 237, 142, 132, 163, 120, 55, 143, 186, 52, 166, - 177, 88, 152, 150, 179, 120, 123, 7, 138, 22, 8, 51, 13, 121, 209, 194, 84, 146, 81, 13, 118, - 191, 45, 15, 209, 108, 251, 217, 50, 208, 151, 247, 91, 64, 237, 174, 212, 192, 63, 52, 37, - 215, 6, 133, 135, 109, 38, 76, 158, 194, 112, 134, 65, 161, 23, 130, 7, 29, 86, 115, 26, 184, - 186, 17, 7, 4, 35, 160, 91, 122, 92, 47, 92, 145, 117, 116, 211, 43, 187, 31, 53, 154, 224, - 251, 167, 31, 167, 102, 25, 129, 146, 233, 128, 114, 177, 12, 67, 71, 44, 105, 254, 223, 89, - 216, 115, 42, 45, 247, 206, 176, 156, 12, 153, 45, 13, 251, 215, 172, 248, 210, 214, 153, 245, - 13, 166, 237, 50, 171, 170, 143, 159, 139, 197, 2, 32, 5, 122, 15, 22, 10, 222, 7, 141, 106, - 185, 213, 182, 78, 156, 247, 30, 131, 31, 96, 185, 53, 44, 175, 52, 103, 249, 122, 2, 119, 203, - 48, 123, 32, 0, 95, 225, 163, 231, 83, 239, 204, 46, 84, 198, 238, 164, 16, 232, 56, 235, 142, - 213, 20, 111, 225, 130, 98, 219, 68, 235, 253, 122, 155, 164, 100, 2, 8, 146, 244, 224, 197, - 117, 213, 192, 188, 114, 247, 51, 255, 118, 31, 230, 101, 225, 189, 171, 161, 46, 4, 222, 233, - 208, 72, 240, 45, 198, 29, 108, 255, 219, 22, 52, 188, 101, 175, 238, 251, 217, 42, 252, 130, - 173, 185, 82, 55, 1, 61, 248, 122, 61, 130, 157, 213, 235, 44, 103, 164, 94, 114, 88, 245, 99, - 20, 43, 249, 57, 98, 86, 177, 77, 57, 34, 62, 194, 40, 56, 105, 63, 142, 57, 215, 243, 49, 109, - 59, 109, 185, 142, 251, 56, 193, 39, 252, 195, 136, 193, 240, 72, 65, 241, 249, 71, 116, 224, - 227, 15, 162, 221, 45, 63, 46, 167, 114, 124, 200, 166, 251, 114, 77, 178, 34, 84, 169, 165, - 246, 66, 73, 78, 113, 115, 1, 120, 194, 171, 65, 135, 89, 54, 133, 205, 114, 2, 37, 232, 112, - 176, 15, 197, 164, 17, 248, 213, 189, 94, 22, 162, 91, 206, 237, 171, 111, 228, 106, 7, 67, - 203, 137, 134, 203, 148, 74, 76, 195, 150, 243, 35, 225, 237, 255, 101, 214, 133, 134, 186, - 220, 164, 167, 111, 79, 98, 26, 96, 213, 132, 26, 235, 97, 233, 166, 103, 42, 66, 145, 182, - 169, 127, 195, 28, 89, 69, 244, 193, 213, 237, 209, 167, 164, 31, 25, 35, 187, 229, 146, 117, - 67, 238, 190, 16, 96, 92, 169, 231, 87, 127, 81, 158, 229, 8, 37, 233, 213, 154, 139, 101, 103, - 128, 137, 232, 243, 179, 19, 171, 248, 8, 190, 219, 231, 208, 130, 25, 160, 241, 83, 152, 82, - 255, 6, 105, 60, 98, 180, 167, 18, 207, 242, 219, 206, 231, 234, 133, 23, 46, 31, 210, 98, 55, - 3, 185, 241, 81, 183, 10, 160, 199, 70, 246, 114, 29, 188, 19, 192, 161, 232, 150, 188, 199, - 186, 119, 192, 189, 101, 36, 57, 192, 251, 86, 202, 34, 218, 223, 60, 208, 196, 229, 46, 246, - 84, 27, 164, 74, 125, 185, 72, 240, 169, 55, 218, 7, 246, 177, 217, 159, 216, 67, 57, 112, 240, - 196, 8, 169, 236, 252, 187, 134, 180, 238, 106, 87, 248, 45, 26, 51, 89, 121, 208, 51, 64, 232, - 125, 22, 84, 176, 171, 67, 74, 22, 183, 244, 158, 204, 152, 84, 181, 211, 172, 142, 234, 220, - 9, 99, 223, 22, 246, 69, 69, 234, 146, 80, 90, 45, 203, 22, 48, 132, 255, 43, 182, 41, 114, - 225, 11, 234, 215, 180, 37, 148, 50, 36, 99, 91, 230, 108, 72, 148, 11, 43, 179, 194, 199, 77, - 158, 6, 76, 215, 176, 65, 68, 202, 156, 111, 130, 150, 8, 177, 76, 131, 101, 145, 253, 3, 135, - 186, 26, 10, 98, 199, 227, 177, 37, 212, 10, 191, 95, 30, 178, 47, 58, 193, 111, 161, 13, 124, - 159, 148, 167, 229, 75, 215, 25, 50, 185, 158, 146, 208, 222, 191, 50, 54, 152, 19, 171, 153, - 57, 88, 69, 45, 152, 185, 233, 13, 250, 36, 169, 142, 104, 60, 213, 67, 240, 88, 59, 32, 5, 25, - 218, 170, 21, 19, 197, 25, 96, 62, 135, 47, 191, 131, 75, 242, 52, 121, 158, 219, 78, 198, 71, - 224, 95, 226, 34, 50, 195, 97, 89, 175, 32, 5, 104, 136, 248, 25, 231, 79, 87, 221, 122, 76, - 120, 203, 74, 63, 155, 73, 158, 1, 72, 120, 34, 139, 5, 163, 50, 15, 139, 250, 128, 32, 25, 32, - 1, 238, 94, 9, 224, 129, 19, 9, 4, 253, 148, 151, 58, 16, 214, 120, 153, 100, 172, 59, 143, - 249, 45, 255, 70, 94, 115, 225, 168, 145, 49, 25, 32, 0, 93, 48, 192, 190, 49, 227, 239, 122, - 111, 235, 47, 28, 94, 105, 63, 198, 208, 250, 34, 195, 198, 89, 19, 38, 18, 247, 105, 195, 41, - 0, 101, 32, 6, 149, 110, 208, 77, 35, 168, 29, 225, 72, 230, 174, 157, 195, 117, 212, 55, 6, - 122, 99, 217, 208, 158, 171, 108, 77, 85, 215, 0, 68, 19, 162, 32, 5, 125, 223, 60, 106, 149, - 196, 158, 212, 162, 97, 92, 191, 247, 123, 178, 77, 198, 155, 218, 121, 49, 27, 32, 89, 255, - 19, 80, 114, 146, 14, 154, 32, 1, 55, 176, 227, 181, 169, 97, 105, 167, 180, 225, 145, 124, 24, - 76, 11, 173, 19, 97, 50, 84, 31, 175, 125, 135, 189, 252, 97, 157, 245, 39, 232, 32, 0, 155, - 216, 113, 218, 212, 176, 180, 211, 218, 112, 200, 190, 12, 38, 5, 214, 137, 176, 153, 42, 15, - 215, 190, 195, 222, 254, 48, 206, 250, 147, 244, 32, 2, 124, 161, 58, 67, 16, 110, 14, 171, - 135, 79, 98, 202, 148, 3, 151, 4, 109, 88, 109, 184, 59, 154, 206, 149, 3, 188, 179, 216, 242, - 166, 76, 32, 2, 108, 234, 247, 211, 61, 236, 193, 178, 197, 162, 71, 76, 221, 226, 176, 114, - 195, 155, 31, 18, 206, 116, 191, 29, 191, 163, 2, 209, 175, 127, 115, 32, 4, 189, 134, 36, 68, - 156, 24, 101, 33, 49, 47, 4, 216, 201, 207, 243, 175, 165, 4, 6, 66, 160, 21, 67, 204, 9, 82, - 199, 98, 221, 232, 238, 32, 3, 27, 229, 34, 251, 197, 33, 70, 65, 72, 194, 146, 254, 5, 26, - 220, 146, 165, 98, 198, 139, 10, 88, 15, 161, 4, 184, 48, 78, 59, 148, 233, 32, 2, 197, 111, - 192, 47, 2, 137, 23, 222, 237, 84, 236, 179, 134, 194, 191, 90, 229, 136, 198, 1, 189, 10, 4, - 17, 22, 116, 128, 252, 191, 98, 87, 32, 2, 40, 158, 82, 67, 243, 218, 62, 217, 54, 60, 27, 133, - 87, 170, 180, 184, 221, 24, 203, 172, 246, 156, 133, 147, 197, 206, 36, 211, 14, 144, 47, 32, - 7, 67, 4, 42, 119, 160, 2, 220, 46, 53, 53, 12, 46, 57, 197, 238, 117, 151, 12, 134, 249, 174, - 253, 49, 252, 247, 36, 173, 218, 252, 164, 106, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 4, 159, 60, 37, 240, 222, 46, 37, 0, - 169, 24, 233, 62, 155, 62, 244, 88, 187, 66, 3, 181, 29, 127, 88, 28, 21, 161, 199, 76, 37, 71, - 157, 32, 3, 95, 21, 84, 123, 217, 153, 109, 228, 161, 68, 236, 128, 30, 174, 129, 221, 62, 180, - 187, 25, 35, 4, 22, 242, 49, 37, 115, 64, 93, 150, 165, 32, 7, 80, 121, 33, 32, 74, 2, 21, 219, - 224, 212, 236, 217, 21, 38, 205, 52, 156, 47, 109, 72, 191, 184, 71, 91, 73, 1, 248, 243, 172, - 103, 236, 32, 4, 36, 216, 247, 210, 220, 235, 207, 152, 236, 150, 124, 26, 45, 204, 255, 118, - 231, 165, 72, 126, 205, 167, 85, 239, 63, 135, 93, 122, 241, 86, 78, 32, 5, 47, 184, 33, 147, - 250, 150, 84, 40, 36, 187, 154, 75, 129, 41, 19, 43, 80, 240, 57, 61, 224, 17, 77, 26, 98, 168, - 71, 138, 168, 1, 75, 32, 0, 193, 80, 45, 189, 34, 108, 62, 106, 2, 106, 17, 244, 104, 124, 116, - 9, 203, 253, 86, 106, 95, 34, 96, 253, 213, 240, 7, 16, 194, 172, 55, 32, 6, 138, 187, 194, - 179, 90, 208, 242, 51, 44, 182, 103, 218, 122, 81, 1, 96, 61, 235, 231, 79, 247, 96, 30, 172, - 2, 188, 15, 254, 248, 30, 135, 32, 5, 106, 52, 104, 220, 144, 92, 7, 26, 44, 63, 120, 183, 123, - 219, 28, 21, 142, 128, 85, 149, 178, 194, 9, 17, 30, 44, 140, 109, 121, 180, 5, 32, 5, 181, 34, - 125, 201, 6, 145, 222, 163, 225, 195, 3, 134, 163, 192, 44, 189, 201, 16, 213, 211, 48, 19, - 192, 41, 113, 220, 93, 156, 79, 194, 62, 32, 3, 191, 105, 12, 117, 23, 129, 8, 135, 99, 6, 208, - 223, 176, 106, 1, 119, 52, 228, 18, 128, 13, 222, 60, 63, 110, 159, 178, 83, 242, 117, 252, 32, - 7, 115, 74, 75, 196, 58, 24, 131, 163, 43, 72, 36, 124, 199, 180, 240, 199, 83, 234, 202, 175, - 197, 99, 139, 75, 180, 104, 233, 228, 84, 234, 207, 32, 6, 126, 229, 98, 91, 11, 153, 57, 82, - 92, 154, 240, 224, 86, 94, 73, 209, 99, 130, 134, 90, 170, 222, 111, 195, 49, 39, 180, 118, 82, - 111, 201, 32, 7, 121, 120, 78, 137, 189, 174, 214, 6, 61, 190, 228, 2, 202, 93, 125, 17, 166, - 214, 133, 123, 117, 247, 149, 154, 41, 187, 146, 243, 22, 80, 77, 32, 0, 31, 17, 80, 225, 142, - 59, 251, 55, 0, 117, 16, 188, 249, 109, 67, 218, 205, 28, 101, 37, 197, 34, 119, 33, 63, 134, - 14, 56, 171, 180, 253, 32, 5, 143, 33, 88, 127, 111, 0, 209, 74, 135, 73, 170, 1, 188, 215, 11, - 180, 217, 221, 244, 15, 181, 10, 166, 54, 245, 3, 194, 104, 16, 148, 122, 32, 4, 243, 1, 105, - 131, 38, 58, 93, 194, 236, 104, 155, 231, 207, 43, 238, 235, 191, 47, 253, 188, 235, 35, 96, - 171, 140, 187, 45, 1, 21, 204, 108, 32, 4, 187, 218, 122, 32, 147, 200, 121, 17, 168, 14, 22, - 111, 188, 77, 154, 12, 170, 196, 158, 191, 145, 28, 97, 181, 34, 245, 98, 250, 234, 180, 141, - 32, 2, 49, 191, 235, 85, 40, 229, 161, 53, 85, 145, 252, 187, 51, 173, 22, 192, 237, 144, 172, - 100, 225, 63, 108, 181, 252, 106, 114, 6, 113, 36, 106, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, - 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 239, 255, 225, 32, 1, 196, 53, 181, 95, 75, 31, 202, 227, 211, 39, 177, 45, - 54, 198, 194, 16, 11, 44, 51, 60, 182, 42, 96, 103, 8, 74, 94, 146, 232, 102, 186, 32, 1, 66, - 251, 23, 165, 84, 44, 67, 206, 195, 81, 235, 119, 88, 133, 27, 74, 22, 85, 155, 187, 25, 123, - 162, 22, 99, 167, 78, 127, 57, 137, 102, 32, 1, 66, 251, 23, 165, 84, 44, 67, 206, 195, 81, - 235, 119, 88, 133, 27, 74, 22, 85, 155, 187, 25, 123, 162, 22, 99, 167, 78, 127, 57, 137, 102, - 32, 1, 66, 251, 23, 165, 84, 44, 67, 206, 195, 81, 235, 119, 88, 133, 27, 74, 22, 85, 155, 187, - 25, 123, 162, 22, 99, 167, 78, 127, 57, 137, 102, 32, 0, 223, 36, 6, 78, 160, 147, 139, 200, - 120, 217, 153, 132, 74, 8, 77, 233, 17, 244, 16, 96, 179, 1, 17, 115, 248, 16, 81, 132, 77, - 176, 186, 32, 0, 10, 88, 194, 113, 195, 231, 180, 138, 180, 27, 131, 125, 27, 70, 124, 223, 90, - 184, 99, 45, 91, 31, 223, 22, 27, 88, 186, 223, 95, 251, 190, 32, 2, 123, 65, 235, 137, 171, - 17, 184, 216, 95, 54, 207, 221, 236, 39, 114, 248, 151, 226, 192, 78, 75, 128, 148, 109, 176, - 146, 155, 61, 198, 9, 220, 32, 2, 18, 177, 85, 222, 153, 101, 197, 17, 101, 54, 245, 83, 144, - 124, 30, 212, 80, 111, 63, 177, 165, 108, 246, 214, 37, 191, 58, 78, 164, 22, 153, 32, 7, 195, - 179, 96, 23, 235, 170, 212, 249, 138, 153, 225, 176, 77, 198, 23, 98, 228, 81, 255, 185, 39, - 90, 103, 193, 240, 39, 47, 53, 207, 150, 239, 32, 2, 127, 141, 47, 10, 46, 91, 17, 177, 195, - 243, 85, 39, 73, 59, 77, 227, 117, 206, 24, 215, 215, 206, 125, 70, 86, 35, 26, 171, 156, 84, - 251, 32, 2, 101, 9, 124, 118, 138, 84, 243, 87, 218, 183, 37, 172, 76, 115, 249, 223, 43, 156, - 122, 192, 205, 155, 167, 26, 27, 184, 214, 198, 9, 73, 10, 32, 0, 9, 58, 185, 83, 31, 206, 79, - 153, 184, 113, 120, 179, 206, 79, 221, 198, 211, 215, 146, 120, 89, 212, 231, 53, 80, 132, 56, - 21, 180, 163, 248, 32, 0, 203, 141, 26, 73, 187, 111, 47, 11, 202, 176, 28, 117, 32, 53, 153, - 52, 133, 19, 116, 103, 122, 197, 170, 211, 147, 167, 249, 107, 243, 118, 123, 32, 3, 183, 49, - 253, 65, 119, 210, 134, 202, 192, 95, 233, 165, 92, 233, 70, 206, 64, 118, 252, 124, 118, 75, - 141, 251, 19, 181, 194, 116, 81, 142, 44, 32, 2, 80, 254, 240, 107, 219, 6, 237, 117, 77, 205, - 85, 182, 127, 219, 76, 62, 206, 140, 212, 157, 214, 221, 216, 211, 252, 133, 5, 40, 23, 226, - 61, 32, 6, 9, 221, 75, 210, 80, 32, 175, 115, 26, 202, 147, 37, 131, 94, 68, 197, 11, 177, 237, - 160, 31, 5, 168, 178, 84, 20, 29, 200, 64, 111, 195, 32, 0, 178, 82, 189, 124, 84, 65, 183, - 242, 70, 28, 182, 15, 28, 202, 161, 68, 217, 195, 155, 28, 147, 114, 114, 35, 247, 114, 45, - 119, 36, 109, 210, 32, 5, 109, 48, 52, 214, 147, 98, 67, 106, 157, 52, 151, 163, 148, 126, 166, - 152, 60, 240, 27, 36, 160, 167, 10, 49, 22, 89, 13, 65, 58, 101, 1, 32, 0, 195, 243, 205, 230, - 246, 33, 54, 63, 127, 194, 33, 20, 231, 176, 85, 64, 130, 70, 106, 17, 243, 213, 145, 70, 37, - 49, 126, 34, 220, 40, 144, 32, 5, 13, 151, 140, 38, 44, 28, 91, 7, 148, 83, 179, 140, 47, 181, - 16, 31, 28, 30, 1, 145, 181, 252, 46, 19, 95, 251, 152, 45, 148, 19, 232, 32, 4, 144, 27, 220, - 221, 194, 121, 212, 41, 235, 129, 119, 182, 30, 212, 196, 0, 216, 26, 191, 217, 126, 255, 192, - 88, 148, 134, 172, 180, 196, 66, 100, 32, 3, 137, 15, 197, 80, 93, 126, 178, 234, 144, 114, - 244, 252, 46, 11, 234, 25, 249, 202, 109, 95, 231, 188, 240, 104, 176, 199, 35, 136, 51, 30, - 234, 32, 6, 51, 196, 113, 60, 194, 36, 158, 221, 158, 174, 63, 154, 42, 42, 62, 107, 157, 46, - 114, 68, 47, 16, 101, 112, 77, 231, 213, 202, 65, 39, 192, 7, 185, 129, 243, 126, 12, 23, 81, - 193, 216, 100, 105, 241, 96, 224, 222, 187, 41, 87, 235, 59, 254, 254, 106, 215, 151, 198, 216, - 137, 87, 154, 17, 37, 116, 103, 217, 130, 125, 42, 71, 113, 160, 130, 226, 178, 176, 106, 211, - 188, 187, 126, 255, 56, 142, 114, 209, 252, 111, 34, 196, 50, 201, 217, 176, 242, 186, 227, 61, - 88, 41, 243, 62, 108, 65, 247, 58, 160, 126, 117, 196, 165, 187, 174, 40, 172, 249, 159, 23, - 38, 110, 145, 238, 99, 102, 145, 192, 151, 109, 212, 7, 144, 241, 219, 159, 240, 4, 186, 81, - 176, 23, 62, 250, 13, 221, 38, 179, 166, 120, 75, 229, 196, 180, 179, 248, 57, 206, 40, 99, - 254, 51, 230, 152, 210, 217, 212, 43, 71, 107, 171, 142, 71, 14, 62, 110, 41, 182, 36, 32, 96, - 88, 122, 254, 193, 218, 68, 147, 165, 70, 14, 28, 171, 15, 190, 222, 28, 255, 161, 229, 154, - 51, 48, 225, 56, 151, 205, 40, 203, 139, 117, 226, 253, 28, 32, 75, 6, 59, 72, 255, 67, 219, - 118, 21, 125, 51, 45, 171, 60, 71, 178, 34, 1, 103, 238, 163, 124, 189, 119, 30, 6, 204, 249, - 51, 163, 124, 49, 48, 66, 231, 52, 43, 104, 108, 3, 211, 196, 2, 32, 4, 76, 102, 247, 56, 162, - 75, 188, 245, 174, 117, 185, 145, 137, 149, 242, 0, 186, 86, 29, 125, 189, 83, 94, 148, 201, - 213, 210, 183, 26, 142, 14, 32, 7, 180, 142, 178, 208, 178, 94, 202, 162, 156, 127, 198, 22, - 62, 204, 155, 159, 70, 215, 19, 191, 180, 25, 148, 2, 251, 209, 157, 254, 186, 209, 147, 2, 8, - 185, 237, 7, 2, 9, 16, 91, 169, 86, 161, 120, 228, 11, 206, 14, 99, 215, 152, 177, 87, 126, - 214, 96, 52, 84, 130, 105, 108, 62, 181, 215, 220, 139, 170, 141, 93, 121, 239, 194, 14, 102, - 7, 189, 196, 115, 187, 60, 215, 18, 222, 131, 7, 75, 212, 188, 192, 76, 203, 238, 85, 79, 12, - 54, 231, 86, 151, 55, 0, 220, 136, 78, 126, 115, 209, 44, 92, 28, 137, 98, 83, 22, 139, 55, - 156, 16, 89, 230, 102, 74, 159, 250, 44, 229, 98, 135, 143, 187, 208, 228, 16, 121, 134, 209, - 217, 57, 22, 60, 30, 69, 253, 111, 210, 50, 102, 144, 215, 31, 249, 250, 32, 200, 168, 28, 236, - 35, 123, 186, 205, 119, 105, 144, 137, 225, 179, 93, 226, 177, 47, 220, 80, 135, 139, 16, 59, - 142, 114, 145, 122, 199, 250, 90, 16, 218, 146, 188, 78, 179, 117, 0, 127, 65, 73, 175, 31, - 243, 113, 84, 117, 9, 243, 236, 149, 222, 78, 175, 15, 254, 3, 30, 184, 97, 18, 206, 47, 248, - 251, 115, 153, 87, 228, 133, 185, 202, 162, 8, 67, 150, 128, 8, 9, 249, 181, 204, 76, 95, 153, - 145, 183, 153, 231, 164, 80, 156, 141, 63, 16, 150, 55, 32, 29, 23, 94, 121, 91, 230, 102, 86, - 203, 113, 159, 74, 128, 251, 129, 47, 111, 246, 84, 15, 173, 114, 238, 116, 16, 155, 12, 31, - 135, 15, 42, 107, 57, 231, 57, 31, 248, 8, 141, 26, 47, 190, 213, 221, 52, 152, 105, 214, 197, - 12, 40, 5, 33, 57, 248, 188, 82, 116, 140, 18, 217, 43, 37, 83, 136, 56, 44, 58, 17, 121, 63, - 224, 18, 67, 171, 13, 122, 132, 252, 153, 36, 224, 133, 210, 189, 180, 69, 146, 227, 165, 98, - 191, 84, 224, 137, 164, 97, 2, 32, 195, 224, 144, 88, 12, 7, 243, 172, 61, 5, 72, 70, 231, 153, - 25, 126, 141, 100, 242, 218, 86, 20, 90, 112, 209, 214, 32, 139, 78, 52, 92, 234, 90, 86, 132, - 197, 86, 73, 49, 109, 163, 185, 196, 195, 155, 141, 71, 195, 163, 248, 147, 134, 215, 134, 31, - 228, 223, 171, 121, 215, 38, 128, 8, 219, 153, 215, 227, 201, 119, 48, 125, 31, 23, 13, 80, - 120, 151, 246, 202, 241, 51, 242, 209, 218, 235, 129, 8, 75, 194, 189, 27, 29, 123, 71, 22, 84, - 43, 35, 129, 61, 245, 107, 25, 19, 158, 188, 56, 201, 24, 23, 148, 115, 220, 185, 3, 231, 184, - 65, 25, 159, 105, 232, 92, 122, 22, 105, 104, 136, 15, 213, 20, 47, 241, 143, 10, 103, 38, 150, - 60, 79, 110, 163, 8, 253, 180, 70, 46, 57, 62, 228, 197, 202, 96, 179, 17, 52, 135, 35, 192, - 169, 105, 94, 140, 38, 226, 17, 200, 114, 66, 199, 188, 124, 141, 168, 29, 23, 194, 195, 44, - 138, 2, 56, 50, 173, 213, 76, 21, 151, 35, 181, 172, 207, 17, 240, 231, 59, 32, 6, 214, 173, - 218, 190, 111, 132, 52, 230, 226, 50, 45, 215, 184, 146, 202, 241, 20, 124, 158, 92, 46, 106, - 82, 81, 0, 171, 137, 38, 7, 25, 82, 32, 1, 84, 72, 210, 222, 186, 171, 17, 16, 224, 213, 207, - 124, 167, 167, 152, 202, 229, 9, 183, 97, 128, 241, 1, 94, 227, 205, 248, 54, 27, 117, 141, 32, - 3, 2, 72, 120, 93, 243, 130, 98, 113, 116, 60, 177, 18, 101, 19, 84, 156, 97, 133, 92, 1, 63, - 124, 183, 253, 137, 8, 27, 209, 24, 88, 185, 32, 0, 122, 78, 118, 229, 190, 149, 251, 119, 23, - 52, 180, 146, 20, 11, 187, 142, 133, 243, 179, 136, 126, 20, 146, 181, 219, 81, 243, 231, 236, - 182, 8, 32, 2, 225, 5, 53, 227, 210, 233, 89, 170, 120, 59, 16, 52, 203, 49, 120, 112, 2, 241, - 9, 58, 77, 86, 152, 1, 105, 99, 124, 3, 22, 88, 52, 32, 3, 211, 122, 101, 202, 49, 2, 159, 39, - 242, 102, 118, 37, 194, 235, 16, 78, 236, 80, 79, 159, 58, 8, 198, 163, 34, 41, 85, 243, 10, - 165, 79, 32, 1, 193, 123, 253, 133, 169, 51, 44, 62, 245, 92, 247, 54, 152, 71, 177, 146, 167, - 145, 243, 165, 11, 67, 157, 196, 52, 190, 76, 190, 137, 197, 223, 32, 4, 224, 189, 254, 194, - 212, 153, 158, 159, 122, 174, 123, 155, 76, 35, 216, 201, 83, 200, 249, 210, 133, 161, 206, - 226, 26, 95, 38, 95, 68, 226, 240, 32, 4, 138, 36, 155, 128, 30, 157, 177, 233, 96, 182, 159, - 21, 167, 19, 232, 28, 162, 65, 90, 189, 143, 147, 60, 172, 83, 132, 114, 91, 118, 21, 47, 32, - 3, 66, 127, 133, 166, 217, 164, 27, 180, 207, 185, 245, 18, 250, 176, 35, 103, 212, 163, 194, - 250, 21, 240, 188, 173, 222, 78, 163, 110, 33, 246, 205, 32, 3, 188, 208, 75, 81, 194, 250, - 231, 255, 53, 210, 168, 71, 22, 123, 141, 8, 74, 173, 8, 200, 31, 215, 72, 130, 45, 171, 188, - 226, 98, 179, 105, 32, 0, 60, 114, 159, 181, 249, 107, 178, 53, 118, 32, 142, 210, 132, 16, - 203, 57, 180, 230, 81, 140, 74, 218, 231, 107, 41, 199, 61, 198, 178, 92, 200, 32, 0, 207, 160, - 97, 233, 192, 245, 68, 14, 217, 125, 135, 109, 239, 108, 122, 105, 90, 228, 200, 103, 32, 188, - 32, 241, 119, 167, 98, 37, 49, 4, 122, 32, 2, 15, 172, 34, 6, 74, 78, 224, 158, 104, 236, 250, - 98, 230, 118, 180, 253, 190, 113, 227, 227, 17, 89, 217, 42, 164, 178, 145, 92, 141, 228, 91, - 32, 7, 33, 155, 173, 33, 217, 120, 91, 104, 215, 213, 222, 121, 116, 61, 86, 54, 215, 149, 207, - 197, 213, 111, 65, 208, 152, 174, 39, 218, 26, 149, 229, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 211, 127, 37, 118, 43, - 160, 128, 160, 83, 21, 91, 160, 69, 181, 0, 96, 181, 193, 248, 179, 67, 72, 195, 77, 241, 179, - 145, 69, 252, 0, 134, 32, 0, 145, 105, 21, 120, 81, 70, 186, 227, 114, 75, 117, 223, 91, 194, - 45, 38, 144, 202, 255, 207, 188, 241, 156, 204, 33, 26, 251, 30, 196, 98, 48, 32, 6, 97, 62, - 102, 41, 112, 254, 80, 220, 35, 165, 152, 101, 38, 123, 227, 25, 122, 129, 184, 195, 165, 163, - 84, 183, 100, 3, 228, 3, 187, 58, 191, 32, 1, 46, 111, 190, 173, 92, 197, 120, 233, 63, 189, - 208, 114, 177, 243, 201, 50, 69, 43, 221, 88, 136, 86, 155, 157, 191, 116, 139, 226, 233, 169, - 173, 32, 2, 97, 248, 193, 95, 248, 204, 192, 87, 209, 246, 102, 78, 186, 30, 249, 68, 95, 106, - 25, 96, 46, 179, 253, 39, 67, 46, 60, 189, 114, 204, 163, 32, 0, 16, 85, 152, 122, 104, 121, - 181, 172, 76, 29, 250, 104, 184, 225, 47, 0, 161, 167, 174, 26, 41, 195, 109, 37, 51, 82, 49, - 207, 95, 172, 233, 32, 4, 75, 9, 119, 219, 200, 226, 151, 83, 154, 172, 222, 200, 202, 28, 233, - 193, 248, 193, 230, 80, 101, 57, 162, 75, 249, 198, 146, 65, 174, 243, 29, 32, 6, 129, 175, - 250, 17, 227, 135, 49, 99, 0, 79, 252, 42, 155, 166, 0, 1, 91, 183, 211, 2, 139, 146, 98, 68, - 230, 117, 70, 124, 198, 132, 177, 32, 6, 136, 163, 62, 213, 198, 47, 62, 133, 199, 115, 215, - 183, 213, 70, 229, 251, 127, 252, 111, 21, 60, 110, 91, 77, 75, 183, 108, 62, 66, 235, 233, 32, - 2, 53, 67, 254, 134, 85, 244, 58, 71, 154, 158, 141, 37, 103, 42, 156, 114, 26, 214, 251, 130, - 216, 238, 104, 26, 255, 57, 105, 217, 30, 197, 30, 32, 5, 215, 123, 64, 210, 236, 25, 222, 165, - 104, 154, 207, 14, 159, 73, 15, 13, 117, 154, 56, 72, 176, 129, 67, 20, 241, 108, 132, 241, 54, - 89, 57, 32, 7, 239, 71, 83, 245, 85, 31, 250, 176, 73, 94, 140, 37, 46, 72, 203, 150, 79, 68, - 26, 51, 244, 107, 103, 71, 239, 118, 35, 237, 67, 183, 48, 32, 2, 67, 176, 132, 110, 91, 110, - 247, 41, 133, 86, 54, 106, 62, 183, 160, 119, 156, 81, 131, 132, 197, 55, 77, 231, 138, 170, - 110, 245, 71, 206, 150, 32, 4, 29, 216, 224, 193, 231, 155, 162, 171, 175, 192, 103, 201, 181, - 150, 28, 209, 46, 137, 105, 175, 54, 157, 242, 235, 192, 25, 105, 57, 54, 228, 177, 32, 3, 158, - 80, 240, 174, 229, 103, 178, 208, 149, 230, 251, 198, 109, 70, 240, 72, 101, 61, 193, 214, 241, - 247, 165, 242, 213, 58, 139, 58, 100, 111, 47, 32, 3, 254, 132, 240, 234, 142, 122, 94, 116, - 130, 158, 52, 131, 164, 229, 212, 67, 230, 212, 164, 23, 157, 2, 126, 228, 120, 232, 111, 2, - 163, 232, 155, 32, 5, 61, 128, 55, 63, 2, 128, 82, 118, 69, 27, 130, 57, 14, 87, 48, 150, 151, - 144, 171, 54, 232, 137, 3, 253, 1, 242, 26, 220, 26, 81, 149, 32, 5, 57, 118, 112, 212, 52, 76, - 34, 244, 232, 159, 201, 159, 188, 228, 220, 7, 59, 44, 80, 65, 186, 13, 187, 207, 105, 138, - 233, 74, 193, 65, 59, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, - 32, 7, 55, 243, 202, 184, 150, 108, 89, 85, 63, 214, 136, 228, 140, 218, 99, 196, 126, 255, 21, - 117, 23, 218, 226, 98, 27, 129, 30, 30, 207, 49, 247, 32, 0, 51, 246, 197, 92, 62, 51, 150, 94, - 20, 164, 247, 9, 39, 103, 247, 172, 252, 98, 73, 30, 240, 224, 229, 46, 242, 98, 101, 114, 234, - 202, 152, 32, 0, 51, 246, 197, 92, 62, 51, 150, 94, 20, 164, 247, 9, 39, 103, 247, 172, 252, - 98, 73, 30, 240, 224, 229, 46, 242, 98, 101, 114, 234, 202, 152, 32, 0, 51, 246, 197, 92, 62, - 51, 150, 94, 20, 164, 247, 9, 39, 103, 247, 172, 252, 98, 73, 30, 240, 224, 229, 46, 242, 98, - 101, 114, 234, 202, 152, 32, 7, 104, 198, 51, 126, 159, 201, 170, 123, 1, 138, 56, 236, 198, - 220, 61, 254, 169, 158, 133, 193, 25, 109, 142, 20, 92, 108, 145, 8, 249, 115, 206, 32, 5, 238, - 89, 103, 75, 246, 225, 191, 219, 236, 156, 122, 56, 230, 195, 77, 29, 15, 105, 132, 109, 152, - 176, 97, 168, 142, 141, 11, 224, 200, 104, 77, 32, 5, 132, 32, 228, 241, 252, 109, 24, 2, 136, - 44, 122, 212, 175, 254, 125, 53, 97, 49, 232, 42, 106, 71, 178, 77, 106, 85, 8, 139, 164, 31, - 151, 32, 6, 30, 208, 162, 18, 150, 1, 177, 87, 238, 4, 87, 23, 21, 135, 5, 184, 243, 195, 137, - 165, 32, 186, 99, 171, 54, 205, 88, 36, 123, 218, 13, 32, 2, 204, 180, 163, 100, 118, 68, 166, - 73, 87, 243, 198, 250, 176, 166, 146, 250, 166, 249, 230, 65, 93, 45, 255, 143, 224, 210, 31, - 242, 172, 83, 238, 32, 6, 86, 151, 113, 239, 3, 96, 201, 52, 75, 230, 86, 92, 111, 33, 13, 92, - 24, 148, 128, 166, 14, 136, 115, 26, 29, 124, 107, 3, 235, 231, 245, 32, 4, 3, 136, 206, 22, - 215, 97, 224, 52, 45, 25, 46, 220, 208, 28, 81, 141, 76, 209, 128, 5, 189, 109, 58, 197, 21, - 68, 132, 55, 55, 83, 19, 32, 3, 117, 252, 111, 205, 82, 100, 222, 249, 234, 136, 127, 170, 162, - 216, 203, 164, 33, 5, 250, 103, 201, 77, 113, 112, 51, 166, 140, 56, 206, 86, 185, 32, 5, 27, - 50, 140, 154, 213, 43, 50, 25, 103, 142, 79, 230, 158, 241, 116, 89, 136, 5, 93, 53, 52, 180, - 83, 59, 210, 122, 90, 127, 91, 81, 161, 32, 3, 168, 169, 95, 100, 158, 95, 90, 235, 132, 148, - 20, 1, 101, 118, 74, 61, 234, 179, 210, 85, 80, 57, 95, 204, 220, 24, 153, 253, 89, 158, 222, - 32, 0, 188, 213, 18, 86, 250, 143, 101, 216, 78, 84, 172, 151, 157, 175, 84, 14, 214, 167, 212, - 158, 112, 167, 139, 141, 185, 248, 46, 53, 201, 6, 36, 32, 1, 158, 159, 142, 231, 0, 218, 208, - 6, 81, 150, 155, 224, 72, 228, 187, 157, 242, 222, 163, 186, 71, 40, 227, 232, 157, 26, 250, - 21, 221, 232, 31, 32, 6, 129, 140, 70, 18, 13, 97, 66, 231, 165, 252, 195, 139, 133, 45, 199, - 157, 128, 63, 23, 105, 145, 184, 197, 239, 149, 172, 67, 0, 213, 1, 212, 32, 1, 52, 14, 31, - 180, 76, 194, 207, 196, 103, 142, 14, 225, 243, 194, 54, 168, 213, 196, 180, 93, 230, 134, 58, - 193, 171, 136, 210, 219, 212, 246, 203, 32, 4, 173, 124, 64, 215, 91, 108, 105, 98, 157, 149, - 26, 9, 251, 88, 134, 31, 122, 36, 57, 213, 38, 47, 92, 16, 180, 49, 149, 96, 235, 86, 91, 32, - 6, 108, 250, 129, 12, 150, 254, 190, 213, 75, 227, 208, 187, 237, 183, 32, 183, 100, 58, 246, - 34, 236, 58, 108, 129, 31, 16, 25, 139, 239, 61, 37, 32, 4, 168, 34, 125, 125, 129, 149, 246, - 153, 243, 190, 171, 181, 23, 233, 118, 244, 60, 197, 76, 199, 140, 114, 13, 35, 5, 92, 22, 193, - 28, 39, 214, 32, 4, 142, 7, 201, 252, 99, 247, 167, 6, 179, 191, 225, 211, 11, 134, 24, 77, - 216, 237, 42, 228, 128, 160, 8, 206, 240, 32, 44, 43, 125, 128, 116, 32, 6, 34, 241, 202, 126, - 63, 60, 89, 80, 61, 5, 78, 197, 98, 88, 110, 192, 135, 211, 87, 132, 154, 11, 13, 231, 161, 67, - 100, 44, 21, 106, 184, 3, 7, 143, 227, 159, 164, 85, 246, 234, 50, 196, 91, 38, 46, 24, 101, - 98, 89, 172, 100, 14, 251, 66, 137, 81, 246, 211, 156, 208, 4, 122, 213, 140, 130, 53, 82, 202, - 111, 213, 169, 243, 94, 112, 245, 75, 185, 11, 236, 112, 194, 41, 200, 247, 216, 56, 153, 254, - 12, 227, 169, 77, 8, 103, 81, 25, 187, 88, 142, 120, 152, 104, 74, 247, 5, 9, 0, 23, 158, 66, - 243, 222, 4, 163, 88, 57, 211, 75, 7, 149, 187, 187, 81, 34, 2, 179, 160, 148, 228, 227, 113, - 247, 198, 39, 137, 45, 114, 186, 121, 135, 78, 55, 65, 177, 99, 102, 65, 28, 76, 48, 138, 171, - 217, 51, 31, 154, 190, 174, 97, 168, 117, 172, 237, 224, 114, 8, 103, 84, 1, 246, 211, 98, 97, - 14, 58, 110, 46, 169, 251, 164, 131, 193, 178, 14, 208, 74, 62, 157, 44, 103, 207, 112, 173, - 216, 171, 182, 141, 100, 237, 193, 155, 100, 252, 234, 227, 240, 128, 133, 102, 185, 99, 234, - 200, 182, 96, 74, 192, 13, 209, 71, 115, 104, 124, 179, 164, 223, 89, 216, 115, 42, 45, 247, - 206, 176, 156, 12, 153, 45, 13, 251, 215, 172, 248, 210, 214, 153, 245, 13, 166, 237, 50, 171, - 170, 143, 159, 139, 197, 2, 32, 4, 92, 87, 228, 248, 120, 156, 15, 188, 36, 219, 70, 149, 33, - 151, 210, 173, 43, 232, 232, 23, 173, 77, 61, 170, 89, 216, 91, 175, 177, 82, 106, 32, 5, 122, - 53, 232, 84, 165, 55, 115, 83, 22, 185, 109, 160, 95, 135, 139, 236, 58, 215, 84, 174, 132, - 161, 172, 156, 207, 194, 206, 81, 243, 106, 27, 2, 8, 156, 90, 196, 61, 251, 24, 214, 160, 159, - 203, 46, 123, 140, 238, 152, 95, 54, 36, 97, 235, 82, 194, 247, 9, 181, 230, 136, 73, 201, 135, - 205, 39, 151, 203, 47, 152, 214, 200, 164, 98, 254, 213, 134, 8, 241, 226, 104, 150, 244, 74, - 115, 219, 255, 83, 218, 101, 111, 191, 131, 194, 62, 167, 55, 72, 152, 27, 86, 66, 6, 244, 122, - 121, 144, 46, 166, 66, 124, 203, 204, 235, 75, 61, 217, 136, 85, 11, 213, 20, 21, 113, 177, - 117, 29, 68, 188, 151, 243, 240, 99, 46, 3, 241, 18, 179, 194, 224, 200, 241, 109, 149, 189, - 60, 148, 99, 5, 35, 145, 128, 128, 21, 203, 239, 181, 228, 240, 47, 135, 220, 34, 178, 167, - 247, 106, 173, 158, 22, 63, 212, 201, 160, 117, 48, 148, 208, 146, 236, 225, 34, 210, 178, 74, - 27, 219, 146, 18, 246, 147, 54, 127, 158, 168, 87, 74, 115, 111, 31, 193, 215, 116, 252, 153, - 159, 179, 50, 108, 250, 181, 183, 86, 85, 81, 49, 50, 182, 5, 156, 167, 125, 1, 203, 193, 96, - 229, 23, 56, 185, 8, 183, 136, 50, 181, 158, 63, 15, 141, 232, 151, 145, 44, 104, 52, 24, 145, - 194, 228, 252, 233, 233, 188, 144, 1, 19, 65, 2, 229, 146, 117, 67, 238, 190, 16, 96, 92, 169, - 231, 87, 127, 81, 158, 229, 8, 37, 233, 213, 154, 139, 101, 103, 128, 137, 232, 243, 179, 19, - 171, 248, 8, 248, 184, 187, 201, 55, 198, 254, 234, 226, 145, 11, 51, 172, 137, 200, 181, 186, - 172, 239, 57, 223, 226, 234, 218, 91, 71, 118, 140, 120, 165, 36, 223, 89, 215, 92, 202, 34, - 161, 165, 176, 85, 85, 31, 163, 26, 106, 45, 165, 211, 109, 81, 191, 141, 172, 192, 132, 37, - 20, 17, 237, 17, 202, 4, 88, 20, 249, 223, 116, 21, 255, 35, 4, 158, 8, 223, 44, 158, 133, 39, - 80, 104, 164, 35, 21, 203, 9, 235, 52, 205, 135, 20, 82, 240, 165, 60, 28, 223, 34, 9, 183, 92, - 14, 152, 76, 254, 209, 126, 53, 54, 232, 220, 227, 8, 161, 218, 16, 26, 241, 254, 53, 194, 219, - 149, 106, 67, 173, 42, 36, 13, 233, 203, 206, 205, 110, 99, 70, 226, 94, 86, 244, 125, 108, 41, - 118, 254, 55, 55, 222, 22, 92, 112, 154, 65, 212, 142, 59, 60, 3, 107, 208, 156, 230, 253, 184, - 103, 128, 240, 14, 203, 86, 220, 97, 117, 207, 209, 61, 122, 165, 43, 161, 50, 96, 156, 89, - 156, 209, 198, 59, 100, 165, 195, 131, 9, 142, 113, 217, 122, 61, 153, 59, 9, 163, 241, 6, 113, - 196, 218, 94, 216, 173, 188, 168, 35, 191, 202, 239, 105, 5, 128, 163, 172, 35, 193, 113, 25, - 50, 185, 158, 146, 208, 222, 191, 50, 54, 152, 19, 171, 153, 57, 88, 69, 45, 152, 185, 233, 13, - 250, 36, 169, 142, 104, 60, 213, 67, 240, 88, 59, 32, 4, 156, 64, 9, 145, 37, 151, 170, 222, - 245, 212, 179, 179, 95, 15, 92, 134, 140, 235, 51, 76, 184, 77, 219, 134, 120, 83, 135, 187, - 113, 230, 108, 32, 7, 180, 85, 30, 235, 49, 150, 123, 139, 21, 97, 53, 180, 76, 65, 53, 29, 81, - 242, 180, 182, 109, 27, 44, 27, 209, 137, 104, 132, 203, 216, 223, 32, 1, 167, 163, 6, 212, - 176, 209, 163, 168, 79, 34, 201, 245, 245, 33, 176, 192, 133, 68, 76, 227, 32, 128, 19, 130, - 94, 41, 155, 15, 85, 69, 3, 32, 6, 93, 22, 62, 108, 26, 116, 170, 1, 220, 48, 246, 63, 14, 245, - 73, 142, 211, 185, 136, 227, 247, 199, 193, 50, 103, 134, 100, 224, 207, 38, 138, 32, 0, 138, - 122, 222, 252, 143, 99, 6, 6, 186, 215, 1, 145, 107, 193, 163, 218, 135, 118, 143, 237, 222, - 70, 147, 53, 154, 212, 171, 33, 177, 105, 117, 32, 5, 96, 8, 244, 182, 3, 126, 26, 207, 220, - 13, 105, 18, 189, 53, 97, 171, 149, 10, 26, 8, 165, 56, 223, 141, 45, 231, 69, 134, 106, 90, - 147, 32, 5, 190, 189, 68, 182, 166, 142, 113, 170, 103, 164, 124, 232, 53, 136, 88, 187, 165, - 178, 126, 26, 94, 174, 1, 109, 116, 175, 4, 218, 178, 115, 157, 32, 6, 223, 94, 162, 91, 83, - 71, 65, 85, 51, 210, 62, 116, 26, 196, 44, 93, 210, 217, 63, 13, 47, 87, 0, 182, 186, 87, 130, - 109, 89, 57, 207, 32, 3, 110, 13, 140, 241, 158, 226, 150, 158, 33, 144, 20, 173, 32, 89, 214, - 81, 43, 239, 131, 96, 7, 149, 210, 204, 242, 237, 242, 10, 76, 151, 155, 32, 3, 54, 190, 117, - 76, 64, 66, 50, 94, 106, 31, 14, 0, 72, 86, 144, 4, 87, 175, 113, 95, 132, 20, 150, 244, 193, - 183, 55, 31, 143, 102, 121, 32, 1, 196, 158, 65, 79, 172, 210, 38, 96, 195, 245, 247, 251, 104, - 34, 204, 51, 17, 168, 172, 143, 54, 7, 25, 194, 187, 91, 201, 165, 246, 30, 130, 32, 6, 211, - 182, 210, 42, 106, 109, 121, 52, 200, 27, 62, 164, 114, 176, 237, 146, 205, 250, 247, 103, 93, - 212, 127, 125, 69, 38, 5, 168, 231, 27, 194, 32, 0, 103, 93, 21, 202, 145, 48, 94, 190, 26, - 109, 238, 97, 191, 230, 177, 28, 40, 27, 153, 235, 76, 73, 184, 67, 147, 150, 249, 28, 123, 46, - 222, 32, 6, 102, 54, 19, 134, 48, 146, 225, 252, 72, 196, 200, 21, 16, 242, 66, 92, 55, 194, - 218, 109, 188, 50, 94, 173, 84, 102, 149, 193, 78, 62, 236, 32, 7, 49, 121, 69, 135, 13, 136, - 111, 113, 172, 9, 89, 125, 155, 112, 225, 80, 94, 100, 81, 16, 78, 3, 129, 200, 63, 245, 123, - 180, 71, 26, 42, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 32, 5, 182, 61, 27, 251, 143, 56, 240, 230, 192, 49, 200, 143, 255, 153, - 215, 200, 138, 223, 117, 110, 114, 188, 121, 78, 3, 227, 6, 137, 144, 131, 56, 32, 3, 23, 106, - 46, 33, 132, 89, 30, 45, 227, 80, 227, 168, 28, 145, 192, 171, 38, 0, 64, 169, 235, 255, 185, - 85, 94, 68, 43, 212, 212, 245, 96, 32, 5, 205, 118, 197, 63, 107, 148, 117, 161, 22, 167, 227, - 58, 18, 176, 249, 94, 70, 209, 8, 251, 141, 92, 74, 236, 181, 239, 172, 249, 79, 132, 199, 32, - 1, 251, 236, 84, 22, 64, 178, 150, 60, 215, 234, 45, 93, 26, 247, 167, 246, 225, 150, 121, 200, - 72, 133, 50, 170, 157, 249, 14, 244, 222, 202, 100, 32, 3, 104, 197, 122, 79, 86, 33, 171, 80, - 105, 176, 97, 127, 183, 155, 202, 210, 41, 50, 193, 230, 105, 41, 80, 94, 32, 25, 109, 74, 84, - 152, 62, 32, 0, 140, 105, 53, 12, 204, 23, 109, 32, 99, 158, 224, 62, 223, 171, 129, 110, 25, - 34, 112, 58, 88, 146, 253, 148, 62, 94, 186, 28, 21, 9, 216, 32, 1, 200, 72, 87, 10, 70, 185, - 170, 2, 244, 47, 69, 150, 134, 74, 15, 125, 152, 29, 21, 142, 193, 86, 47, 87, 130, 206, 26, - 149, 113, 90, 251, 32, 0, 171, 27, 118, 237, 98, 130, 23, 13, 77, 156, 195, 190, 14, 235, 26, - 124, 83, 128, 52, 145, 130, 117, 81, 229, 98, 35, 104, 9, 201, 162, 255, 32, 2, 102, 37, 165, - 125, 41, 20, 174, 20, 27, 202, 245, 7, 134, 253, 254, 182, 106, 22, 52, 179, 183, 65, 253, 24, - 37, 175, 40, 54, 192, 157, 134, 32, 0, 229, 143, 12, 220, 204, 241, 43, 26, 130, 177, 242, 7, - 140, 67, 1, 255, 14, 189, 19, 251, 63, 41, 192, 147, 180, 237, 237, 169, 11, 134, 101, 32, 3, - 169, 47, 96, 251, 89, 185, 44, 150, 94, 67, 58, 247, 56, 231, 232, 157, 51, 67, 59, 11, 45, 67, - 249, 192, 188, 147, 65, 230, 200, 140, 128, 32, 2, 65, 166, 39, 128, 133, 91, 168, 43, 103, 11, - 110, 235, 198, 179, 72, 198, 82, 163, 63, 209, 72, 237, 231, 227, 158, 70, 251, 9, 185, 17, - 170, 32, 5, 25, 72, 27, 123, 213, 104, 215, 121, 121, 85, 229, 23, 243, 127, 148, 33, 145, 63, - 168, 119, 237, 75, 234, 188, 198, 98, 220, 15, 87, 137, 129, 32, 4, 65, 237, 54, 118, 172, 230, - 149, 79, 201, 82, 241, 124, 164, 242, 212, 103, 128, 201, 136, 40, 124, 222, 143, 32, 199, 15, - 49, 184, 124, 96, 158, 32, 1, 235, 160, 73, 109, 4, 233, 12, 41, 163, 205, 106, 30, 239, 247, - 132, 171, 183, 95, 84, 124, 132, 32, 154, 217, 64, 69, 79, 231, 170, 71, 229, 32, 2, 83, 235, - 88, 245, 124, 155, 148, 78, 176, 119, 210, 81, 96, 24, 181, 208, 66, 161, 121, 229, 84, 100, - 148, 161, 40, 8, 52, 64, 75, 91, 23, 32, 4, 32, 122, 123, 210, 97, 99, 50, 125, 70, 140, 155, - 24, 207, 244, 72, 94, 243, 41, 78, 145, 135, 67, 104, 118, 23, 12, 111, 75, 26, 122, 203, 32, - 7, 136, 227, 2, 96, 38, 242, 105, 239, 68, 47, 219, 157, 213, 93, 48, 161, 64, 164, 250, 78, - 209, 101, 62, 61, 240, 232, 12, 48, 212, 248, 241, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, - 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 239, 255, 225, 32, 0, 58, 129, 30, 76, 176, 24, 111, 103, 5, 239, 139, 54, 59, - 134, 167, 114, 197, 80, 165, 136, 113, 166, 191, 174, 75, 172, 117, 193, 188, 79, 191, 32, 4, - 180, 232, 114, 72, 98, 11, 53, 63, 127, 2, 27, 49, 133, 85, 139, 214, 50, 80, 71, 71, 176, 113, - 202, 120, 85, 245, 157, 109, 190, 3, 128, 32, 4, 180, 232, 114, 72, 98, 11, 53, 63, 127, 2, 27, - 49, 133, 85, 139, 214, 50, 80, 71, 71, 176, 113, 202, 120, 85, 245, 157, 109, 190, 3, 128, 32, - 4, 180, 232, 114, 72, 98, 11, 53, 63, 127, 2, 27, 49, 133, 85, 139, 214, 50, 80, 71, 71, 176, - 113, 202, 120, 85, 245, 157, 109, 190, 3, 128, 32, 0, 5, 163, 104, 105, 99, 47, 186, 197, 98, - 16, 243, 163, 205, 84, 210, 9, 176, 77, 125, 133, 73, 89, 50, 125, 165, 26, 56, 92, 68, 119, - 113, 32, 2, 16, 215, 198, 204, 185, 235, 248, 101, 74, 180, 181, 111, 58, 212, 105, 78, 51, - 159, 120, 168, 158, 247, 105, 25, 64, 124, 116, 50, 202, 182, 235, 32, 6, 36, 24, 103, 125, - 154, 4, 209, 15, 152, 251, 76, 72, 101, 181, 245, 213, 121, 145, 103, 14, 188, 113, 196, 171, - 46, 79, 172, 66, 110, 62, 49, 32, 3, 135, 47, 178, 59, 85, 29, 104, 227, 133, 249, 128, 162, - 126, 176, 31, 195, 239, 25, 215, 232, 82, 214, 82, 181, 184, 142, 67, 42, 52, 101, 222, 32, 0, - 222, 174, 252, 159, 156, 229, 69, 58, 15, 31, 122, 75, 47, 207, 57, 4, 61, 88, 133, 13, 217, - 17, 174, 163, 79, 141, 154, 44, 185, 210, 26, 32, 0, 173, 22, 169, 9, 107, 226, 42, 151, 33, - 162, 184, 244, 107, 72, 179, 107, 3, 130, 145, 63, 43, 236, 108, 33, 23, 251, 97, 250, 94, 190, - 245, 32, 6, 200, 177, 47, 103, 138, 94, 218, 112, 31, 97, 43, 101, 114, 117, 99, 75, 166, 211, - 165, 29, 157, 118, 210, 190, 224, 204, 205, 95, 98, 202, 92, 32, 3, 255, 119, 15, 101, 204, 80, - 154, 190, 214, 187, 102, 143, 89, 155, 136, 196, 117, 53, 198, 51, 103, 68, 213, 47, 77, 45, - 69, 208, 223, 31, 64, 32, 0, 213, 66, 133, 206, 18, 94, 44, 226, 208, 103, 136, 235, 165, 114, - 239, 206, 161, 234, 88, 210, 162, 209, 211, 104, 30, 137, 176, 204, 231, 174, 66, 32, 5, 39, 5, - 0, 143, 234, 125, 187, 180, 150, 73, 117, 255, 81, 157, 102, 97, 20, 9, 33, 187, 78, 187, 235, - 52, 13, 154, 123, 68, 137, 93, 70, 32, 3, 229, 54, 198, 218, 23, 140, 26, 184, 216, 68, 167, 6, - 201, 87, 13, 240, 92, 130, 228, 74, 47, 34, 52, 191, 137, 153, 233, 203, 47, 25, 74, 32, 5, 86, - 78, 15, 90, 225, 101, 68, 180, 53, 221, 241, 131, 195, 50, 211, 120, 120, 247, 190, 224, 174, - 89, 78, 246, 205, 38, 85, 69, 230, 178, 117, 32, 1, 106, 168, 254, 170, 174, 193, 171, 204, - 114, 98, 148, 160, 133, 188, 114, 19, 38, 147, 119, 88, 196, 176, 169, 7, 228, 118, 122, 179, - 99, 106, 124, 32, 0, 238, 129, 61, 62, 223, 159, 58, 194, 55, 247, 156, 56, 182, 13, 247, 238, - 181, 44, 104, 204, 221, 231, 133, 141, 55, 84, 150, 116, 173, 6, 126, 32, 5, 198, 136, 201, - 134, 173, 212, 161, 248, 38, 98, 26, 53, 53, 174, 90, 63, 69, 124, 203, 100, 48, 222, 25, 31, - 98, 137, 53, 116, 103, 100, 19, 32, 6, 252, 1, 171, 25, 163, 75, 127, 142, 17, 220, 134, 250, - 116, 98, 111, 61, 73, 115, 3, 234, 22, 151, 77, 50, 92, 61, 67, 144, 237, 2, 116, 32, 2, 211, - 141, 169, 190, 197, 245, 223, 215, 35, 247, 90, 245, 119, 5, 4, 226, 182, 163, 98, 109, 40, 29, - 255, 140, 129, 207, 232, 121, 108, 157, 134, 32, 4, 229, 156, 27, 166, 207, 171, 143, 43, 182, - 7, 64, 50, 141, 112, 253, 89, 192, 106, 175, 118, 205, 164, 99, 83, 182, 55, 151, 194, 243, - 187, 222, 32, 4, 84, 140, 219, 172, 171, 132, 66, 176, 14, 181, 240, 106, 210, 5, 93, 108, 229, - 240, 28, 190, 19, 166, 43, 81, 6, 202, 187, 163, 113, 60, 112, 7, 30, 56, 95, 93, 65, 107, 32, - 178, 223, 114, 163, 90, 50, 171, 52, 10, 213, 111, 89, 246, 18, 76, 178, 252, 77, 219, 102, - 185, 114, 197, 12, 114, 192, 138, 45, 225, 107, 22, 106, 224, 41, 137, 103, 248, 223, 22, 39, - 61, 244, 177, 198, 228, 253, 97, 173, 154, 127, 57, 66, 129, 207, 106, 251, 250, 124, 108, 169, - 249, 27, 120, 207, 2, 180, 162, 185, 140, 51, 198, 145, 199, 171, 237, 142, 132, 163, 120, 55, - 143, 186, 52, 166, 177, 88, 152, 150, 179, 120, 123, 7, 138, 22, 8, 51, 13, 121, 209, 194, 84, - 146, 81, 13, 118, 191, 45, 15, 209, 108, 251, 217, 50, 208, 151, 247, 91, 64, 237, 174, 212, - 192, 63, 52, 37, 215, 6, 133, 135, 109, 38, 76, 158, 194, 112, 134, 65, 161, 23, 130, 7, 29, - 86, 115, 26, 184, 186, 17, 7, 4, 35, 160, 91, 122, 92, 47, 92, 145, 117, 116, 211, 43, 187, 31, - 53, 154, 224, 251, 167, 31, 167, 102, 25, 129, 146, 233, 128, 114, 177, 12, 67, 71, 44, 105, - 254, 223, 89, 216, 115, 42, 45, 247, 206, 176, 156, 12, 153, 45, 13, 251, 215, 172, 248, 210, - 214, 153, 245, 13, 166, 237, 50, 171, 170, 143, 159, 139, 197, 2, 32, 7, 172, 215, 165, 196, 4, - 43, 142, 132, 176, 80, 252, 203, 135, 109, 163, 211, 28, 76, 93, 172, 241, 129, 98, 17, 228, - 146, 220, 113, 156, 1, 184, 32, 3, 95, 208, 58, 3, 34, 144, 82, 63, 225, 146, 62, 58, 97, 192, - 221, 34, 203, 197, 77, 11, 10, 213, 215, 59, 46, 68, 206, 177, 216, 96, 45, 2, 8, 37, 73, 254, - 131, 157, 74, 167, 45, 231, 116, 153, 4, 129, 194, 210, 57, 116, 186, 154, 142, 35, 0, 191, - 198, 123, 192, 101, 198, 36, 11, 109, 251, 219, 22, 52, 188, 101, 175, 238, 251, 217, 42, 252, - 130, 173, 185, 82, 55, 1, 61, 248, 122, 61, 130, 157, 213, 235, 44, 103, 164, 94, 114, 88, 245, - 99, 20, 43, 249, 57, 98, 86, 177, 77, 57, 34, 62, 194, 40, 56, 105, 63, 142, 57, 215, 243, 49, - 109, 59, 109, 185, 142, 251, 56, 193, 39, 252, 195, 136, 193, 240, 72, 65, 241, 249, 71, 116, - 224, 227, 15, 162, 221, 45, 63, 46, 167, 114, 124, 200, 166, 251, 114, 77, 178, 34, 84, 169, - 165, 246, 66, 73, 78, 113, 115, 1, 120, 194, 171, 65, 135, 89, 54, 133, 205, 114, 2, 37, 232, - 112, 176, 15, 197, 164, 17, 248, 213, 189, 94, 22, 162, 91, 206, 237, 171, 111, 228, 106, 7, - 67, 203, 137, 134, 203, 148, 74, 76, 195, 150, 243, 35, 225, 237, 255, 101, 214, 133, 134, 186, - 220, 164, 167, 111, 79, 98, 26, 96, 213, 132, 26, 235, 97, 233, 166, 103, 42, 66, 145, 182, - 169, 127, 195, 28, 89, 69, 244, 193, 213, 237, 209, 167, 164, 31, 25, 35, 187, 229, 146, 117, - 67, 238, 190, 16, 96, 92, 169, 231, 87, 127, 81, 158, 229, 8, 37, 233, 213, 154, 139, 101, 103, - 128, 137, 232, 243, 179, 19, 171, 248, 8, 62, 240, 129, 84, 231, 116, 81, 87, 171, 7, 185, 132, - 62, 177, 218, 218, 189, 29, 153, 247, 103, 151, 127, 204, 16, 88, 152, 104, 233, 58, 161, 255, - 3, 185, 241, 81, 183, 10, 160, 199, 70, 246, 114, 29, 188, 19, 192, 161, 232, 150, 188, 199, - 186, 119, 192, 189, 101, 36, 57, 192, 251, 86, 202, 34, 218, 223, 60, 208, 196, 229, 46, 246, - 84, 27, 164, 74, 125, 185, 72, 240, 169, 55, 218, 7, 246, 177, 217, 159, 216, 67, 57, 112, 240, - 196, 8, 169, 236, 252, 187, 134, 180, 238, 106, 87, 248, 45, 26, 51, 89, 121, 208, 51, 64, 232, - 125, 22, 84, 176, 171, 67, 74, 22, 183, 244, 158, 204, 152, 84, 181, 211, 172, 142, 234, 220, - 9, 99, 223, 22, 246, 69, 69, 234, 146, 80, 90, 45, 203, 22, 48, 132, 255, 43, 182, 41, 114, - 225, 11, 234, 215, 180, 37, 148, 50, 36, 99, 91, 230, 108, 72, 148, 11, 43, 179, 194, 199, 77, - 158, 6, 76, 215, 176, 65, 68, 202, 156, 111, 130, 150, 8, 177, 76, 131, 101, 145, 253, 3, 135, - 186, 26, 10, 98, 199, 227, 177, 37, 212, 10, 191, 95, 30, 178, 47, 58, 193, 111, 161, 13, 124, - 159, 148, 167, 229, 75, 215, 25, 50, 185, 158, 146, 208, 222, 191, 50, 54, 152, 19, 171, 153, - 57, 88, 69, 45, 152, 185, 233, 13, 250, 36, 169, 142, 104, 60, 213, 67, 240, 88, 59, 32, 0, - 166, 143, 42, 142, 87, 169, 31, 11, 27, 210, 12, 68, 31, 182, 65, 117, 250, 144, 16, 91, 173, - 97, 19, 254, 149, 210, 84, 21, 253, 96, 120, 32, 4, 41, 156, 3, 173, 114, 108, 175, 146, 233, - 175, 17, 238, 202, 118, 142, 94, 121, 182, 45, 179, 102, 148, 213, 156, 196, 66, 160, 19, 5, - 29, 39, 32, 6, 108, 64, 255, 166, 174, 19, 182, 248, 170, 76, 25, 101, 120, 24, 239, 2, 23, 63, - 32, 9, 94, 175, 138, 77, 170, 63, 183, 143, 59, 115, 142, 32, 3, 44, 122, 23, 255, 24, 38, 243, - 213, 251, 142, 134, 163, 16, 187, 220, 20, 13, 29, 222, 245, 71, 0, 161, 5, 181, 218, 247, 13, - 75, 58, 32, 32, 7, 229, 194, 112, 210, 213, 228, 202, 0, 76, 189, 118, 147, 194, 36, 29, 153, - 60, 12, 190, 196, 241, 195, 93, 59, 190, 241, 148, 101, 110, 25, 12, 32, 5, 173, 2, 59, 106, - 96, 5, 29, 145, 49, 0, 14, 23, 82, 156, 122, 170, 103, 10, 65, 39, 146, 245, 198, 6, 26, 185, - 150, 14, 65, 16, 66, 32, 3, 8, 145, 103, 131, 196, 179, 140, 37, 249, 147, 115, 195, 116, 2, - 74, 246, 4, 56, 1, 171, 104, 77, 160, 175, 85, 36, 230, 200, 80, 38, 72, 32, 1, 132, 72, 179, - 193, 226, 89, 198, 18, 252, 201, 185, 225, 186, 1, 37, 123, 2, 28, 0, 213, 180, 38, 208, 87, - 170, 146, 115, 100, 40, 19, 36, 32, 3, 171, 35, 71, 90, 156, 125, 54, 148, 224, 20, 204, 191, - 234, 21, 246, 170, 77, 143, 54, 167, 147, 24, 19, 245, 232, 152, 177, 125, 251, 9, 0, 32, 6, - 32, 19, 73, 56, 101, 113, 222, 150, 126, 103, 38, 64, 23, 197, 61, 36, 93, 105, 0, 69, 204, - 175, 230, 198, 170, 11, 144, 166, 153, 47, 109, 32, 1, 29, 213, 165, 59, 53, 227, 227, 171, 79, - 44, 174, 51, 97, 175, 90, 20, 63, 111, 214, 99, 68, 145, 112, 78, 127, 108, 10, 25, 250, 152, - 67, 32, 6, 204, 127, 175, 144, 38, 255, 80, 252, 77, 226, 7, 181, 210, 249, 17, 156, 173, 228, - 22, 24, 108, 101, 222, 196, 125, 124, 34, 21, 26, 41, 60, 32, 1, 1, 245, 202, 107, 202, 77, - 104, 34, 22, 181, 60, 149, 47, 83, 238, 119, 147, 20, 28, 27, 150, 176, 240, 199, 204, 215, 99, - 238, 66, 11, 162, 32, 0, 41, 135, 231, 101, 240, 74, 109, 97, 213, 230, 13, 220, 132, 204, 79, - 104, 239, 38, 4, 222, 31, 243, 88, 228, 158, 77, 74, 113, 104, 32, 231, 32, 6, 253, 194, 225, - 44, 163, 117, 146, 188, 76, 162, 246, 189, 79, 123, 139, 161, 68, 20, 56, 171, 200, 254, 88, - 60, 98, 118, 29, 4, 155, 15, 226, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 100, 32, 251, 198, 159, 1, 247, 12, 160, 94, 135, - 200, 45, 246, 119, 38, 108, 199, 147, 140, 94, 250, 44, 232, 201, 29, 38, 60, 232, 115, 42, 32, - 6, 248, 21, 91, 221, 143, 155, 216, 33, 110, 122, 203, 49, 112, 175, 80, 39, 136, 229, 70, 175, - 81, 119, 15, 175, 184, 32, 24, 109, 127, 251, 196, 32, 1, 213, 211, 90, 187, 239, 184, 54, 242, - 41, 23, 119, 156, 57, 168, 25, 107, 14, 142, 120, 30, 20, 131, 18, 120, 71, 99, 187, 95, 73, - 102, 87, 32, 7, 232, 182, 129, 69, 143, 174, 25, 223, 66, 49, 64, 32, 174, 19, 228, 47, 172, - 119, 217, 99, 92, 3, 197, 18, 155, 219, 138, 215, 65, 146, 101, 32, 3, 147, 118, 65, 245, 241, - 204, 90, 229, 135, 53, 20, 61, 6, 127, 54, 246, 193, 246, 52, 109, 53, 128, 192, 111, 131, 224, - 230, 183, 22, 133, 186, 32, 5, 43, 178, 109, 43, 78, 76, 194, 131, 70, 113, 67, 218, 111, 135, - 0, 239, 165, 249, 8, 114, 44, 141, 60, 219, 106, 250, 90, 66, 164, 36, 0, 32, 1, 46, 154, 148, - 230, 179, 190, 49, 33, 98, 117, 94, 58, 101, 183, 243, 209, 29, 84, 71, 18, 165, 153, 81, 93, - 110, 234, 123, 246, 45, 217, 169, 32, 7, 170, 50, 2, 39, 193, 159, 151, 186, 19, 168, 237, 7, - 166, 54, 228, 197, 89, 205, 54, 54, 68, 171, 158, 194, 208, 78, 68, 136, 32, 180, 132, 32, 5, - 159, 18, 60, 150, 103, 158, 110, 170, 224, 38, 17, 161, 240, 2, 15, 87, 98, 9, 64, 185, 165, - 62, 157, 8, 107, 112, 34, 114, 202, 18, 85, 32, 3, 237, 206, 118, 90, 116, 2, 108, 83, 0, 105, - 254, 108, 86, 132, 242, 9, 227, 96, 66, 2, 58, 35, 42, 82, 24, 237, 222, 47, 162, 213, 32, 32, - 1, 80, 55, 105, 41, 207, 136, 201, 137, 192, 241, 230, 139, 235, 242, 49, 19, 33, 1, 252, 96, - 138, 109, 232, 163, 112, 72, 220, 161, 12, 100, 124, 32, 3, 128, 167, 243, 198, 207, 107, 166, - 173, 142, 164, 251, 83, 11, 106, 58, 53, 24, 115, 181, 187, 128, 189, 135, 116, 68, 66, 31, - 246, 125, 220, 250, 32, 0, 122, 159, 127, 39, 250, 214, 143, 89, 84, 201, 167, 17, 235, 230, - 136, 118, 223, 21, 216, 95, 229, 186, 152, 7, 40, 220, 125, 47, 113, 119, 53, 32, 0, 2, 2, 113, - 107, 140, 184, 231, 247, 11, 70, 82, 140, 156, 124, 11, 62, 1, 250, 30, 25, 84, 251, 4, 237, - 215, 208, 241, 17, 150, 241, 133, 32, 1, 5, 5, 136, 49, 246, 212, 227, 60, 197, 251, 113, 194, - 188, 179, 50, 241, 137, 251, 51, 52, 160, 86, 90, 197, 75, 57, 250, 177, 148, 180, 155, 32, 5, - 177, 251, 114, 115, 201, 142, 215, 249, 28, 205, 105, 118, 251, 113, 123, 132, 205, 140, 72, - 243, 202, 2, 195, 248, 96, 28, 182, 12, 140, 165, 57, 32, 4, 186, 10, 44, 47, 66, 34, 143, 51, - 253, 221, 196, 150, 134, 156, 69, 139, 160, 116, 199, 220, 244, 81, 127, 249, 196, 1, 250, 141, - 110, 46, 53, 32, 5, 21, 242, 125, 66, 20, 198, 53, 163, 185, 171, 58, 47, 187, 233, 164, 129, - 145, 77, 156, 27, 12, 240, 165, 235, 10, 4, 248, 9, 2, 115, 198, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, - 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 239, 255, 225, 32, 6, 187, 191, 78, 79, 246, 26, 10, 85, 107, 225, - 214, 19, 178, 27, 151, 30, 106, 24, 73, 101, 81, 222, 70, 200, 68, 51, 23, 188, 125, 65, 207, - 32, 4, 61, 176, 228, 242, 140, 243, 114, 106, 144, 62, 135, 193, 36, 228, 246, 132, 223, 144, - 177, 202, 108, 133, 98, 123, 14, 0, 161, 88, 231, 193, 69, 32, 4, 61, 176, 228, 242, 140, 243, - 114, 106, 144, 62, 135, 193, 36, 228, 246, 132, 223, 144, 177, 202, 108, 133, 98, 123, 14, 0, - 161, 88, 231, 193, 69, 32, 4, 61, 176, 228, 242, 140, 243, 114, 106, 144, 62, 135, 193, 36, - 228, 246, 132, 223, 144, 177, 202, 108, 133, 98, 123, 14, 0, 161, 88, 231, 193, 69, 32, 0, 216, - 34, 32, 138, 213, 169, 14, 208, 160, 180, 195, 54, 132, 203, 118, 4, 235, 206, 36, 242, 72, 86, - 179, 121, 240, 75, 220, 250, 9, 126, 177, 32, 2, 8, 57, 154, 188, 175, 168, 248, 107, 139, 189, - 179, 77, 180, 196, 64, 36, 187, 227, 74, 77, 141, 231, 103, 41, 183, 33, 165, 104, 92, 201, 94, - 32, 2, 16, 133, 8, 171, 44, 120, 218, 134, 25, 109, 127, 192, 115, 201, 240, 93, 44, 117, 128, - 212, 17, 169, 80, 91, 207, 101, 208, 133, 194, 249, 5, 32, 2, 175, 67, 185, 76, 143, 174, 195, - 170, 35, 196, 37, 85, 43, 222, 248, 16, 217, 42, 98, 197, 232, 146, 53, 249, 165, 192, 115, 20, - 74, 246, 174, 32, 5, 111, 151, 157, 196, 194, 232, 28, 7, 244, 155, 10, 209, 225, 199, 64, 195, - 132, 255, 99, 81, 212, 207, 104, 136, 168, 39, 95, 118, 36, 217, 132, 32, 2, 144, 66, 96, 53, - 27, 231, 96, 173, 19, 24, 19, 244, 174, 34, 184, 181, 93, 180, 41, 230, 66, 183, 161, 123, 53, - 78, 154, 172, 122, 135, 186, 32, 1, 152, 248, 63, 117, 121, 11, 37, 226, 127, 3, 142, 7, 106, - 74, 25, 58, 10, 153, 46, 177, 141, 118, 221, 236, 40, 212, 251, 54, 70, 250, 12, 32, 6, 50, - 185, 46, 241, 55, 80, 15, 7, 78, 242, 10, 66, 37, 20, 235, 66, 33, 174, 157, 249, 70, 5, 26, - 183, 82, 133, 224, 154, 95, 123, 58, 32, 5, 253, 178, 186, 90, 250, 135, 19, 111, 87, 189, 193, - 166, 170, 98, 185, 215, 109, 154, 240, 33, 35, 208, 211, 87, 62, 69, 65, 165, 179, 103, 154, - 32, 5, 106, 244, 118, 69, 159, 249, 155, 236, 88, 59, 78, 67, 117, 22, 146, 183, 124, 52, 1, - 170, 253, 246, 105, 157, 232, 168, 43, 138, 73, 139, 158, 32, 2, 18, 204, 95, 191, 3, 208, 65, - 39, 131, 233, 114, 195, 117, 119, 8, 159, 131, 220, 220, 160, 241, 229, 11, 247, 96, 249, 37, - 138, 114, 208, 247, 32, 0, 163, 189, 96, 73, 84, 33, 86, 211, 55, 66, 111, 79, 109, 74, 66, - 138, 102, 11, 140, 24, 188, 32, 56, 46, 141, 193, 48, 161, 121, 183, 136, 32, 2, 84, 60, 50, - 213, 51, 208, 153, 180, 87, 255, 223, 115, 159, 157, 97, 11, 52, 174, 103, 36, 125, 99, 208, - 66, 158, 206, 162, 82, 113, 159, 44, 32, 7, 206, 132, 103, 44, 8, 251, 3, 4, 204, 128, 221, 14, - 221, 90, 118, 169, 220, 45, 150, 232, 167, 246, 14, 3, 176, 154, 49, 39, 48, 205, 1, 32, 2, - 126, 227, 160, 140, 85, 176, 180, 247, 35, 79, 98, 199, 146, 81, 226, 39, 120, 143, 37, 66, - 149, 140, 160, 224, 253, 196, 80, 73, 54, 166, 41, 32, 0, 136, 230, 108, 206, 7, 172, 148, 42, - 120, 220, 17, 50, 109, 250, 165, 53, 244, 136, 9, 190, 10, 43, 255, 11, 26, 231, 44, 215, 163, - 106, 115, 32, 6, 85, 207, 138, 54, 242, 201, 19, 219, 246, 206, 128, 82, 159, 129, 132, 162, - 195, 184, 235, 33, 32, 126, 11, 166, 93, 129, 175, 179, 205, 135, 0, 32, 7, 7, 172, 237, 220, - 64, 70, 119, 171, 250, 118, 28, 177, 99, 26, 172, 238, 226, 223, 20, 217, 202, 255, 54, 196, - 93, 179, 223, 106, 173, 49, 111, 32, 7, 206, 177, 11, 109, 6, 131, 77, 220, 64, 132, 119, 159, - 193, 180, 48, 239, 63, 216, 62, 112, 208, 6, 35, 73, 190, 219, 138, 220, 23, 86, 178, 7, 185, - 129, 243, 126, 12, 23, 81, 193, 216, 100, 105, 241, 96, 224, 222, 187, 41, 87, 235, 59, 254, - 254, 106, 215, 151, 198, 216, 137, 87, 154, 17, 37, 116, 103, 217, 130, 125, 42, 71, 113, 160, - 130, 226, 178, 176, 106, 211, 188, 187, 126, 255, 56, 142, 114, 209, 252, 111, 34, 196, 50, - 201, 217, 176, 242, 186, 227, 61, 88, 41, 243, 62, 108, 65, 247, 58, 160, 126, 117, 196, 165, - 187, 174, 40, 172, 249, 159, 23, 38, 110, 145, 238, 99, 102, 145, 192, 151, 109, 212, 7, 144, - 241, 219, 159, 240, 4, 186, 81, 176, 23, 62, 250, 13, 221, 38, 179, 166, 120, 75, 229, 196, - 180, 179, 248, 57, 206, 40, 99, 254, 51, 230, 152, 210, 217, 212, 43, 71, 107, 171, 142, 71, - 14, 62, 110, 41, 182, 36, 32, 96, 88, 122, 254, 193, 218, 68, 147, 165, 70, 14, 28, 171, 15, - 190, 222, 28, 255, 161, 229, 154, 51, 48, 225, 56, 151, 205, 40, 203, 139, 117, 226, 253, 28, - 32, 75, 6, 59, 72, 255, 67, 219, 118, 21, 125, 51, 45, 171, 60, 71, 178, 34, 1, 103, 238, 163, - 124, 189, 119, 30, 6, 204, 249, 51, 163, 124, 49, 48, 66, 231, 52, 43, 104, 108, 3, 211, 196, - 2, 32, 2, 247, 104, 232, 78, 138, 140, 199, 2, 140, 39, 120, 94, 185, 164, 123, 77, 125, 113, - 179, 173, 99, 143, 19, 123, 80, 101, 84, 6, 173, 103, 69, 32, 1, 15, 198, 103, 129, 99, 8, 22, - 107, 216, 82, 89, 124, 124, 99, 18, 26, 181, 85, 166, 91, 48, 19, 253, 134, 85, 43, 62, 164, - 107, 61, 150, 2, 8, 62, 44, 109, 175, 20, 72, 111, 8, 201, 64, 93, 178, 214, 219, 167, 10, 170, - 159, 48, 151, 59, 160, 66, 114, 152, 134, 248, 127, 120, 30, 179, 14, 139, 170, 141, 93, 121, - 239, 194, 14, 102, 7, 189, 196, 115, 187, 60, 215, 18, 222, 131, 7, 75, 212, 188, 192, 76, 203, - 238, 85, 79, 12, 54, 231, 86, 151, 55, 0, 220, 136, 78, 126, 115, 209, 44, 92, 28, 137, 98, 83, - 22, 139, 55, 156, 16, 89, 230, 102, 74, 159, 250, 44, 229, 98, 135, 143, 187, 208, 228, 16, - 121, 134, 209, 217, 57, 22, 60, 30, 69, 253, 111, 210, 50, 102, 144, 215, 31, 249, 250, 32, - 200, 168, 28, 236, 35, 123, 186, 205, 119, 105, 144, 137, 225, 179, 93, 226, 177, 47, 220, 80, - 135, 139, 16, 59, 142, 114, 145, 122, 199, 250, 90, 16, 218, 146, 188, 78, 179, 117, 0, 127, - 65, 73, 175, 31, 243, 113, 84, 117, 9, 243, 236, 149, 222, 78, 175, 15, 254, 3, 30, 184, 97, - 18, 206, 47, 248, 251, 115, 153, 87, 228, 133, 185, 202, 162, 8, 67, 150, 128, 8, 9, 249, 181, - 204, 76, 95, 153, 145, 183, 153, 231, 164, 80, 156, 141, 63, 16, 150, 55, 32, 29, 23, 94, 121, - 91, 230, 102, 86, 203, 113, 159, 74, 128, 251, 129, 47, 111, 246, 84, 15, 173, 114, 238, 116, - 16, 155, 12, 31, 135, 15, 42, 107, 57, 231, 57, 31, 248, 8, 92, 249, 101, 16, 8, 74, 255, 104, - 122, 65, 96, 127, 45, 224, 74, 185, 38, 177, 102, 195, 110, 102, 251, 117, 128, 155, 193, 70, - 209, 131, 134, 95, 63, 224, 18, 67, 171, 13, 122, 132, 252, 153, 36, 224, 133, 210, 189, 180, - 69, 146, 227, 165, 98, 191, 84, 224, 137, 164, 97, 2, 32, 195, 224, 144, 88, 12, 7, 243, 172, - 61, 5, 72, 70, 231, 153, 25, 126, 141, 100, 242, 218, 86, 20, 90, 112, 209, 214, 32, 139, 78, - 52, 92, 234, 90, 86, 132, 197, 86, 73, 49, 109, 163, 185, 196, 195, 155, 141, 71, 195, 163, - 248, 147, 134, 215, 134, 31, 228, 223, 171, 121, 215, 38, 128, 8, 219, 153, 215, 227, 201, 119, - 48, 125, 31, 23, 13, 80, 120, 151, 246, 202, 241, 51, 242, 209, 218, 235, 129, 8, 75, 194, 189, - 27, 29, 123, 71, 22, 84, 43, 35, 129, 61, 245, 107, 25, 19, 158, 188, 56, 201, 24, 23, 148, - 115, 220, 185, 3, 231, 184, 65, 25, 159, 105, 232, 92, 122, 22, 105, 104, 136, 15, 213, 20, 47, - 241, 143, 10, 103, 38, 150, 60, 79, 110, 163, 8, 253, 180, 70, 46, 57, 62, 228, 197, 202, 96, - 179, 17, 52, 135, 35, 192, 169, 105, 94, 140, 38, 226, 17, 200, 114, 66, 199, 188, 124, 141, - 168, 29, 23, 194, 195, 44, 138, 2, 56, 50, 173, 213, 76, 21, 151, 35, 181, 172, 207, 17, 240, - 231, 59, 32, 3, 231, 1, 133, 105, 97, 226, 179, 108, 126, 58, 84, 78, 86, 117, 74, 66, 233, - 104, 124, 175, 92, 68, 143, 109, 254, 160, 50, 243, 105, 59, 13, 32, 4, 143, 212, 130, 250, - 198, 88, 191, 18, 127, 88, 135, 118, 245, 3, 215, 13, 226, 150, 186, 41, 244, 175, 119, 11, 49, - 58, 174, 57, 141, 150, 200, 32, 3, 237, 24, 186, 10, 144, 100, 60, 177, 121, 160, 180, 151, - 106, 22, 212, 84, 230, 225, 223, 37, 107, 47, 110, 218, 115, 0, 205, 45, 112, 24, 179, 32, 0, - 144, 221, 82, 119, 83, 231, 101, 41, 132, 156, 177, 221, 161, 88, 5, 214, 182, 89, 132, 4, 158, - 12, 118, 122, 164, 52, 12, 140, 9, 192, 80, 32, 4, 11, 74, 65, 121, 99, 166, 33, 25, 12, 205, - 197, 109, 12, 183, 195, 198, 3, 231, 165, 69, 227, 248, 35, 5, 51, 19, 62, 215, 100, 26, 189, - 32, 7, 168, 120, 147, 12, 236, 108, 124, 55, 116, 27, 30, 166, 94, 3, 133, 92, 22, 80, 90, 237, - 116, 149, 106, 173, 77, 220, 192, 229, 1, 30, 228, 32, 0, 144, 124, 82, 154, 203, 93, 210, 178, - 4, 170, 176, 46, 204, 196, 201, 236, 2, 84, 119, 191, 35, 246, 193, 165, 158, 125, 40, 189, - 131, 28, 131, 32, 4, 72, 62, 41, 77, 101, 174, 241, 217, 2, 85, 88, 23, 102, 98, 100, 246, 1, - 42, 59, 223, 145, 251, 96, 210, 207, 62, 148, 94, 193, 142, 66, 32, 6, 63, 126, 116, 113, 69, - 86, 163, 24, 77, 37, 12, 74, 147, 135, 149, 59, 48, 113, 216, 141, 16, 166, 41, 76, 46, 129, - 178, 61, 225, 110, 134, 32, 0, 80, 144, 156, 103, 169, 192, 140, 123, 99, 173, 4, 166, 136, 11, - 46, 255, 53, 133, 15, 96, 216, 177, 44, 197, 70, 150, 37, 178, 246, 60, 69, 32, 0, 132, 238, - 214, 50, 92, 196, 9, 21, 1, 39, 28, 225, 116, 106, 130, 110, 70, 137, 21, 241, 106, 248, 49, - 159, 55, 255, 54, 186, 120, 195, 63, 32, 3, 144, 197, 104, 217, 245, 26, 125, 76, 46, 117, 1, - 150, 69, 102, 216, 69, 34, 161, 24, 193, 244, 139, 104, 144, 104, 154, 225, 136, 232, 60, 31, - 32, 2, 150, 9, 175, 183, 81, 237, 68, 65, 118, 54, 196, 91, 47, 114, 143, 142, 238, 25, 231, - 186, 112, 185, 99, 25, 85, 118, 65, 96, 205, 92, 127, 32, 3, 165, 214, 95, 78, 123, 191, 221, - 120, 129, 38, 241, 81, 168, 36, 94, 249, 129, 118, 113, 204, 199, 132, 254, 55, 208, 87, 170, - 159, 189, 97, 1, 32, 1, 238, 74, 143, 113, 208, 95, 16, 104, 12, 141, 216, 231, 180, 104, 146, - 60, 240, 151, 243, 131, 171, 106, 247, 254, 175, 14, 61, 94, 95, 87, 229, 32, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 4, 204, 236, - 235, 136, 183, 129, 162, 75, 235, 210, 25, 81, 247, 166, 210, 135, 172, 130, 161, 80, 90, 183, - 84, 156, 89, 216, 30, 196, 41, 208, 215, 32, 6, 131, 10, 67, 164, 68, 109, 189, 205, 93, 11, 3, - 81, 113, 26, 19, 107, 200, 207, 195, 3, 37, 117, 200, 102, 191, 117, 234, 127, 119, 156, 148, - 32, 6, 78, 34, 240, 240, 171, 220, 113, 184, 143, 203, 245, 31, 63, 102, 106, 255, 10, 23, 162, - 53, 166, 172, 112, 18, 226, 180, 36, 99, 239, 188, 209, 32, 1, 183, 108, 72, 116, 66, 213, 158, - 5, 46, 172, 165, 253, 231, 4, 189, 2, 243, 188, 150, 98, 62, 67, 35, 241, 229, 112, 121, 169, - 136, 15, 215, 32, 1, 182, 189, 134, 28, 103, 154, 101, 48, 180, 19, 255, 53, 35, 8, 35, 68, 46, - 161, 212, 238, 126, 247, 94, 204, 7, 103, 109, 240, 214, 247, 80, 32, 4, 169, 74, 44, 134, 187, - 169, 93, 154, 131, 164, 218, 96, 104, 247, 117, 192, 187, 227, 223, 39, 195, 76, 70, 78, 115, - 161, 59, 99, 211, 228, 230, 32, 2, 136, 171, 226, 58, 237, 9, 27, 178, 228, 34, 4, 135, 92, - 226, 106, 18, 248, 72, 116, 183, 254, 118, 65, 208, 44, 99, 122, 93, 174, 112, 130, 32, 7, 158, - 12, 64, 160, 138, 200, 229, 157, 96, 20, 234, 216, 250, 68, 27, 186, 162, 192, 113, 131, 68, - 246, 122, 73, 28, 26, 47, 2, 224, 187, 144, 32, 4, 5, 138, 33, 10, 4, 81, 61, 153, 108, 90, 11, - 76, 235, 68, 68, 68, 248, 214, 138, 90, 174, 9, 107, 184, 95, 26, 49, 45, 251, 114, 234, 32, 4, - 206, 163, 72, 223, 163, 17, 135, 31, 255, 156, 59, 21, 47, 26, 244, 100, 117, 78, 154, 135, - 166, 22, 173, 120, 87, 220, 14, 122, 45, 66, 130, 32, 0, 69, 121, 90, 55, 255, 55, 20, 161, - 234, 95, 71, 25, 228, 156, 246, 136, 36, 20, 208, 178, 171, 183, 223, 225, 139, 190, 20, 179, - 0, 183, 111, 32, 3, 88, 103, 218, 19, 224, 60, 253, 206, 139, 39, 133, 78, 71, 235, 251, 254, - 64, 109, 251, 172, 126, 151, 103, 191, 83, 10, 170, 137, 136, 0, 250, 32, 4, 103, 231, 120, - 239, 116, 211, 238, 132, 56, 69, 102, 77, 111, 66, 67, 93, 16, 196, 159, 204, 39, 184, 57, 11, - 27, 32, 61, 197, 245, 131, 48, 32, 6, 201, 76, 97, 138, 33, 147, 101, 37, 179, 85, 99, 20, 36, - 93, 12, 223, 242, 75, 178, 179, 51, 24, 79, 126, 90, 96, 142, 46, 126, 198, 112, 32, 1, 147, - 254, 58, 143, 38, 224, 92, 98, 33, 27, 206, 51, 214, 71, 51, 169, 97, 58, 20, 215, 159, 183, - 146, 151, 48, 110, 248, 42, 1, 157, 148, 32, 3, 224, 19, 161, 116, 19, 29, 60, 179, 169, 117, - 93, 124, 146, 171, 150, 171, 136, 63, 148, 246, 202, 224, 19, 215, 214, 158, 108, 156, 1, 97, - 110, 32, 7, 176, 192, 121, 94, 231, 205, 149, 139, 133, 27, 50, 43, 171, 6, 70, 142, 121, 161, - 157, 143, 145, 212, 218, 125, 73, 178, 124, 68, 7, 165, 177, 32, 3, 9, 28, 139, 79, 223, 84, - 123, 23, 227, 102, 28, 232, 73, 253, 106, 156, 39, 149, 169, 66, 186, 74, 175, 78, 114, 101, 1, - 108, 97, 21, 57, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +static PROOF: [u8; 25531] = [ + 193, 90, 0, 0, 64, 34, 150, 252, 123, 41, 36, 58, 219, 76, 25, 24, 145, 52, 128, 169, 121, 212, + 127, 31, 128, 230, 237, 60, 212, 165, 171, 81, 190, 191, 157, 241, 200, 1, 77, 31, 232, 212, + 215, 170, 34, 124, 137, 195, 171, 208, 228, 24, 74, 34, 237, 107, 135, 106, 205, 168, 33, 72, + 193, 202, 8, 15, 250, 12, 38, 185, 72, 32, 4, 171, 46, 46, 72, 149, 150, 106, 253, 54, 138, 21, + 229, 36, 56, 120, 99, 238, 141, 157, 240, 156, 48, 237, 33, 19, 160, 95, 129, 156, 230, 103, + 32, 5, 92, 56, 221, 221, 174, 163, 72, 227, 62, 104, 105, 67, 88, 2, 1, 167, 128, 234, 197, 5, + 19, 84, 237, 187, 255, 26, 202, 199, 198, 212, 123, 32, 6, 179, 252, 250, 97, 234, 175, 28, + 235, 179, 10, 3, 105, 223, 144, 217, 123, 180, 69, 201, 228, 37, 168, 76, 160, 177, 251, 61, 4, + 188, 206, 150, 32, 4, 35, 76, 126, 184, 197, 8, 44, 216, 227, 61, 246, 83, 23, 174, 189, 38, + 10, 238, 119, 48, 144, 139, 151, 205, 240, 162, 170, 38, 69, 32, 218, 32, 0, 78, 185, 188, 8, + 218, 190, 3, 120, 130, 120, 175, 57, 130, 249, 75, 60, 8, 193, 5, 209, 215, 185, 84, 20, 152, + 161, 136, 29, 105, 202, 146, 32, 1, 32, 251, 95, 208, 37, 117, 134, 77, 38, 169, 174, 238, 162, + 116, 104, 140, 208, 75, 38, 112, 222, 177, 176, 95, 20, 91, 133, 96, 134, 241, 165, 32, 5, 58, + 73, 111, 62, 133, 211, 177, 161, 47, 249, 216, 236, 145, 223, 222, 0, 208, 194, 218, 24, 115, + 204, 219, 46, 122, 185, 109, 97, 142, 6, 157, 32, 6, 157, 36, 183, 159, 66, 233, 225, 80, 151, + 252, 236, 118, 72, 239, 239, 0, 104, 97, 109, 12, 57, 230, 109, 151, 61, 92, 182, 176, 199, 3, + 79, 32, 1, 112, 61, 72, 192, 28, 241, 170, 208, 254, 139, 231, 7, 209, 54, 27, 192, 145, 224, + 147, 87, 46, 129, 150, 250, 105, 17, 146, 120, 238, 160, 242, 32, 6, 171, 57, 209, 156, 186, + 18, 81, 248, 81, 70, 231, 108, 187, 34, 194, 209, 111, 66, 41, 165, 196, 44, 203, 185, 199, 88, + 119, 146, 115, 47, 134, 32, 3, 253, 112, 171, 215, 178, 72, 192, 87, 190, 133, 240, 187, 7, + 161, 188, 230, 12, 255, 184, 120, 26, 25, 36, 123, 72, 169, 168, 139, 153, 157, 59, 32, 4, 66, + 223, 179, 251, 173, 74, 78, 2, 84, 31, 166, 43, 236, 208, 150, 145, 194, 243, 10, 118, 191, + 206, 20, 156, 165, 1, 69, 115, 4, 199, 206, 32, 7, 139, 31, 61, 86, 188, 154, 191, 165, 215, + 226, 86, 162, 70, 214, 134, 187, 31, 125, 208, 216, 59, 183, 54, 16, 82, 52, 237, 240, 126, 50, + 223, 32, 7, 191, 175, 19, 56, 74, 241, 8, 88, 216, 27, 92, 136, 239, 219, 106, 181, 155, 238, + 129, 10, 129, 221, 197, 69, 118, 172, 159, 87, 101, 181, 40, 32, 6, 1, 130, 118, 140, 160, 245, + 70, 213, 30, 155, 31, 17, 36, 171, 217, 155, 43, 167, 29, 86, 82, 125, 66, 209, 133, 185, 134, + 204, 61, 249, 223, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 5, 5, 77, 210, 22, 240, 224, 140, 52, 197, 169, 11, 160, 21, 13, + 55, 239, 229, 66, 219, 125, 182, 6, 36, 172, 145, 58, 153, 209, 202, 52, 114, 32, 0, 148, 26, + 6, 89, 195, 227, 119, 6, 167, 12, 169, 231, 161, 188, 192, 231, 34, 181, 72, 158, 226, 167, + 181, 10, 160, 173, 188, 242, 236, 219, 152, 32, 0, 228, 51, 150, 138, 23, 240, 58, 239, 11, 11, + 49, 205, 232, 207, 77, 226, 183, 230, 217, 57, 64, 109, 196, 12, 39, 85, 255, 237, 18, 85, 193, + 32, 6, 85, 4, 87, 121, 137, 197, 38, 112, 136, 27, 230, 225, 169, 48, 102, 5, 84, 136, 250, 71, + 223, 219, 186, 104, 60, 135, 3, 128, 62, 106, 45, 32, 7, 56, 179, 166, 236, 231, 225, 176, 54, + 191, 5, 110, 209, 16, 229, 236, 73, 110, 41, 50, 90, 219, 209, 125, 93, 79, 197, 186, 92, 170, + 49, 219, 32, 3, 10, 93, 160, 62, 131, 106, 88, 66, 230, 41, 206, 250, 29, 161, 133, 102, 106, + 249, 221, 8, 97, 108, 255, 63, 67, 47, 168, 171, 220, 106, 41, 32, 0, 139, 66, 37, 71, 31, 54, + 72, 41, 206, 95, 73, 251, 212, 195, 255, 11, 100, 97, 24, 153, 43, 0, 48, 38, 167, 83, 93, 94, + 34, 210, 250, 32, 5, 109, 143, 37, 8, 2, 69, 20, 0, 152, 119, 96, 22, 198, 72, 166, 96, 100, + 110, 176, 22, 243, 142, 119, 221, 147, 218, 103, 40, 35, 46, 125, 32, 4, 188, 184, 9, 251, 140, + 5, 113, 122, 209, 130, 111, 194, 127, 208, 115, 60, 123, 105, 88, 5, 7, 234, 212, 115, 83, 139, + 114, 75, 218, 7, 70, 32, 1, 227, 195, 188, 203, 231, 139, 5, 12, 254, 39, 124, 95, 116, 106, + 12, 10, 179, 28, 165, 161, 182, 30, 11, 214, 138, 148, 228, 179, 58, 162, 36, 32, 5, 62, 149, + 75, 155, 0, 72, 204, 169, 3, 6, 65, 123, 194, 43, 245, 19, 54, 172, 21, 95, 53, 55, 148, 127, + 95, 27, 64, 184, 193, 216, 120, 32, 4, 27, 7, 54, 57, 150, 199, 162, 106, 136, 171, 108, 35, + 200, 0, 240, 225, 124, 114, 127, 192, 131, 131, 14, 216, 142, 47, 58, 30, 65, 127, 55, 32, 1, + 148, 91, 227, 154, 152, 80, 25, 52, 55, 158, 182, 187, 14, 180, 170, 20, 138, 197, 24, 175, + 230, 9, 219, 122, 144, 144, 61, 108, 215, 189, 250, 32, 7, 114, 120, 91, 223, 13, 219, 241, + 233, 217, 192, 111, 190, 224, 172, 246, 176, 182, 66, 142, 136, 226, 43, 54, 172, 53, 15, 59, + 142, 97, 211, 188, 32, 2, 132, 57, 150, 70, 49, 1, 8, 234, 246, 45, 249, 78, 230, 149, 128, + 150, 96, 213, 213, 11, 99, 63, 254, 93, 43, 187, 22, 231, 60, 154, 36, 32, 3, 63, 223, 107, 43, + 58, 72, 137, 1, 117, 86, 227, 189, 56, 81, 54, 68, 42, 58, 135, 219, 191, 103, 9, 82, 207, 206, + 34, 151, 38, 200, 153, 32, 1, 238, 242, 213, 113, 252, 6, 128, 90, 154, 103, 235, 16, 227, 37, + 91, 202, 212, 170, 172, 81, 1, 10, 69, 89, 136, 49, 193, 130, 176, 239, 60, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, + 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, 5, 164, 73, 93, 67, 178, 117, + 132, 236, 197, 104, 163, 216, 184, 202, 92, 66, 199, 96, 214, 233, 218, 106, 44, 172, 235, 112, + 136, 66, 249, 90, 126, 32, 3, 216, 209, 48, 53, 210, 164, 231, 47, 146, 117, 140, 11, 65, 169, + 131, 250, 39, 109, 15, 216, 66, 25, 51, 242, 199, 136, 251, 208, 55, 7, 38, 32, 0, 28, 192, + 139, 158, 30, 114, 208, 27, 253, 203, 86, 73, 93, 185, 133, 138, 151, 159, 169, 44, 150, 149, + 137, 211, 84, 61, 186, 3, 10, 45, 127, 32, 7, 151, 176, 122, 197, 209, 248, 208, 207, 146, 225, + 176, 196, 201, 153, 23, 74, 153, 165, 107, 70, 98, 205, 161, 122, 185, 108, 196, 218, 16, 169, + 168, 32, 3, 137, 111, 149, 102, 153, 147, 0, 104, 36, 138, 100, 191, 144, 237, 41, 233, 209, + 213, 131, 135, 16, 234, 103, 148, 243, 231, 86, 220, 198, 217, 201, 32, 2, 125, 208, 61, 184, + 217, 116, 160, 114, 35, 47, 160, 98, 129, 153, 162, 43, 22, 18, 93, 47, 145, 110, 192, 97, 211, + 116, 207, 38, 25, 85, 24, 32, 4, 152, 150, 235, 18, 135, 172, 152, 201, 60, 89, 181, 164, 133, + 155, 248, 226, 65, 214, 142, 49, 219, 216, 225, 220, 73, 222, 23, 153, 75, 82, 156, 32, 2, 76, + 75, 117, 137, 67, 214, 76, 100, 158, 44, 218, 210, 66, 205, 252, 113, 32, 235, 71, 24, 237, + 236, 112, 238, 36, 239, 11, 204, 165, 169, 78, 32, 6, 175, 201, 40, 59, 171, 43, 36, 172, 136, + 108, 255, 100, 80, 227, 10, 174, 193, 212, 112, 189, 11, 125, 254, 168, 172, 28, 240, 125, 57, + 253, 45, 32, 1, 255, 183, 117, 104, 114, 177, 68, 60, 194, 230, 255, 45, 181, 89, 233, 102, 27, + 100, 223, 222, 254, 63, 101, 162, 246, 68, 126, 58, 217, 87, 79, 32, 0, 114, 182, 113, 16, 115, + 168, 121, 94, 190, 154, 99, 70, 138, 167, 215, 132, 181, 223, 136, 147, 14, 148, 170, 167, 159, + 29, 60, 250, 92, 24, 190, 32, 7, 107, 94, 193, 123, 29, 115, 158, 67, 69, 79, 230, 88, 1, 84, + 42, 183, 6, 188, 186, 23, 202, 40, 94, 191, 211, 15, 171, 160, 230, 206, 22, 32, 4, 236, 198, + 161, 176, 103, 140, 241, 199, 93, 76, 39, 115, 254, 213, 231, 156, 64, 243, 38, 213, 147, 204, + 53, 0, 133, 24, 170, 120, 158, 49, 121, 32, 0, 70, 11, 93, 84, 254, 167, 35, 223, 122, 21, 131, + 118, 66, 134, 48, 64, 156, 144, 114, 42, 84, 93, 42, 166, 82, 19, 25, 33, 96, 110, 224, 32, 5, + 172, 169, 28, 33, 136, 147, 144, 105, 246, 97, 83, 182, 80, 191, 36, 150, 127, 167, 6, 69, 190, + 182, 91, 132, 194, 174, 247, 39, 151, 95, 246, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 3, 76, 211, 160, 97, 229, 52, 131, 185, + 195, 236, 105, 209, 142, 248, 93, 71, 33, 48, 49, 104, 170, 92, 146, 98, 174, 83, 1, 102, 205, + 200, 192, 32, 7, 21, 81, 39, 117, 159, 244, 252, 181, 175, 145, 237, 1, 23, 50, 219, 46, 206, + 97, 51, 195, 250, 165, 147, 122, 124, 78, 133, 234, 165, 212, 70, 32, 2, 178, 201, 123, 52, + 117, 122, 50, 239, 44, 197, 75, 38, 69, 125, 242, 79, 142, 17, 155, 233, 154, 65, 23, 28, 123, + 55, 181, 160, 88, 161, 235, 32, 2, 97, 38, 117, 200, 21, 124, 210, 53, 130, 17, 191, 250, 34, + 14, 52, 151, 68, 138, 131, 98, 249, 199, 148, 80, 56, 252, 154, 65, 213, 27, 192, 32, 0, 205, + 164, 83, 227, 233, 193, 236, 210, 30, 88, 170, 213, 13, 56, 163, 215, 160, 26, 150, 23, 215, + 32, 252, 71, 100, 125, 180, 18, 148, 175, 39, 32, 3, 45, 95, 106, 195, 89, 219, 152, 25, 57, + 128, 134, 236, 220, 170, 22, 10, 97, 237, 14, 147, 217, 20, 174, 29, 230, 46, 245, 87, 63, 10, + 9, 32, 3, 134, 64, 23, 104, 88, 168, 156, 44, 38, 141, 172, 137, 24, 124, 147, 215, 125, 196, + 252, 194, 131, 105, 70, 62, 139, 23, 141, 214, 75, 34, 196, 32, 7, 93, 19, 92, 57, 51, 193, + 163, 234, 57, 245, 251, 103, 204, 240, 215, 231, 38, 72, 197, 247, 151, 71, 191, 130, 143, 88, + 241, 1, 100, 102, 217, 32, 5, 43, 151, 202, 64, 35, 38, 41, 169, 58, 219, 96, 206, 11, 117, + 119, 84, 93, 194, 67, 15, 2, 242, 147, 229, 149, 91, 222, 249, 92, 31, 92, 32, 2, 89, 7, 44, + 73, 97, 6, 224, 231, 0, 56, 130, 53, 184, 253, 76, 88, 187, 244, 246, 33, 102, 42, 19, 92, 201, + 250, 169, 85, 234, 251, 36, 32, 5, 37, 89, 172, 166, 123, 192, 20, 204, 113, 206, 178, 126, + 109, 201, 135, 209, 180, 221, 30, 30, 97, 167, 219, 101, 162, 44, 245, 229, 176, 105, 184, 32, + 2, 209, 51, 25, 126, 164, 89, 205, 178, 219, 33, 106, 172, 156, 85, 134, 204, 144, 26, 171, + 220, 244, 133, 64, 46, 109, 7, 155, 135, 3, 196, 98, 32, 7, 168, 92, 40, 141, 187, 183, 115, + 78, 146, 156, 15, 97, 150, 45, 64, 224, 150, 100, 243, 178, 163, 219, 179, 111, 162, 215, 107, + 86, 87, 182, 159, 32, 0, 132, 167, 145, 122, 234, 98, 222, 154, 156, 118, 163, 208, 243, 48, + 51, 45, 24, 208, 224, 75, 117, 206, 138, 206, 103, 15, 227, 34, 57, 175, 135, 32, 5, 239, 184, + 156, 216, 201, 229, 236, 146, 243, 183, 254, 196, 139, 157, 236, 157, 30, 48, 142, 221, 118, + 118, 234, 33, 60, 254, 67, 243, 214, 168, 130, 32, 0, 46, 216, 103, 55, 129, 154, 253, 255, + 113, 23, 89, 175, 255, 179, 132, 214, 206, 245, 7, 52, 120, 138, 66, 122, 184, 203, 113, 156, + 214, 90, 65, 32, 0, 73, 33, 6, 193, 9, 222, 195, 71, 225, 131, 170, 231, 144, 206, 14, 16, 158, + 154, 184, 162, 0, 249, 182, 49, 74, 153, 197, 210, 39, 218, 148, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, + 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 36, 2, 46, 32, 2, 91, 68, 30, 167, 149, 38, + 118, 145, 95, 148, 62, 85, 145, 230, 53, 55, 130, 176, 244, 48, 67, 173, 42, 214, 141, 102, + 245, 187, 90, 230, 19, 32, 5, 34, 69, 70, 141, 13, 255, 133, 225, 104, 55, 188, 22, 185, 42, + 245, 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, 220, 87, 144, 176, 77, 60, 32, 5, 34, 69, 70, + 141, 13, 255, 133, 225, 104, 55, 188, 22, 185, 42, 245, 181, 99, 180, 84, 29, 89, 106, 130, + 235, 89, 220, 87, 144, 176, 77, 60, 32, 5, 34, 69, 70, 141, 13, 255, 133, 225, 104, 55, 188, + 22, 185, 42, 245, 181, 99, 180, 84, 29, 89, 106, 130, 235, 89, 220, 87, 144, 176, 77, 60, 32, + 4, 172, 50, 234, 18, 80, 78, 10, 45, 122, 188, 84, 165, 217, 106, 152, 186, 208, 180, 33, 157, + 178, 199, 196, 247, 86, 102, 162, 254, 117, 30, 72, 32, 0, 181, 233, 160, 243, 50, 209, 135, + 128, 205, 250, 37, 164, 203, 220, 42, 145, 194, 139, 44, 213, 121, 80, 254, 184, 153, 32, 79, + 69, 57, 83, 216, 32, 6, 19, 23, 214, 231, 29, 95, 179, 150, 132, 169, 32, 196, 217, 91, 169, + 163, 124, 152, 184, 237, 143, 180, 231, 117, 152, 167, 123, 83, 209, 158, 147, 32, 6, 53, 28, + 103, 222, 115, 121, 45, 17, 1, 207, 189, 245, 123, 206, 196, 83, 65, 169, 108, 164, 89, 160, + 91, 82, 95, 127, 247, 39, 181, 34, 110, 32, 3, 31, 236, 182, 13, 15, 107, 36, 19, 38, 250, 71, + 179, 208, 136, 106, 154, 173, 227, 31, 17, 152, 79, 12, 144, 63, 104, 61, 227, 164, 31, 228, + 32, 1, 159, 112, 153, 196, 243, 158, 187, 254, 28, 60, 241, 46, 105, 131, 157, 200, 26, 27, + 122, 61, 228, 124, 189, 29, 242, 143, 79, 146, 139, 250, 153, 32, 6, 193, 59, 12, 1, 213, 95, + 240, 123, 204, 211, 89, 91, 2, 87, 245, 137, 143, 166, 33, 52, 104, 219, 164, 137, 65, 209, 54, + 190, 218, 216, 212, 32, 3, 229, 249, 197, 153, 170, 217, 112, 20, 238, 233, 2, 174, 64, 10, + 149, 156, 93, 238, 183, 26, 131, 97, 252, 235, 129, 71, 6, 23, 175, 79, 173, 32, 6, 253, 237, + 190, 189, 104, 8, 118, 156, 19, 217, 149, 238, 165, 120, 5, 142, 216, 5, 93, 213, 251, 77, 5, + 153, 131, 157, 90, 222, 105, 122, 120, 32, 2, 129, 96, 22, 155, 203, 11, 182, 232, 185, 144, + 137, 252, 51, 123, 36, 247, 87, 64, 32, 96, 251, 255, 208, 25, 98, 57, 66, 175, 117, 176, 193, + 32, 3, 193, 122, 195, 109, 117, 12, 99, 231, 45, 239, 98, 138, 155, 65, 11, 130, 87, 253, 152, + 152, 204, 121, 224, 34, 239, 131, 187, 169, 165, 80, 147, 32, 4, 246, 117, 1, 253, 181, 210, + 144, 241, 105, 62, 229, 159, 173, 99, 208, 146, 160, 204, 225, 177, 13, 249, 172, 46, 189, 182, + 61, 154, 161, 37, 214, 32, 7, 204, 121, 1, 160, 97, 61, 117, 31, 237, 167, 240, 191, 56, 46, + 150, 73, 122, 206, 192, 102, 9, 94, 103, 110, 233, 248, 26, 236, 255, 132, 154, 32, 4, 137, + 131, 56, 170, 8, 125, 216, 70, 207, 87, 130, 132, 70, 1, 125, 242, 43, 121, 151, 176, 95, 112, + 189, 214, 242, 170, 214, 129, 55, 154, 128, 32, 4, 201, 114, 110, 175, 33, 250, 89, 22, 203, + 194, 137, 227, 107, 31, 82, 20, 62, 160, 204, 121, 71, 65, 18, 220, 240, 245, 105, 137, 65, + 104, 186, 32, 5, 73, 163, 150, 210, 209, 44, 45, 88, 147, 177, 140, 73, 245, 231, 52, 114, 8, + 113, 216, 127, 88, 253, 134, 215, 221, 35, 183, 71, 99, 55, 163, 32, 4, 183, 149, 133, 222, 16, + 181, 158, 187, 26, 112, 160, 13, 165, 89, 191, 175, 117, 135, 204, 186, 117, 164, 118, 218, + 181, 24, 248, 8, 182, 165, 181, 32, 5, 180, 94, 217, 181, 61, 240, 200, 208, 79, 68, 123, 238, + 236, 167, 131, 241, 128, 31, 247, 36, 252, 212, 75, 196, 240, 89, 232, 91, 20, 91, 209, 32, 4, + 44, 50, 77, 67, 251, 45, 217, 70, 42, 101, 206, 195, 184, 161, 10, 85, 151, 59, 51, 28, 208, + 81, 78, 247, 241, 88, 37, 195, 149, 151, 228, 32, 2, 97, 27, 155, 37, 146, 202, 43, 214, 62, + 62, 237, 12, 122, 139, 243, 242, 150, 242, 183, 49, 130, 122, 247, 48, 56, 106, 114, 143, 235, + 8, 79, 32, 2, 185, 124, 96, 183, 155, 14, 136, 208, 75, 114, 37, 245, 191, 178, 178, 63, 111, + 67, 60, 28, 27, 238, 221, 224, 240, 253, 47, 91, 239, 13, 179, 32, 2, 185, 124, 96, 183, 155, + 14, 136, 208, 75, 114, 37, 245, 191, 178, 178, 63, 111, 67, 60, 28, 27, 238, 221, 224, 240, + 253, 47, 91, 239, 13, 179, 32, 2, 185, 124, 96, 183, 155, 14, 136, 208, 75, 114, 37, 245, 191, + 178, 178, 63, 111, 67, 60, 28, 27, 238, 221, 224, 240, 253, 47, 91, 239, 13, 179, 32, 3, 81, + 15, 195, 103, 39, 66, 162, 136, 92, 92, 14, 153, 166, 21, 71, 131, 158, 181, 10, 33, 207, 138, + 191, 11, 61, 159, 8, 59, 196, 116, 206, 32, 6, 92, 174, 186, 3, 163, 232, 46, 142, 159, 231, + 236, 199, 52, 201, 253, 94, 196, 194, 27, 170, 65, 56, 3, 235, 54, 159, 71, 74, 224, 145, 194, + 32, 7, 37, 192, 191, 55, 232, 222, 163, 133, 119, 204, 154, 2, 252, 68, 204, 127, 35, 115, 18, + 255, 101, 92, 159, 113, 38, 148, 11, 88, 14, 188, 24, 32, 1, 185, 157, 208, 201, 232, 187, 167, + 69, 41, 191, 112, 20, 178, 90, 158, 106, 103, 187, 169, 152, 209, 138, 82, 222, 48, 151, 43, + 211, 122, 139, 62, 32, 0, 82, 151, 15, 254, 101, 72, 162, 180, 25, 122, 196, 91, 240, 198, 230, + 133, 210, 171, 61, 168, 31, 207, 166, 62, 126, 137, 58, 58, 215, 89, 196, 32, 7, 243, 45, 235, + 180, 10, 147, 108, 102, 148, 41, 190, 1, 4, 246, 173, 38, 149, 22, 241, 244, 63, 151, 200, 36, + 150, 99, 141, 148, 213, 170, 198, 32, 3, 127, 184, 40, 17, 194, 24, 186, 31, 167, 103, 162, + 171, 234, 52, 191, 197, 201, 175, 217, 230, 31, 216, 93, 227, 79, 185, 7, 14, 166, 193, 138, + 32, 5, 198, 163, 129, 217, 40, 49, 109, 212, 20, 113, 205, 18, 187, 6, 2, 41, 105, 178, 32, + 112, 127, 81, 84, 120, 159, 217, 95, 160, 16, 1, 38, 32, 0, 214, 196, 180, 99, 92, 89, 104, + 164, 35, 27, 6, 129, 63, 210, 196, 204, 72, 210, 247, 189, 173, 49, 236, 125, 2, 169, 73, 236, + 99, 59, 143, 32, 6, 118, 23, 240, 120, 81, 48, 50, 12, 164, 136, 237, 115, 2, 142, 1, 195, 49, + 183, 32, 18, 172, 59, 8, 81, 8, 54, 22, 129, 174, 149, 208, 32, 5, 235, 69, 63, 4, 2, 112, 82, + 71, 244, 130, 29, 243, 60, 199, 50, 242, 167, 231, 155, 160, 192, 217, 62, 149, 60, 73, 211, + 184, 175, 27, 199, 32, 4, 149, 42, 108, 215, 143, 62, 251, 86, 44, 43, 206, 24, 25, 153, 123, + 152, 228, 253, 246, 39, 29, 119, 101, 65, 112, 255, 188, 65, 104, 178, 152, 32, 1, 11, 198, + 183, 72, 20, 94, 168, 158, 4, 35, 255, 90, 48, 115, 225, 132, 79, 198, 112, 81, 250, 150, 205, + 240, 253, 74, 111, 40, 244, 118, 132, 32, 4, 34, 127, 238, 138, 239, 252, 226, 9, 107, 68, 128, + 80, 62, 242, 72, 177, 253, 112, 233, 34, 77, 129, 230, 247, 131, 60, 168, 117, 37, 255, 47, 32, + 1, 28, 189, 19, 230, 215, 229, 105, 97, 221, 162, 156, 153, 44, 128, 199, 191, 82, 91, 140, + 143, 108, 204, 195, 58, 240, 115, 225, 147, 155, 56, 110, 32, 0, 57, 219, 152, 51, 97, 31, 231, + 165, 105, 11, 92, 33, 44, 44, 210, 231, 255, 254, 124, 211, 208, 61, 253, 82, 136, 246, 114, + 193, 58, 71, 189, 32, 7, 89, 87, 211, 70, 169, 55, 121, 167, 147, 202, 221, 211, 140, 161, 36, + 161, 129, 74, 133, 100, 113, 62, 104, 54, 249, 65, 156, 152, 203, 181, 174, 32, 7, 10, 198, 8, + 71, 203, 123, 22, 215, 252, 95, 209, 228, 106, 1, 250, 148, 203, 149, 7, 35, 101, 250, 210, + 186, 174, 64, 246, 179, 223, 66, 30, 32, 5, 242, 65, 116, 15, 209, 144, 160, 137, 244, 39, 200, + 223, 20, 127, 84, 108, 194, 233, 203, 81, 191, 36, 35, 67, 165, 197, 81, 185, 130, 37, 193, 23, + 2, 1, 109, 191, 15, 222, 88, 251, 175, 15, 246, 130, 107, 105, 199, 118, 215, 241, 134, 76, + 221, 63, 47, 49, 81, 45, 0, 176, 60, 72, 117, 236, 249, 84, 2, 32, 3, 48, 127, 250, 48, 148, + 100, 159, 213, 218, 177, 58, 105, 139, 239, 218, 169, 185, 129, 164, 21, 88, 237, 88, 82, 191, + 111, 215, 137, 76, 223, 150, 32, 6, 16, 251, 113, 251, 35, 236, 142, 170, 88, 82, 203, 6, 0, + 233, 114, 105, 131, 238, 92, 207, 172, 58, 109, 219, 24, 246, 118, 55, 208, 230, 78, 5, 82, + 112, 121, 238, 185, 141, 1, 80, 168, 186, 34, 154, 225, 245, 139, 2, 123, 218, 190, 111, 15, + 255, 121, 41, 116, 227, 152, 42, 33, 168, 14, 1, 91, 35, 62, 84, 79, 132, 71, 170, 171, 23, + 112, 214, 190, 237, 191, 69, 250, 105, 10, 169, 108, 130, 87, 63, 201, 38, 178, 114, 139, 43, + 91, 113, 197, 141, 75, 213, 227, 174, 88, 250, 176, 106, 37, 76, 216, 107, 19, 138, 88, 156, + 239, 72, 27, 21, 218, 19, 60, 112, 156, 230, 158, 109, 190, 37, 168, 189, 205, 25, 14, 92, 102, + 145, 190, 239, 54, 179, 10, 15, 6, 103, 175, 66, 228, 134, 136, 118, 9, 64, 139, 92, 75, 55, + 96, 218, 229, 124, 227, 68, 31, 40, 194, 252, 219, 96, 27, 0, 152, 179, 217, 227, 210, 255, 77, + 23, 81, 166, 105, 218, 253, 13, 104, 220, 10, 192, 184, 18, 0, 193, 32, 1, 104, 12, 185, 205, + 138, 208, 244, 253, 33, 222, 248, 187, 128, 233, 27, 56, 12, 110, 239, 76, 137, 124, 240, 234, + 232, 103, 31, 240, 100, 98, 53, 3, 5, 6, 115, 121, 6, 85, 176, 150, 169, 85, 8, 57, 5, 144, + 154, 23, 153, 192, 202, 71, 43, 105, 225, 171, 19, 208, 87, 5, 6, 31, 239, 175, 229, 64, 33, + 47, 206, 7, 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, + 254, 106, 31, 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, + 67, 219, 63, 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, + 102, 12, 170, 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, + 151, 90, 245, 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, + 250, 154, 27, 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, + 252, 116, 208, 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, + 223, 109, 188, 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, + 26, 37, 229, 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, + 227, 24, 59, 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, + 45, 174, 176, 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, + 48, 62, 253, 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, + 105, 125, 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, + 163, 214, 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, + 28, 242, 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, + 125, 20, 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, + 102, 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, + 69, 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, + 202, 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, + 81, 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, + 248, 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, + 31, 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, + 73, 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, + 50, 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, + 48, 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, + 84, 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, + 46, 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, + 63, 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, + 213, 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, + 44, 89, 107, 176, 167, 113, 52, 250, 5, 32, 2, 78, 97, 121, 127, 238, 47, 119, 199, 15, 200, + 125, 16, 150, 254, 168, 168, 59, 109, 247, 150, 38, 227, 91, 124, 212, 64, 194, 25, 127, 188, + 125, 32, 0, 113, 249, 249, 255, 228, 13, 136, 153, 122, 192, 187, 56, 42, 197, 101, 118, 59, + 124, 61, 95, 37, 92, 208, 123, 153, 43, 38, 94, 210, 169, 139, 32, 6, 167, 37, 185, 208, 123, + 36, 223, 152, 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, + 114, 99, 207, 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, + 184, 44, 166, 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, + 35, 233, 0, 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, + 190, 166, 214, 43, 224, 224, 149, 217, 5, 6, 168, 38, 220, 25, 149, 71, 84, 129, 64, 73, 73, + 59, 140, 69, 111, 67, 195, 15, 178, 147, 177, 105, 84, 186, 79, 63, 194, 33, 85, 112, 1, 213, + 33, 47, 206, 7, 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, + 254, 106, 31, 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, + 67, 219, 63, 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, + 102, 12, 170, 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, + 151, 90, 245, 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, + 250, 154, 27, 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, + 252, 116, 208, 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, + 223, 109, 188, 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, + 26, 37, 229, 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, + 227, 24, 59, 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, + 45, 174, 176, 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, + 48, 62, 253, 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, + 105, 125, 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, + 163, 214, 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, + 28, 242, 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, + 125, 20, 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, + 102, 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, + 69, 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, + 202, 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, + 81, 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, + 248, 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, + 31, 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, + 73, 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, + 50, 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, + 48, 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, + 84, 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, + 46, 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, + 63, 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, + 213, 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, + 44, 89, 107, 176, 167, 113, 52, 250, 5, 32, 4, 87, 105, 19, 100, 49, 206, 121, 89, 252, 243, + 252, 225, 44, 180, 118, 113, 8, 186, 165, 2, 200, 56, 154, 176, 142, 73, 83, 118, 20, 113, 72, + 32, 6, 136, 174, 25, 103, 129, 228, 236, 152, 40, 59, 146, 249, 150, 237, 20, 81, 215, 10, 200, + 139, 59, 35, 133, 200, 43, 47, 215, 22, 184, 229, 2, 32, 6, 167, 37, 185, 208, 123, 36, 223, + 152, 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, 114, 99, + 207, 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, 184, 44, + 166, 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, 35, 233, + 0, 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, 190, 166, + 214, 43, 224, 224, 149, 217, 5, 6, 168, 38, 220, 25, 149, 71, 84, 129, 64, 73, 73, 59, 140, 69, + 111, 67, 195, 15, 178, 147, 177, 105, 84, 186, 79, 63, 194, 33, 85, 112, 1, 213, 33, 47, 206, + 7, 162, 188, 218, 42, 204, 87, 160, 251, 213, 62, 180, 7, 121, 99, 0, 211, 63, 30, 254, 106, + 31, 176, 41, 25, 175, 80, 100, 190, 111, 88, 75, 217, 188, 235, 191, 225, 117, 197, 67, 219, + 63, 231, 94, 69, 23, 155, 115, 39, 126, 66, 230, 232, 97, 65, 196, 47, 174, 63, 3, 102, 12, + 170, 97, 193, 165, 130, 39, 228, 52, 78, 243, 4, 108, 238, 148, 139, 4, 138, 220, 22, 151, 90, + 245, 199, 228, 126, 112, 63, 238, 140, 3, 100, 141, 244, 146, 128, 230, 70, 232, 217, 250, 154, + 27, 231, 23, 163, 61, 230, 124, 50, 221, 102, 196, 17, 93, 22, 222, 37, 204, 3, 54, 252, 116, + 208, 45, 93, 164, 82, 210, 92, 91, 63, 69, 76, 186, 185, 17, 78, 26, 238, 81, 20, 223, 109, + 188, 216, 28, 7, 139, 13, 241, 111, 119, 115, 3, 223, 5, 138, 86, 3, 251, 12, 37, 26, 26, 37, + 229, 31, 77, 213, 196, 29, 94, 132, 241, 23, 42, 110, 8, 144, 8, 241, 120, 250, 91, 227, 24, + 59, 118, 27, 166, 110, 151, 252, 42, 38, 13, 148, 178, 163, 7, 86, 243, 206, 99, 51, 45, 174, + 176, 2, 176, 222, 79, 185, 46, 239, 213, 231, 110, 203, 243, 231, 144, 68, 128, 118, 48, 62, + 253, 17, 89, 151, 143, 22, 34, 87, 213, 31, 37, 220, 227, 16, 247, 190, 214, 150, 187, 105, + 125, 102, 15, 145, 3, 159, 192, 79, 117, 81, 131, 127, 69, 145, 20, 160, 108, 209, 65, 86, 163, + 214, 220, 48, 231, 113, 225, 26, 192, 222, 215, 102, 154, 77, 26, 184, 181, 230, 178, 31, 28, + 242, 118, 216, 199, 237, 31, 119, 2, 113, 240, 112, 172, 197, 210, 98, 201, 91, 235, 196, 125, + 20, 119, 2, 25, 24, 146, 211, 241, 4, 74, 100, 162, 210, 220, 234, 124, 46, 120, 142, 236, 102, + 152, 212, 37, 114, 126, 89, 239, 119, 17, 248, 231, 171, 61, 108, 232, 175, 196, 62, 194, 69, + 13, 46, 84, 234, 17, 3, 21, 46, 232, 96, 9, 70, 128, 36, 230, 16, 165, 191, 254, 148, 41, 202, + 229, 89, 212, 233, 141, 168, 174, 229, 140, 224, 81, 97, 29, 1, 23, 2, 22, 151, 247, 233, 81, + 79, 170, 158, 249, 229, 199, 81, 89, 200, 216, 145, 38, 153, 216, 65, 34, 219, 99, 193, 248, + 36, 23, 36, 129, 51, 155, 81, 170, 123, 1, 59, 160, 203, 139, 28, 197, 58, 211, 230, 37, 31, + 85, 193, 140, 119, 108, 130, 78, 25, 191, 43, 91, 161, 3, 0, 250, 166, 193, 194, 161, 166, 73, + 214, 204, 218, 13, 80, 76, 118, 250, 79, 222, 128, 150, 252, 245, 253, 158, 102, 27, 172, 50, + 192, 131, 192, 147, 28, 23, 189, 214, 38, 116, 4, 248, 192, 87, 143, 63, 134, 25, 6, 201, 48, + 121, 23, 233, 211, 162, 185, 219, 176, 27, 21, 61, 121, 58, 228, 60, 47, 126, 131, 56, 51, 84, + 132, 13, 81, 124, 22, 98, 17, 50, 110, 109, 59, 181, 109, 154, 84, 88, 60, 188, 0, 191, 64, 46, + 229, 182, 32, 20, 2, 74, 42, 18, 235, 9, 249, 64, 116, 65, 54, 67, 1, 118, 34, 236, 213, 63, + 88, 202, 131, 114, 138, 154, 105, 29, 58, 106, 66, 201, 200, 74, 150, 27, 149, 159, 90, 213, + 12, 112, 252, 64, 244, 104, 220, 50, 128, 113, 201, 104, 20, 107, 144, 30, 58, 28, 189, 44, 89, + 107, 176, 167, 113, 52, 250, 5, 32, 4, 87, 105, 19, 100, 49, 206, 121, 89, 252, 243, 252, 225, + 44, 180, 118, 113, 8, 186, 165, 2, 200, 56, 154, 176, 142, 73, 83, 118, 20, 113, 72, 32, 6, + 136, 174, 25, 103, 129, 228, 236, 152, 40, 59, 146, 249, 150, 237, 20, 81, 215, 10, 200, 139, + 59, 35, 133, 200, 43, 47, 215, 22, 184, 229, 2, 32, 6, 167, 37, 185, 208, 123, 36, 223, 152, + 196, 150, 147, 157, 29, 252, 246, 66, 97, 248, 67, 241, 28, 215, 27, 63, 0, 229, 114, 99, 207, + 95, 21, 32, 2, 112, 59, 63, 171, 54, 166, 178, 221, 202, 101, 39, 130, 200, 103, 184, 44, 166, + 220, 246, 108, 106, 76, 54, 33, 134, 47, 117, 48, 240, 255, 173, 32, 5, 196, 143, 35, 233, 0, + 32, 54, 82, 255, 118, 12, 30, 80, 201, 187, 151, 120, 176, 215, 146, 164, 12, 194, 190, 166, + 214, 43, 224, 224, 149, 217, 3, 7, 126, 40, 243, 172, 167, 175, 234, 240, 174, 123, 250, 6, 48, + 63, 188, 203, 225, 130, 112, 128, 54, 46, 62, 66, 37, 235, 178, 113, 16, 113, 73, 110, 112, + 159, 196, 68, 95, 225, 214, 36, 29, 252, 221, 60, 143, 203, 240, 62, 161, 247, 182, 212, 232, + 10, 238, 65, 42, 138, 195, 155, 1, 255, 6, 12, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, + 152, 24, 136, 150, 9, 10, 132, 241, 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, + 239, 231, 102, 253, 245, 174, 171, 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, + 101, 195, 107, 199, 25, 191, 20, 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, + 25, 13, 66, 10, 104, 202, 169, 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, + 27, 8, 27, 237, 138, 226, 59, 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, + 103, 194, 98, 234, 250, 167, 255, 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, + 50, 217, 122, 69, 188, 177, 88, 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, + 144, 238, 168, 55, 70, 208, 7, 126, 40, 243, 172, 167, 175, 234, 240, 174, 123, 250, 6, 48, 63, + 188, 203, 225, 130, 112, 128, 54, 46, 62, 66, 37, 235, 178, 113, 16, 113, 73, 110, 112, 159, + 196, 68, 95, 225, 214, 36, 29, 252, 221, 60, 143, 203, 240, 62, 161, 247, 182, 212, 232, 10, + 238, 65, 42, 138, 195, 155, 1, 255, 6, 12, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, + 24, 136, 150, 9, 10, 132, 241, 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, + 231, 102, 253, 245, 174, 171, 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, + 195, 107, 199, 25, 191, 20, 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, + 13, 66, 10, 104, 202, 169, 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, + 8, 27, 237, 138, 226, 59, 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, + 194, 98, 234, 250, 167, 255, 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, + 217, 122, 69, 188, 177, 88, 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, + 238, 168, 55, 70, 208, 2, 32, 1, 233, 234, 212, 219, 245, 192, 123, 144, 73, 15, 40, 185, 48, + 206, 143, 33, 112, 161, 73, 157, 3, 160, 114, 210, 143, 56, 4, 8, 165, 17, 9, 32, 4, 13, 241, + 238, 159, 178, 110, 42, 17, 135, 191, 138, 114, 16, 117, 167, 46, 153, 106, 118, 27, 51, 83, + 133, 25, 221, 99, 241, 25, 41, 228, 94, 2, 32, 6, 97, 207, 224, 224, 82, 69, 83, 207, 133, 120, + 91, 161, 171, 39, 132, 249, 198, 104, 32, 25, 172, 171, 108, 226, 123, 235, 117, 51, 115, 30, + 157, 32, 4, 73, 249, 195, 252, 248, 178, 231, 139, 175, 74, 150, 185, 254, 59, 95, 235, 210, + 55, 3, 15, 131, 6, 156, 43, 249, 193, 136, 234, 229, 164, 33, 8, 209, 209, 231, 125, 217, 21, + 253, 56, 232, 148, 168, 235, 251, 35, 20, 86, 24, 135, 7, 4, 215, 13, 47, 207, 5, 74, 174, 31, + 140, 70, 195, 40, 227, 252, 46, 119, 163, 198, 19, 57, 229, 116, 112, 182, 233, 55, 59, 100, + 87, 119, 110, 130, 33, 131, 93, 13, 238, 17, 121, 187, 215, 36, 180, 136, 108, 178, 97, 88, 78, + 182, 228, 173, 17, 44, 182, 31, 7, 181, 38, 217, 220, 243, 61, 83, 119, 57, 169, 54, 207, 163, + 122, 191, 230, 12, 144, 114, 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, 29, 19, 195, + 22, 35, 246, 10, 106, 109, 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, 22, 144, 60, + 121, 121, 64, 190, 105, 81, 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, 106, 49, 93, + 166, 34, 146, 145, 252, 153, 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, 63, 64, 105, + 4, 102, 211, 3, 43, 180, 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, 197, 66, 155, + 36, 12, 34, 61, 169, 95, 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, 185, 201, 27, + 71, 191, 77, 148, 228, 244, 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, 26, 166, 148, + 184, 91, 40, 185, 45, 214, 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, 186, 141, 6, 8, + 118, 57, 168, 189, 244, 16, 138, 153, 83, 38, 111, 182, 217, 196, 52, 183, 62, 175, 249, 132, + 106, 145, 120, 20, 4, 174, 223, 62, 198, 85, 252, 125, 227, 252, 46, 119, 163, 198, 19, 57, + 229, 116, 112, 182, 233, 55, 59, 100, 87, 119, 110, 130, 33, 131, 93, 13, 238, 17, 121, 187, + 215, 36, 180, 136, 108, 178, 97, 88, 78, 182, 228, 173, 17, 44, 182, 31, 7, 181, 38, 217, 220, + 243, 61, 83, 119, 57, 169, 54, 207, 163, 122, 191, 230, 12, 144, 114, 23, 142, 0, 23, 70, 116, + 106, 121, 99, 128, 210, 78, 29, 19, 195, 22, 35, 246, 10, 106, 109, 28, 115, 181, 223, 237, + 182, 4, 20, 128, 109, 23, 191, 22, 144, 60, 121, 121, 64, 190, 105, 81, 199, 185, 217, 98, 169, + 242, 90, 255, 155, 197, 57, 106, 49, 93, 166, 34, 146, 145, 252, 153, 151, 197, 90, 125, 16, + 25, 41, 188, 114, 58, 89, 239, 63, 64, 105, 4, 102, 211, 3, 43, 180, 109, 69, 210, 168, 47, + 199, 3, 231, 17, 41, 201, 253, 197, 66, 155, 36, 12, 34, 61, 169, 95, 84, 39, 35, 149, 250, + 234, 76, 165, 17, 124, 40, 205, 185, 201, 27, 71, 191, 77, 148, 228, 244, 148, 31, 91, 252, 89, + 141, 6, 13, 139, 105, 158, 82, 26, 166, 148, 184, 91, 40, 185, 45, 214, 150, 78, 230, 134, 109, + 250, 24, 78, 249, 49, 197, 186, 141, 6, 36, 32, 2, 86, 79, 48, 107, 126, 61, 24, 210, 237, 10, + 182, 195, 72, 125, 211, 70, 109, 185, 250, 240, 62, 4, 11, 187, 27, 143, 175, 57, 125, 94, 153, + 32, 4, 254, 53, 26, 124, 207, 53, 62, 121, 2, 88, 132, 0, 97, 194, 182, 196, 79, 153, 8, 70, + 245, 186, 240, 75, 93, 168, 248, 19, 186, 62, 103, 32, 5, 50, 131, 105, 65, 246, 226, 154, 89, + 66, 193, 219, 26, 205, 111, 208, 58, 45, 248, 251, 187, 199, 36, 32, 48, 190, 129, 96, 236, + 255, 27, 144, 32, 2, 126, 156, 56, 125, 48, 128, 55, 103, 124, 60, 236, 170, 251, 141, 254, 20, + 36, 194, 117, 113, 163, 84, 126, 60, 85, 40, 109, 188, 104, 8, 220, 32, 3, 126, 5, 243, 19, 15, + 100, 195, 248, 111, 24, 8, 66, 31, 206, 185, 250, 157, 253, 130, 250, 212, 215, 201, 109, 114, + 17, 77, 66, 30, 0, 249, 32, 3, 154, 240, 158, 216, 219, 127, 231, 252, 171, 182, 115, 22, 217, + 9, 140, 21, 181, 156, 129, 167, 167, 123, 236, 67, 123, 163, 210, 247, 60, 136, 238, 32, 0, + 254, 121, 218, 105, 91, 132, 149, 196, 172, 197, 116, 69, 212, 111, 22, 82, 149, 200, 67, 149, + 121, 140, 92, 64, 180, 116, 100, 77, 20, 67, 188, 32, 0, 127, 60, 237, 52, 173, 194, 74, 226, + 86, 98, 186, 34, 234, 55, 139, 41, 74, 228, 33, 202, 188, 198, 46, 32, 90, 58, 50, 38, 138, 33, + 222, 32, 1, 156, 125, 144, 121, 200, 214, 175, 83, 30, 242, 25, 116, 104, 215, 93, 104, 171, + 188, 133, 4, 42, 169, 117, 153, 138, 209, 222, 202, 208, 136, 242, 32, 6, 48, 159, 5, 1, 58, + 210, 88, 153, 24, 92, 235, 226, 37, 223, 99, 137, 6, 157, 34, 152, 232, 189, 224, 149, 31, 199, + 2, 151, 144, 185, 67, 32, 5, 206, 82, 119, 10, 68, 54, 55, 225, 149, 228, 6, 217, 106, 134, + 231, 90, 87, 223, 78, 249, 157, 89, 128, 203, 239, 34, 108, 70, 123, 85, 165, 32, 2, 145, 139, + 150, 155, 51, 193, 93, 125, 14, 213, 102, 125, 153, 114, 73, 183, 178, 229, 40, 208, 144, 116, + 237, 245, 160, 115, 173, 210, 176, 87, 209, 32, 0, 240, 122, 130, 165, 93, 45, 85, 72, 6, 20, + 188, 202, 113, 217, 76, 168, 135, 171, 184, 105, 187, 5, 233, 6, 170, 40, 143, 134, 226, 174, + 124, 32, 1, 196, 212, 101, 79, 31, 79, 200, 7, 65, 116, 197, 74, 156, 94, 49, 124, 61, 169, + 100, 156, 145, 60, 76, 120, 69, 103, 98, 224, 79, 90, 242, 32, 2, 63, 73, 76, 135, 1, 157, 109, + 229, 148, 123, 31, 8, 65, 234, 176, 146, 37, 31, 38, 109, 20, 228, 132, 197, 128, 104, 119, 39, + 179, 37, 124, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 6, 139, 235, 255, 24, 24, 33, 103, 13, 72, 10, 211, 192, 220, 35, 80, + 251, 155, 139, 242, 230, 194, 24, 66, 255, 43, 225, 135, 94, 251, 253, 37, 32, 1, 79, 128, 11, + 14, 135, 225, 42, 145, 219, 39, 224, 255, 116, 61, 228, 148, 0, 243, 183, 243, 22, 179, 200, + 159, 244, 195, 212, 66, 113, 253, 139, 32, 6, 17, 100, 149, 214, 84, 144, 114, 104, 213, 128, + 180, 91, 168, 174, 4, 37, 176, 108, 92, 23, 212, 235, 10, 15, 53, 13, 233, 130, 249, 20, 35, + 32, 4, 83, 50, 27, 135, 53, 115, 119, 154, 213, 92, 213, 138, 24, 203, 214, 244, 25, 56, 3, + 133, 58, 148, 25, 255, 176, 224, 26, 54, 194, 200, 244, 32, 3, 213, 66, 151, 78, 219, 91, 212, + 110, 132, 197, 221, 39, 20, 141, 26, 60, 15, 173, 189, 139, 153, 167, 63, 2, 45, 177, 227, 31, + 255, 173, 165, 32, 3, 25, 13, 20, 144, 5, 177, 125, 69, 81, 86, 129, 196, 15, 169, 59, 79, 222, + 141, 94, 70, 91, 40, 246, 28, 73, 139, 76, 41, 210, 16, 101, 32, 0, 244, 217, 143, 120, 194, + 121, 76, 168, 204, 189, 77, 177, 162, 173, 32, 32, 193, 66, 161, 249, 145, 50, 88, 94, 224, 98, + 126, 164, 57, 51, 176, 32, 3, 230, 217, 81, 157, 54, 166, 224, 134, 217, 162, 132, 88, 91, 131, + 193, 92, 81, 218, 117, 236, 137, 37, 40, 30, 62, 5, 238, 210, 215, 155, 70, 32, 1, 127, 93, + 217, 48, 96, 65, 48, 106, 224, 86, 222, 228, 237, 0, 19, 219, 123, 119, 16, 121, 138, 249, 61, + 152, 169, 100, 100, 123, 165, 9, 242, 32, 4, 179, 94, 122, 0, 72, 6, 166, 115, 176, 37, 188, + 23, 240, 245, 124, 213, 186, 40, 198, 140, 102, 214, 140, 234, 197, 214, 116, 92, 4, 185, 26, + 32, 5, 192, 108, 139, 59, 1, 125, 224, 31, 75, 203, 90, 105, 3, 226, 101, 169, 51, 208, 195, + 151, 13, 53, 47, 165, 29, 63, 153, 29, 18, 100, 92, 32, 3, 169, 50, 79, 60, 7, 21, 201, 62, + 148, 46, 219, 133, 52, 176, 160, 22, 14, 190, 134, 31, 239, 109, 107, 22, 163, 225, 143, 229, + 1, 199, 164, 32, 0, 59, 200, 71, 79, 88, 64, 45, 134, 42, 27, 29, 120, 98, 25, 117, 212, 72, + 105, 114, 223, 119, 15, 194, 52, 248, 10, 33, 47, 71, 1, 148, 32, 1, 109, 133, 168, 234, 226, + 124, 40, 101, 236, 57, 226, 175, 84, 180, 216, 255, 182, 14, 154, 216, 196, 129, 93, 45, 40, + 73, 71, 158, 224, 63, 226, 32, 5, 160, 133, 29, 43, 68, 237, 171, 35, 4, 229, 78, 49, 111, 222, + 8, 152, 238, 210, 138, 215, 80, 218, 78, 181, 130, 230, 116, 254, 171, 145, 190, 32, 4, 87, + 170, 15, 89, 149, 175, 140, 133, 140, 106, 177, 191, 73, 245, 85, 147, 10, 30, 81, 104, 19, + 158, 112, 41, 149, 142, 181, 241, 115, 122, 248, 32, 2, 255, 253, 143, 178, 10, 253, 6, 181, + 113, 154, 36, 93, 242, 165, 18, 204, 157, 0, 73, 204, 95, 28, 235, 90, 13, 128, 113, 208, 35, + 110, 163, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 32, - 4, 255, 27, 106, 201, 44, 14, 220, 142, 147, 181, 147, 92, 84, 188, 236, 193, 193, 195, 15, - 241, 66, 233, 116, 141, 86, 139, 199, 167, 232, 176, 147, 32, 2, 197, 182, 174, 133, 66, 71, - 72, 229, 155, 174, 201, 229, 163, 137, 195, 85, 146, 235, 121, 247, 90, 60, 231, 57, 183, 90, - 94, 113, 154, 16, 167, 32, 2, 197, 182, 174, 133, 66, 71, 72, 229, 155, 174, 201, 229, 163, - 137, 195, 85, 146, 235, 121, 247, 90, 60, 231, 57, 183, 90, 94, 113, 154, 16, 167, 32, 2, 197, - 182, 174, 133, 66, 71, 72, 229, 155, 174, 201, 229, 163, 137, 195, 85, 146, 235, 121, 247, 90, - 60, 231, 57, 183, 90, 94, 113, 154, 16, 167, 32, 3, 216, 129, 227, 99, 217, 37, 101, 177, 159, - 4, 68, 104, 53, 177, 144, 54, 161, 196, 95, 173, 37, 172, 185, 205, 160, 225, 241, 122, 207, - 151, 44, 32, 0, 151, 196, 232, 108, 31, 41, 218, 248, 56, 28, 0, 47, 145, 195, 83, 167, 0, 21, - 140, 161, 70, 182, 209, 251, 96, 47, 189, 141, 218, 120, 174, 32, 5, 218, 121, 61, 198, 233, - 31, 227, 46, 250, 36, 19, 192, 183, 74, 37, 124, 127, 172, 117, 156, 180, 82, 186, 102, 21, - 125, 254, 176, 55, 105, 229, 32, 0, 66, 81, 241, 105, 163, 250, 188, 235, 217, 81, 99, 55, 68, - 228, 61, 18, 31, 115, 17, 87, 54, 242, 22, 7, 208, 91, 186, 253, 13, 82, 251, 32, 1, 20, 132, - 180, 228, 44, 72, 229, 247, 135, 63, 165, 202, 219, 240, 244, 38, 4, 179, 222, 189, 16, 203, - 71, 9, 61, 56, 144, 17, 201, 254, 148, 32, 3, 69, 36, 86, 208, 249, 235, 179, 140, 137, 83, - 105, 165, 45, 9, 244, 93, 233, 208, 101, 205, 74, 146, 224, 3, 68, 130, 118, 129, 169, 59, 235, - 32, 3, 152, 236, 103, 230, 27, 36, 127, 113, 95, 102, 10, 80, 209, 181, 73, 9, 23, 128, 224, - 191, 194, 50, 108, 61, 231, 122, 228, 144, 59, 71, 43, 32, 3, 195, 245, 184, 131, 104, 30, 240, - 95, 138, 107, 160, 106, 48, 167, 206, 78, 59, 49, 82, 39, 106, 178, 127, 189, 25, 122, 24, 120, - 243, 176, 13, 32, 0, 152, 202, 196, 40, 41, 108, 106, 152, 101, 92, 98, 19, 118, 51, 124, 219, - 56, 201, 74, 176, 83, 34, 80, 240, 203, 10, 75, 131, 191, 244, 163, 32, 5, 24, 61, 127, 118, - 198, 162, 174, 121, 186, 135, 217, 19, 73, 185, 250, 202, 236, 224, 162, 198, 189, 15, 209, - 208, 0, 236, 186, 53, 110, 26, 168, 32, 7, 81, 111, 212, 40, 211, 55, 185, 196, 195, 10, 132, - 152, 219, 123, 133, 140, 45, 137, 71, 137, 6, 173, 126, 159, 104, 48, 105, 217, 11, 210, 57, - 32, 7, 7, 23, 225, 223, 72, 243, 0, 67, 72, 196, 246, 186, 154, 84, 106, 101, 54, 160, 107, - 246, 183, 193, 94, 161, 187, 215, 110, 129, 161, 222, 41, 32, 1, 193, 179, 20, 81, 131, 101, - 40, 64, 234, 174, 62, 220, 34, 247, 91, 253, 32, 32, 31, 99, 21, 179, 77, 211, 24, 216, 64, 42, - 73, 11, 236, 32, 4, 226, 156, 169, 253, 81, 66, 154, 157, 21, 27, 130, 39, 154, 209, 161, 182, - 166, 76, 104, 42, 170, 28, 190, 75, 108, 17, 143, 99, 219, 97, 62, 32, 5, 162, 163, 11, 168, - 89, 215, 21, 194, 75, 44, 194, 237, 200, 232, 14, 188, 194, 230, 218, 166, 16, 136, 241, 113, - 80, 208, 73, 230, 24, 183, 52, 32, 2, 247, 255, 140, 190, 199, 123, 7, 84, 62, 190, 185, 196, - 206, 247, 124, 22, 2, 121, 168, 46, 72, 242, 82, 57, 48, 65, 29, 36, 217, 120, 251, 32, 1, 87, - 162, 118, 169, 219, 68, 21, 63, 138, 12, 138, 221, 252, 200, 37, 232, 72, 95, 17, 38, 194, 86, - 153, 252, 153, 235, 69, 58, 54, 136, 37, 32, 6, 95, 111, 8, 103, 96, 11, 148, 205, 63, 52, 120, - 237, 43, 50, 119, 39, 30, 31, 194, 113, 102, 35, 0, 113, 238, 144, 204, 22, 135, 194, 106, 32, - 3, 36, 45, 197, 165, 153, 210, 223, 245, 43, 17, 221, 176, 180, 113, 225, 58, 81, 17, 118, 64, - 178, 161, 123, 35, 181, 250, 190, 251, 71, 203, 129, 1, 253, 0, 0, 0, 0, 0, 0, 0, 64, 32, 7, - 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 7, 255, 255, 255, 255, 255, - 185, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 251, 225, 32, 7, 255, 255, 255, 255, 255, 185, 240, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 36, + 32, 3, 84, 27, 87, 135, 15, 57, 172, 159, 3, 225, 109, 133, 42, 61, 83, 239, 131, 242, 200, + 160, 52, 87, 0, 181, 112, 46, 15, 194, 84, 77, 79, 32, 7, 188, 29, 48, 216, 71, 33, 24, 62, 97, + 7, 201, 197, 14, 255, 184, 35, 29, 162, 189, 156, 197, 76, 147, 189, 181, 145, 0, 192, 85, 120, + 140, 32, 4, 237, 117, 1, 36, 37, 198, 171, 163, 155, 46, 237, 50, 212, 159, 123, 185, 54, 50, + 139, 170, 38, 228, 93, 132, 37, 226, 7, 36, 24, 204, 146, 32, 2, 158, 107, 51, 60, 100, 199, + 69, 151, 241, 169, 217, 121, 125, 25, 189, 125, 73, 64, 140, 76, 137, 72, 48, 58, 239, 20, 108, + 177, 1, 42, 140, 32, 1, 27, 217, 253, 111, 140, 4, 203, 241, 56, 110, 161, 128, 74, 196, 11, + 182, 220, 79, 250, 214, 161, 124, 158, 229, 30, 51, 174, 44, 107, 72, 67, 32, 4, 153, 151, 232, + 60, 26, 126, 101, 141, 56, 139, 57, 28, 134, 96, 217, 66, 136, 81, 2, 67, 126, 15, 199, 50, 12, + 76, 246, 35, 86, 44, 242, 32, 3, 246, 142, 87, 128, 141, 14, 96, 192, 152, 44, 238, 57, 1, 104, + 95, 136, 108, 78, 158, 154, 235, 95, 208, 83, 70, 201, 93, 90, 51, 242, 224, 32, 1, 251, 71, + 43, 192, 70, 135, 48, 96, 76, 22, 119, 28, 128, 180, 47, 196, 54, 39, 79, 77, 117, 175, 232, + 41, 163, 100, 174, 173, 25, 249, 112, 32, 5, 142, 12, 152, 22, 101, 75, 87, 107, 36, 221, 184, + 156, 79, 103, 107, 54, 161, 252, 217, 43, 40, 176, 171, 17, 41, 79, 98, 12, 130, 33, 166, 32, + 2, 125, 144, 17, 30, 245, 147, 128, 169, 244, 237, 54, 193, 101, 235, 179, 161, 60, 54, 105, + 18, 238, 217, 113, 216, 65, 234, 4, 58, 249, 104, 149, 32, 4, 16, 135, 217, 85, 38, 82, 172, + 64, 178, 52, 209, 103, 45, 208, 116, 226, 72, 229, 214, 237, 246, 47, 101, 18, 24, 82, 9, 149, + 110, 125, 164, 32, 7, 242, 58, 112, 247, 159, 250, 25, 111, 180, 119, 101, 221, 251, 137, 188, + 136, 187, 207, 71, 129, 154, 222, 50, 212, 194, 77, 17, 131, 85, 58, 114, 32, 7, 242, 113, 171, + 124, 230, 188, 19, 112, 125, 108, 113, 249, 196, 21, 102, 26, 213, 94, 247, 45, 8, 49, 73, 70, + 202, 194, 115, 137, 241, 153, 130, 32, 2, 233, 210, 109, 6, 113, 40, 250, 51, 212, 11, 48, 172, + 148, 235, 19, 101, 195, 78, 78, 186, 191, 218, 144, 254, 26, 71, 179, 1, 10, 188, 133, 32, 2, + 5, 82, 56, 185, 122, 156, 51, 212, 232, 216, 21, 100, 89, 130, 221, 7, 104, 144, 88, 225, 205, + 197, 255, 123, 100, 192, 228, 54, 122, 131, 48, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 3, 9, 188, 25, 165, 212, 95, 130, 7, + 21, 107, 213, 216, 158, 56, 138, 38, 198, 134, 30, 23, 245, 212, 132, 231, 211, 138, 76, 59, + 129, 73, 152, 32, 6, 216, 91, 49, 138, 90, 228, 233, 118, 58, 26, 247, 71, 55, 137, 248, 148, + 211, 218, 94, 104, 141, 163, 120, 28, 78, 243, 38, 4, 144, 100, 218, 32, 1, 213, 30, 175, 120, + 136, 190, 30, 48, 198, 93, 127, 2, 167, 209, 159, 194, 197, 187, 42, 255, 152, 225, 10, 50, + 248, 236, 97, 168, 204, 36, 185, 32, 6, 116, 40, 157, 22, 58, 92, 233, 38, 170, 101, 43, 46, + 218, 197, 93, 104, 41, 210, 10, 37, 60, 43, 79, 149, 114, 172, 29, 190, 168, 251, 199, 32, 3, + 24, 39, 128, 93, 103, 196, 5, 79, 185, 117, 7, 146, 153, 90, 254, 5, 130, 12, 153, 185, 207, + 30, 14, 152, 200, 36, 42, 150, 145, 210, 27, 32, 1, 22, 73, 41, 59, 234, 26, 5, 135, 102, 92, + 167, 184, 165, 180, 231, 236, 178, 6, 250, 91, 234, 0, 126, 175, 131, 92, 216, 45, 119, 80, + 147, 32, 0, 24, 159, 203, 4, 157, 232, 213, 23, 28, 133, 127, 211, 180, 158, 133, 47, 30, 163, + 246, 49, 145, 99, 106, 178, 121, 228, 187, 189, 10, 235, 213, 32, 4, 99, 234, 125, 221, 33, + 133, 10, 52, 208, 236, 249, 75, 84, 168, 16, 251, 127, 242, 68, 20, 178, 122, 76, 88, 145, 157, + 217, 88, 247, 115, 214, 32, 2, 163, 178, 0, 139, 184, 59, 187, 162, 214, 144, 29, 205, 51, 123, + 107, 71, 190, 65, 51, 8, 57, 146, 214, 252, 83, 154, 220, 2, 107, 132, 195, 32, 4, 245, 208, + 72, 15, 125, 89, 230, 25, 67, 1, 21, 219, 221, 174, 195, 233, 136, 44, 168, 226, 226, 232, 109, + 145, 115, 34, 246, 46, 238, 31, 9, 32, 4, 5, 199, 250, 218, 169, 198, 26, 12, 113, 75, 31, 56, + 13, 183, 227, 40, 115, 178, 77, 19, 179, 62, 10, 22, 158, 203, 154, 28, 109, 3, 3, 32, 6, 147, + 245, 50, 239, 249, 33, 78, 188, 107, 146, 100, 193, 23, 35, 66, 146, 24, 208, 159, 121, 233, + 179, 230, 34, 79, 169, 107, 66, 239, 151, 71, 32, 2, 186, 75, 75, 101, 248, 183, 192, 47, 126, + 193, 171, 154, 78, 117, 212, 136, 151, 56, 73, 34, 108, 13, 84, 224, 233, 150, 212, 9, 223, + 221, 56, 32, 2, 225, 247, 82, 0, 223, 3, 14, 142, 45, 9, 147, 121, 101, 208, 194, 33, 160, 92, + 2, 218, 29, 73, 200, 95, 100, 35, 40, 82, 31, 85, 18, 32, 4, 209, 105, 159, 89, 180, 86, 117, + 227, 33, 207, 136, 30, 228, 226, 166, 27, 117, 209, 88, 34, 122, 137, 25, 36, 106, 240, 50, + 122, 131, 86, 26, 32, 1, 156, 156, 80, 200, 107, 146, 183, 124, 114, 132, 209, 146, 60, 127, + 13, 38, 239, 122, 154, 136, 79, 36, 20, 147, 163, 108, 55, 188, 107, 1, 218, 32, 1, 215, 126, + 4, 177, 226, 207, 95, 131, 141, 176, 153, 7, 39, 6, 7, 156, 2, 94, 137, 84, 5, 222, 197, 120, + 162, 201, 13, 50, 254, 47, 147, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 251, 225, 32, 7, 255, 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 32, 7, 255, 255, - 255, 255, 255, 103, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 247, 1, 1, 251, 251, 127, 1, 251, 1, 128, 0, 30, 32, 7, - 255, 255, 255, 255, 255, 239, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 32, 7, 255, 255, 255, 255, 255, 253, - 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 225, 32, 7, 255, 255, 255, 255, 255, 213, 144, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, - 129, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, 255, 194, - 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 252, 97, 32, 0, 0, 0, 0, 0, 0, 21, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 32, 7, 255, 255, 255, 255, 255, 249, 176, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 161, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, 134, 32, 7, 255, 255, 255, - 255, 255, 198, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 252, 161, 32, 0, 0, 0, 0, 0, 0, 2, 32, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 7, 255, 255, 255, 255, 255, 196, 144, + 239, 255, 225, 1, 8, 143, 175, 215, 219, 185, 111, 177, 75, 73, 66, 181, 115, 149, 60, 155, 36, + 64, 201, 97, 22, 44, 5, 199, 189, 156, 70, 107, 202, 123, 120, 195, 22, 53, 201, 115, 119, 123, + 31, 250, 105, 162, 10, 120, 21, 98, 82, 118, 173, 203, 237, 10, 153, 113, 191, 66, 88, 177, 10, + 231, 230, 41, 67, 192, 249, 207, 99, 1, 50, 32, 159, 23, 4, 177, 177, 25, 222, 245, 171, 142, + 54, 95, 204, 168, 62, 52, 171, 93, 250, 1, 204, 122, 117, 10, 52, 107, 186, 208, 168, 36, 249, + 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, 186, 134, 137, 48, 43, 33, 53, 149, 217, 125, 46, + 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, 144, 161, 214, 44, 14, 190, 143, 160, 9, 57, 35, + 119, 113, 194, 148, 35, 100, 168, 241, 151, 40, 203, 46, 56, 120, 109, 133, 0, 249, 165, 107, + 85, 164, 80, 243, 242, 36, 235, 226, 199, 191, 219, 14, 217, 84, 59, 78, 176, 165, 80, 100, + 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, 160, 180, 121, 49, 249, 172, 16, 189, 110, 38, + 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, 172, 17, 108, 168, 101, 136, 252, 118, 147, 242, + 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, 12, 53, 221, 137, 122, 171, 78, 203, 75, 123, 4, + 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, 190, 94, 157, 8, 225, 76, 17, 77, 187, 76, 17, + 108, 216, 38, 71, 160, 110, 32, 238, 61, 148, 165, 33, 215, 48, 170, 187, 219, 64, 213, 76, + 195, 223, 39, 9, 99, 53, 201, 115, 119, 123, 31, 250, 105, 162, 10, 120, 21, 98, 82, 118, 173, + 203, 237, 10, 153, 113, 191, 66, 88, 177, 10, 231, 230, 41, 67, 192, 249, 207, 99, 1, 50, 32, + 159, 23, 4, 177, 177, 25, 222, 245, 171, 142, 54, 95, 204, 168, 62, 52, 171, 93, 250, 1, 204, + 122, 117, 10, 52, 107, 186, 208, 168, 36, 249, 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, 186, + 134, 137, 48, 43, 33, 53, 149, 217, 125, 46, 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, 144, + 161, 214, 44, 14, 190, 143, 160, 9, 57, 35, 119, 113, 194, 148, 35, 100, 168, 241, 151, 40, + 203, 46, 56, 120, 109, 133, 0, 249, 165, 107, 85, 164, 80, 243, 242, 36, 235, 226, 199, 191, + 219, 14, 217, 84, 59, 78, 176, 165, 80, 100, 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, 160, + 180, 121, 49, 249, 172, 16, 189, 110, 38, 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, 172, + 17, 108, 168, 101, 136, 252, 118, 147, 242, 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, 12, 53, + 221, 137, 122, 171, 78, 203, 75, 123, 4, 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, 190, 94, + 157, 23, 32, 7, 142, 147, 6, 21, 123, 33, 253, 55, 126, 197, 126, 95, 171, 57, 134, 188, 164, + 221, 62, 106, 229, 100, 218, 190, 240, 169, 120, 192, 242, 21, 214, 32, 2, 101, 51, 207, 215, + 58, 21, 130, 193, 114, 124, 220, 41, 22, 255, 94, 247, 102, 86, 120, 184, 44, 249, 175, 191, + 29, 59, 152, 233, 95, 5, 160, 32, 2, 101, 51, 207, 215, 58, 21, 130, 193, 114, 124, 220, 41, + 22, 255, 94, 247, 102, 86, 120, 184, 44, 249, 175, 191, 29, 59, 152, 233, 95, 5, 160, 32, 2, + 101, 51, 207, 215, 58, 21, 130, 193, 114, 124, 220, 41, 22, 255, 94, 247, 102, 86, 120, 184, + 44, 249, 175, 191, 29, 59, 152, 233, 95, 5, 160, 32, 5, 198, 59, 14, 129, 9, 55, 157, 229, 85, + 214, 246, 241, 224, 2, 104, 97, 11, 102, 254, 74, 31, 2, 254, 77, 240, 254, 1, 102, 112, 220, + 7, 32, 1, 149, 3, 229, 123, 254, 53, 90, 180, 12, 177, 118, 229, 25, 101, 129, 174, 72, 233, + 55, 115, 47, 64, 99, 224, 87, 203, 224, 83, 2, 144, 43, 32, 0, 79, 20, 33, 32, 162, 37, 81, + 246, 73, 132, 117, 228, 12, 110, 16, 235, 7, 30, 180, 27, 160, 14, 240, 189, 46, 91, 21, 216, + 64, 203, 90, 32, 4, 207, 119, 72, 96, 117, 72, 171, 211, 147, 31, 198, 86, 198, 33, 202, 8, 6, + 63, 191, 98, 133, 58, 151, 176, 55, 207, 50, 174, 186, 248, 239, 32, 5, 102, 223, 99, 115, 159, + 58, 4, 232, 62, 106, 40, 104, 197, 201, 116, 203, 33, 53, 13, 177, 69, 201, 180, 236, 150, 241, + 181, 218, 146, 10, 116, 32, 1, 229, 236, 16, 65, 233, 5, 25, 255, 34, 148, 148, 207, 205, 111, + 18, 183, 183, 223, 158, 88, 223, 85, 195, 89, 193, 138, 56, 116, 139, 16, 214, 32, 2, 195, 67, + 230, 197, 69, 139, 144, 214, 26, 24, 185, 48, 100, 137, 109, 31, 149, 226, 191, 153, 160, 76, + 154, 187, 218, 196, 45, 88, 165, 156, 249, 32, 4, 150, 62, 232, 148, 88, 193, 152, 250, 109, + 51, 174, 177, 44, 230, 166, 218, 76, 178, 43, 40, 205, 68, 44, 145, 166, 158, 254, 174, 149, + 36, 242, 32, 3, 14, 240, 4, 89, 157, 192, 58, 248, 159, 116, 211, 142, 150, 142, 236, 73, 182, + 90, 7, 91, 87, 7, 107, 189, 244, 179, 122, 127, 12, 35, 200, 32, 0, 102, 8, 193, 109, 36, 67, + 152, 19, 67, 42, 213, 132, 150, 154, 75, 167, 254, 116, 92, 85, 7, 210, 141, 9, 152, 66, 190, + 21, 152, 91, 153, 32, 1, 91, 26, 194, 246, 49, 88, 141, 120, 56, 129, 221, 243, 22, 225, 191, + 169, 145, 249, 173, 166, 166, 109, 88, 242, 17, 47, 51, 59, 30, 66, 1, 32, 3, 232, 219, 46, 93, + 245, 81, 62, 168, 92, 214, 120, 226, 205, 59, 177, 47, 110, 187, 138, 89, 242, 38, 118, 227, + 133, 239, 79, 143, 41, 76, 195, 32, 6, 96, 42, 9, 76, 140, 157, 64, 61, 18, 171, 162, 200, 156, + 48, 30, 116, 232, 116, 105, 54, 195, 84, 138, 241, 187, 228, 228, 188, 169, 108, 50, 32, 3, + 242, 246, 201, 183, 94, 3, 21, 82, 94, 19, 165, 232, 178, 141, 204, 58, 254, 226, 79, 253, 255, + 197, 255, 106, 177, 227, 92, 44, 3, 16, 148, 32, 7, 172, 240, 10, 110, 252, 147, 209, 62, 144, + 69, 91, 110, 89, 49, 137, 243, 236, 80, 229, 176, 61, 77, 238, 156, 26, 209, 115, 132, 240, 40, + 37, 32, 5, 56, 62, 108, 119, 202, 132, 171, 82, 235, 152, 214, 103, 223, 3, 209, 46, 5, 142, + 250, 0, 208, 197, 29, 191, 211, 7, 115, 228, 33, 108, 220, 32, 4, 215, 67, 154, 69, 154, 135, + 111, 35, 73, 247, 166, 78, 184, 82, 69, 114, 206, 159, 233, 186, 58, 99, 189, 163, 35, 51, 194, + 129, 196, 70, 175, 32, 7, 143, 126, 248, 168, 217, 88, 140, 0, 12, 61, 63, 246, 166, 185, 11, + 35, 151, 132, 157, 153, 254, 16, 148, 180, 109, 175, 72, 53, 36, 149, 68, 32, 1, 236, 79, 252, + 134, 255, 218, 253, 131, 249, 54, 44, 115, 128, 13, 110, 84, 125, 169, 171, 149, 21, 25, 25, + 43, 50, 96, 96, 240, 46, 18, 209, 23, 32, 7, 123, 254, 206, 143, 220, 186, 173, 38, 118, 168, + 213, 206, 142, 252, 212, 241, 204, 119, 97, 230, 125, 163, 44, 15, 99, 53, 38, 247, 9, 221, 5, + 32, 6, 65, 81, 64, 213, 111, 197, 141, 191, 201, 239, 87, 166, 191, 113, 153, 69, 74, 58, 63, + 119, 180, 145, 88, 240, 125, 76, 249, 230, 100, 76, 33, 32, 6, 65, 81, 64, 213, 111, 197, 141, + 191, 201, 239, 87, 166, 191, 113, 153, 69, 74, 58, 63, 119, 180, 145, 88, 240, 125, 76, 249, + 230, 100, 76, 33, 32, 6, 65, 81, 64, 213, 111, 197, 141, 191, 201, 239, 87, 166, 191, 113, 153, + 69, 74, 58, 63, 119, 180, 145, 88, 240, 125, 76, 249, 230, 100, 76, 33, 32, 5, 127, 151, 0, + 195, 85, 156, 93, 220, 124, 93, 211, 199, 213, 219, 169, 232, 163, 164, 216, 153, 69, 217, 95, + 174, 160, 27, 153, 77, 255, 143, 195, 32, 4, 116, 170, 73, 12, 229, 17, 213, 164, 3, 80, 214, + 11, 225, 8, 113, 66, 120, 217, 249, 198, 26, 209, 159, 110, 255, 6, 156, 32, 8, 242, 30, 32, 7, + 26, 124, 12, 227, 29, 202, 131, 159, 181, 209, 82, 246, 47, 231, 207, 6, 85, 19, 192, 218, 191, + 52, 31, 181, 194, 26, 87, 121, 151, 59, 70, 32, 6, 200, 217, 53, 151, 217, 208, 244, 172, 30, + 181, 142, 206, 36, 5, 51, 32, 245, 208, 31, 16, 249, 47, 43, 56, 155, 151, 35, 209, 66, 61, + 201, 32, 6, 16, 20, 202, 32, 119, 52, 245, 191, 147, 160, 244, 114, 155, 169, 23, 156, 26, 161, + 0, 166, 196, 161, 182, 62, 6, 131, 246, 243, 86, 39, 221, 32, 5, 5, 163, 229, 128, 60, 138, + 169, 223, 143, 34, 170, 203, 110, 41, 108, 51, 90, 243, 113, 20, 215, 31, 32, 53, 113, 150, 0, + 195, 229, 198, 180, 32, 6, 44, 176, 222, 123, 156, 157, 231, 226, 48, 148, 190, 187, 175, 32, + 100, 111, 48, 114, 173, 212, 193, 168, 158, 31, 119, 154, 13, 145, 47, 219, 154, 32, 2, 113, + 130, 114, 142, 187, 124, 33, 124, 16, 20, 163, 7, 135, 128, 195, 232, 186, 201, 37, 31, 180, + 151, 145, 61, 119, 163, 98, 230, 222, 255, 131, 32, 4, 186, 228, 154, 64, 145, 17, 193, 44, + 173, 111, 162, 13, 97, 122, 140, 184, 126, 230, 252, 139, 174, 186, 116, 229, 185, 81, 167, + 227, 172, 78, 7, 32, 7, 240, 155, 140, 34, 182, 40, 232, 244, 88, 158, 211, 201, 54, 132, 55, + 150, 207, 180, 66, 185, 128, 54, 145, 39, 226, 7, 188, 8, 191, 147, 74, 32, 6, 134, 39, 224, + 177, 50, 236, 236, 153, 60, 185, 186, 40, 212, 76, 252, 143, 184, 252, 118, 71, 93, 162, 133, + 66, 219, 167, 72, 9, 242, 255, 32, 32, 1, 175, 41, 43, 235, 241, 178, 150, 134, 241, 2, 209, + 204, 164, 137, 113, 245, 194, 181, 3, 76, 210, 255, 165, 17, 108, 25, 255, 198, 149, 65, 57, + 32, 1, 37, 226, 94, 6, 217, 61, 207, 85, 133, 227, 150, 102, 59, 43, 254, 164, 172, 211, 111, + 135, 239, 44, 158, 9, 121, 176, 242, 38, 78, 211, 38, 32, 0, 241, 143, 25, 238, 182, 108, 169, + 79, 193, 187, 243, 204, 195, 168, 56, 11, 79, 106, 139, 211, 183, 216, 46, 199, 2, 17, 104, + 196, 30, 159, 3, 32, 7, 49, 117, 239, 61, 103, 243, 249, 84, 100, 93, 128, 212, 230, 191, 75, + 204, 66, 228, 160, 172, 95, 235, 108, 61, 50, 101, 122, 183, 172, 29, 135, 32, 1, 137, 98, 241, + 24, 90, 195, 105, 148, 86, 253, 61, 129, 148, 255, 117, 38, 9, 72, 11, 57, 196, 49, 184, 45, + 195, 139, 134, 127, 128, 122, 164, 32, 2, 51, 119, 88, 85, 182, 72, 99, 157, 75, 108, 161, 111, + 186, 148, 194, 23, 244, 174, 92, 16, 6, 2, 7, 195, 130, 59, 168, 128, 140, 224, 155, 32, 3, 32, + 246, 95, 74, 197, 162, 195, 50, 53, 214, 192, 20, 143, 82, 58, 124, 45, 255, 158, 204, 214, 62, + 53, 60, 204, 135, 216, 177, 141, 72, 119, 32, 1, 207, 226, 59, 11, 86, 156, 112, 88, 167, 104, + 228, 76, 20, 89, 3, 136, 180, 95, 81, 151, 169, 88, 234, 196, 64, 171, 110, 94, 18, 237, 93, 7, + 223, 176, 137, 56, 34, 20, 220, 117, 204, 42, 101, 37, 254, 127, 159, 184, 45, 146, 224, 216, + 213, 171, 14, 47, 51, 242, 104, 53, 159, 183, 189, 142, 20, 35, 156, 90, 14, 139, 81, 151, 85, + 223, 66, 250, 192, 71, 233, 46, 70, 17, 154, 249, 67, 242, 43, 218, 37, 219, 8, 233, 176, 45, + 219, 183, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, 24, 136, 150, 9, 10, 132, 241, + 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, 231, 102, 253, 245, 174, 171, + 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, 195, 107, 199, 25, 191, 20, + 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, 13, 66, 10, 104, 202, 169, + 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, 8, 27, 237, 138, 226, 59, + 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, 194, 98, 234, 250, 167, 255, + 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, 217, 122, 69, 188, 177, 88, + 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, 238, 168, 55, 70, 208, 7, + 223, 176, 137, 56, 34, 20, 220, 117, 204, 42, 101, 37, 254, 127, 159, 184, 45, 146, 224, 216, + 213, 171, 14, 47, 51, 242, 104, 53, 159, 183, 189, 142, 20, 35, 156, 90, 14, 139, 81, 151, 85, + 223, 66, 250, 192, 71, 233, 46, 70, 17, 154, 249, 67, 242, 43, 218, 37, 219, 8, 233, 176, 45, + 219, 183, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, 24, 136, 150, 9, 10, 132, 241, + 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, 231, 102, 253, 245, 174, 171, + 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, 195, 107, 199, 25, 191, 20, + 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, 13, 66, 10, 104, 202, 169, + 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, 8, 27, 237, 138, 226, 59, + 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, 194, 98, 234, 250, 167, 255, + 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, 217, 122, 69, 188, 177, 88, + 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, 238, 168, 55, 70, 208, 2, 32, + 3, 217, 169, 177, 240, 4, 196, 128, 119, 155, 197, 53, 156, 199, 129, 214, 199, 115, 49, 99, + 221, 93, 178, 149, 239, 66, 87, 206, 118, 3, 14, 60, 32, 6, 87, 144, 100, 92, 84, 221, 234, 68, + 28, 139, 255, 104, 182, 74, 187, 18, 52, 203, 153, 252, 214, 202, 207, 140, 188, 228, 92, 76, + 181, 68, 146, 2, 32, 7, 72, 105, 187, 61, 213, 88, 1, 200, 104, 118, 116, 175, 244, 180, 112, + 101, 129, 142, 225, 30, 53, 245, 14, 34, 102, 171, 248, 86, 76, 249, 145, 32, 0, 156, 162, 126, + 22, 103, 4, 195, 229, 54, 125, 189, 196, 218, 227, 45, 130, 192, 214, 57, 20, 131, 93, 239, + 121, 38, 1, 128, 14, 153, 174, 205, 8, 44, 48, 189, 171, 247, 67, 41, 148, 159, 94, 72, 103, + 197, 198, 82, 66, 242, 249, 236, 141, 6, 123, 74, 227, 249, 206, 124, 47, 6, 104, 38, 230, 94, + 67, 253, 217, 249, 92, 206, 177, 100, 149, 47, 9, 185, 213, 11, 122, 221, 185, 44, 217, 47, 88, + 67, 78, 150, 36, 16, 37, 108, 107, 8, 115, 164, 164, 228, 237, 174, 82, 103, 190, 174, 187, 38, + 84, 24, 36, 133, 6, 191, 42, 16, 136, 195, 125, 107, 122, 215, 96, 219, 72, 76, 145, 38, 227, + 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, 29, 19, 195, 22, 35, 246, 10, 106, 109, + 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, 22, 144, 60, 121, 121, 64, 190, 105, 81, + 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, 106, 49, 93, 166, 34, 146, 145, 252, 153, + 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, 63, 64, 105, 4, 102, 211, 3, 43, 180, + 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, 197, 66, 155, 36, 12, 34, 61, 169, 95, + 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, 185, 201, 27, 71, 191, 77, 148, 228, 244, + 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, 26, 166, 148, 184, 91, 40, 185, 45, 214, + 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, 186, 141, 6, 8, 6, 66, 38, 16, 51, 119, 153, + 33, 189, 204, 188, 70, 40, 192, 179, 242, 238, 41, 48, 183, 178, 43, 146, 114, 227, 246, 152, + 194, 231, 155, 121, 149, 94, 67, 253, 217, 249, 92, 206, 177, 100, 149, 47, 9, 185, 213, 11, + 122, 221, 185, 44, 217, 47, 88, 67, 78, 150, 36, 16, 37, 108, 107, 8, 115, 164, 164, 228, 237, + 174, 82, 103, 190, 174, 187, 38, 84, 24, 36, 133, 6, 191, 42, 16, 136, 195, 125, 107, 122, 215, + 96, 219, 72, 76, 145, 38, 227, 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, 29, 19, + 195, 22, 35, 246, 10, 106, 109, 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, 22, 144, + 60, 121, 121, 64, 190, 105, 81, 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, 106, 49, + 93, 166, 34, 146, 145, 252, 153, 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, 63, 64, + 105, 4, 102, 211, 3, 43, 180, 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, 197, 66, + 155, 36, 12, 34, 61, 169, 95, 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, 185, 201, + 27, 71, 191, 77, 148, 228, 244, 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, 26, 166, + 148, 184, 91, 40, 185, 45, 214, 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, 186, 141, 6, + 36, 32, 3, 93, 72, 164, 72, 101, 137, 91, 50, 183, 167, 161, 235, 151, 19, 164, 69, 67, 138, + 192, 137, 97, 219, 32, 20, 126, 228, 1, 243, 84, 45, 40, 32, 0, 158, 110, 8, 12, 109, 214, 114, + 60, 23, 15, 171, 234, 87, 10, 24, 85, 255, 37, 163, 17, 166, 2, 189, 34, 250, 167, 64, 199, + 231, 68, 55, 32, 5, 12, 1, 165, 225, 71, 142, 63, 199, 27, 214, 62, 4, 7, 215, 119, 29, 204, + 176, 115, 44, 72, 45, 87, 253, 79, 104, 116, 220, 180, 99, 147, 32, 4, 209, 23, 205, 180, 18, + 18, 5, 133, 190, 98, 214, 252, 241, 64, 183, 234, 66, 9, 110, 133, 133, 252, 73, 122, 227, 44, + 221, 238, 127, 5, 190, 32, 0, 205, 182, 86, 228, 119, 212, 109, 157, 248, 48, 151, 3, 27, 143, + 210, 167, 197, 226, 35, 215, 173, 105, 126, 98, 90, 144, 169, 200, 216, 4, 27, 32, 7, 182, 153, + 192, 164, 94, 213, 7, 81, 178, 161, 104, 1, 253, 131, 118, 69, 226, 98, 112, 103, 138, 99, 200, + 16, 8, 213, 118, 146, 174, 172, 238, 32, 3, 138, 128, 166, 158, 66, 88, 106, 238, 115, 17, 236, + 1, 17, 18, 205, 217, 56, 172, 81, 37, 107, 16, 246, 41, 64, 227, 56, 191, 103, 249, 87, 32, 5, + 197, 64, 83, 79, 33, 44, 61, 247, 57, 136, 246, 0, 136, 137, 102, 236, 156, 86, 40, 146, 181, + 136, 123, 20, 160, 113, 156, 95, 179, 252, 172, 32, 6, 97, 73, 120, 25, 217, 174, 109, 140, + 223, 119, 82, 47, 54, 201, 127, 136, 196, 162, 77, 83, 117, 2, 151, 62, 68, 58, 246, 118, 115, + 38, 101, 32, 2, 157, 38, 116, 98, 175, 2, 6, 138, 30, 94, 136, 19, 148, 154, 76, 125, 244, 11, + 40, 93, 81, 83, 119, 116, 203, 167, 224, 50, 191, 159, 159, 32, 0, 175, 156, 11, 75, 255, 239, + 88, 118, 72, 86, 193, 185, 17, 52, 234, 231, 153, 47, 173, 77, 247, 77, 216, 57, 191, 20, 5, + 200, 213, 74, 117, 32, 0, 62, 153, 65, 208, 118, 182, 23, 174, 164, 155, 159, 232, 5, 38, 51, + 63, 181, 127, 35, 243, 123, 219, 125, 192, 209, 221, 203, 76, 82, 98, 86, 32, 5, 22, 232, 170, + 86, 80, 89, 25, 218, 109, 61, 10, 143, 104, 0, 127, 160, 66, 115, 104, 106, 128, 168, 41, 111, + 29, 36, 98, 131, 0, 67, 246, 32, 1, 206, 169, 179, 80, 23, 138, 158, 196, 38, 80, 29, 56, 215, + 173, 212, 221, 84, 28, 18, 145, 203, 40, 27, 75, 188, 125, 92, 200, 191, 96, 148, 32, 4, 101, + 254, 40, 26, 84, 221, 157, 243, 85, 218, 229, 203, 94, 91, 182, 129, 32, 133, 66, 82, 255, 210, + 103, 89, 210, 64, 214, 170, 248, 216, 89, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 93, 83, 13, 39, 225, 51, 125, 213, 246, + 158, 232, 243, 193, 188, 211, 178, 58, 244, 128, 89, 225, 155, 190, 28, 228, 120, 50, 132, 29, + 234, 244, 32, 7, 219, 227, 185, 248, 229, 236, 137, 91, 50, 171, 67, 42, 191, 128, 161, 154, + 154, 107, 199, 194, 156, 33, 2, 143, 255, 67, 51, 24, 104, 133, 14, 32, 5, 146, 19, 155, 160, + 141, 85, 232, 35, 7, 21, 242, 138, 101, 227, 115, 172, 245, 197, 2, 96, 173, 89, 196, 94, 161, + 149, 172, 204, 213, 77, 241, 32, 7, 249, 80, 4, 89, 1, 199, 167, 27, 139, 62, 219, 86, 89, 72, + 123, 115, 154, 224, 30, 134, 235, 116, 44, 8, 144, 129, 32, 105, 174, 180, 51, 32, 3, 109, 79, + 177, 19, 59, 134, 178, 84, 88, 166, 192, 234, 24, 132, 50, 9, 52, 190, 175, 103, 0, 114, 130, + 129, 252, 85, 105, 80, 13, 215, 44, 32, 0, 65, 132, 220, 157, 57, 141, 161, 237, 95, 246, 72, + 187, 213, 187, 214, 26, 96, 82, 179, 52, 229, 229, 37, 214, 245, 159, 218, 49, 194, 193, 152, + 32, 3, 183, 108, 42, 159, 80, 215, 118, 17, 80, 122, 97, 61, 97, 29, 254, 39, 135, 228, 135, + 94, 107, 106, 221, 63, 255, 38, 138, 6, 196, 241, 139, 32, 4, 138, 187, 53, 147, 230, 88, 117, + 192, 126, 19, 155, 155, 156, 127, 50, 196, 43, 177, 64, 239, 117, 169, 171, 254, 53, 246, 1, 9, + 12, 34, 196, 32, 4, 160, 6, 194, 185, 109, 38, 218, 213, 228, 88, 0, 31, 246, 241, 176, 177, + 119, 122, 108, 5, 184, 189, 59, 198, 144, 139, 221, 103, 10, 165, 2, 32, 2, 111, 100, 250, 248, + 222, 100, 64, 48, 166, 146, 122, 2, 8, 68, 252, 92, 143, 51, 116, 45, 110, 98, 100, 44, 170, + 181, 38, 109, 65, 195, 204, 32, 1, 20, 223, 54, 185, 95, 217, 122, 110, 8, 220, 187, 76, 74, + 65, 22, 142, 193, 220, 144, 118, 178, 193, 68, 147, 34, 251, 2, 23, 86, 59, 84, 32, 4, 11, 211, + 18, 40, 27, 51, 227, 51, 84, 194, 0, 1, 238, 114, 180, 172, 52, 106, 108, 246, 211, 240, 67, + 31, 136, 195, 86, 130, 203, 152, 13, 32, 3, 59, 216, 203, 252, 156, 121, 46, 5, 129, 98, 56, + 105, 47, 215, 70, 255, 253, 194, 22, 92, 185, 162, 24, 189, 240, 19, 167, 129, 51, 118, 15, 32, + 6, 218, 40, 8, 230, 94, 223, 24, 55, 176, 253, 83, 23, 76, 163, 40, 55, 166, 131, 30, 122, 192, + 213, 155, 114, 135, 8, 36, 157, 222, 29, 33, 32, 7, 158, 22, 33, 155, 10, 170, 45, 59, 39, 103, + 96, 105, 160, 76, 244, 221, 24, 125, 27, 163, 37, 121, 209, 200, 87, 71, 74, 128, 187, 192, 77, + 32, 1, 233, 235, 60, 224, 219, 201, 97, 98, 250, 177, 160, 110, 194, 21, 88, 29, 170, 204, 12, + 223, 187, 86, 195, 43, 127, 188, 63, 33, 176, 119, 155, 32, 7, 144, 93, 56, 141, 120, 249, 210, + 19, 205, 141, 91, 105, 178, 112, 63, 196, 79, 204, 39, 17, 230, 127, 8, 93, 82, 101, 30, 208, + 100, 221, 55, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 36, + 32, 4, 198, 99, 47, 102, 150, 146, 77, 205, 39, 228, 73, 208, 170, 131, 91, 229, 149, 154, 108, + 150, 155, 148, 154, 250, 98, 98, 240, 61, 154, 247, 24, 32, 4, 94, 5, 186, 86, 10, 188, 71, 33, + 154, 51, 118, 173, 32, 170, 225, 114, 150, 144, 214, 197, 235, 56, 211, 75, 119, 174, 112, 233, + 134, 174, 246, 32, 6, 214, 144, 50, 68, 104, 213, 16, 190, 137, 200, 55, 159, 110, 39, 163, 41, + 71, 7, 35, 94, 3, 92, 153, 218, 69, 52, 27, 30, 199, 213, 181, 32, 0, 116, 127, 190, 3, 87, + 233, 226, 70, 40, 211, 143, 245, 140, 24, 29, 26, 136, 122, 149, 187, 102, 54, 132, 255, 0, + 112, 16, 195, 254, 74, 70, 32, 3, 133, 116, 230, 181, 99, 254, 244, 122, 42, 252, 158, 208, + 173, 141, 214, 205, 60, 187, 213, 75, 112, 84, 154, 121, 184, 79, 230, 137, 204, 151, 160, 32, + 5, 110, 77, 198, 197, 214, 119, 53, 127, 26, 252, 4, 108, 154, 64, 215, 160, 192, 232, 92, 43, + 150, 104, 189, 48, 198, 58, 17, 232, 126, 121, 248, 32, 0, 96, 162, 110, 38, 151, 101, 178, 95, + 20, 2, 138, 205, 173, 58, 137, 128, 172, 7, 37, 18, 32, 29, 212, 234, 147, 6, 25, 14, 62, 61, + 42, 32, 0, 48, 81, 55, 19, 75, 178, 217, 47, 138, 1, 69, 102, 214, 157, 68, 192, 86, 3, 146, + 137, 16, 14, 234, 117, 73, 131, 12, 135, 31, 30, 149, 32, 0, 70, 215, 101, 166, 112, 230, 215, + 64, 31, 158, 62, 130, 47, 190, 212, 203, 151, 94, 13, 91, 191, 102, 229, 251, 161, 134, 218, + 86, 116, 251, 118, 32, 7, 148, 42, 133, 153, 156, 222, 114, 121, 161, 47, 246, 4, 104, 253, 82, + 143, 222, 175, 136, 17, 84, 9, 185, 73, 24, 157, 55, 77, 203, 206, 170, 32, 6, 95, 234, 173, + 56, 114, 7, 142, 82, 41, 132, 108, 170, 143, 82, 105, 193, 167, 44, 204, 178, 1, 244, 42, 127, + 140, 103, 23, 202, 169, 243, 42, 32, 2, 77, 249, 28, 214, 193, 240, 93, 31, 25, 106, 39, 150, + 196, 222, 90, 225, 108, 105, 128, 180, 141, 3, 101, 242, 62, 156, 7, 172, 206, 229, 231, 32, 3, + 46, 149, 238, 10, 122, 151, 202, 36, 91, 125, 53, 54, 22, 155, 163, 176, 198, 51, 163, 149, + 187, 117, 74, 51, 8, 165, 38, 14, 24, 245, 40, 32, 4, 239, 189, 161, 235, 217, 214, 24, 228, + 113, 16, 30, 82, 45, 123, 159, 104, 103, 91, 25, 207, 207, 250, 116, 228, 250, 245, 176, 93, 7, + 252, 107, 32, 2, 166, 141, 155, 18, 183, 248, 119, 26, 147, 37, 170, 247, 219, 46, 2, 31, 160, + 9, 208, 255, 31, 92, 171, 51, 122, 64, 44, 65, 105, 106, 97, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, 156, 46, 34, 115, 162, + 75, 166, 181, 116, 156, 220, 60, 49, 126, 243, 206, 46, 132, 61, 180, 42, 193, 57, 105, 171, + 49, 92, 216, 164, 255, 63, 32, 2, 111, 35, 213, 86, 255, 227, 39, 70, 26, 138, 73, 140, 112, + 19, 17, 180, 33, 56, 2, 82, 70, 96, 99, 52, 178, 58, 41, 184, 87, 179, 113, 32, 3, 147, 237, + 53, 138, 141, 166, 16, 37, 177, 164, 176, 191, 57, 129, 184, 47, 249, 237, 203, 85, 179, 192, + 68, 69, 59, 199, 14, 83, 181, 238, 61, 32, 1, 118, 29, 91, 8, 184, 237, 60, 139, 126, 142, 141, + 59, 114, 71, 146, 78, 93, 78, 242, 123, 222, 219, 252, 43, 225, 140, 28, 230, 172, 188, 58, 32, + 1, 84, 130, 32, 213, 78, 79, 117, 206, 162, 1, 8, 224, 147, 200, 238, 60, 27, 30, 226, 8, 3, + 71, 210, 50, 243, 97, 91, 91, 212, 126, 174, 32, 0, 224, 32, 20, 176, 200, 99, 47, 72, 141, 39, + 151, 121, 176, 140, 240, 31, 227, 173, 200, 71, 167, 168, 90, 97, 117, 3, 120, 21, 185, 211, + 224, 32, 1, 37, 68, 6, 241, 202, 81, 117, 47, 144, 18, 225, 165, 193, 17, 181, 203, 96, 21, + 248, 79, 44, 199, 18, 158, 228, 197, 98, 156, 192, 94, 80, 32, 5, 185, 214, 73, 108, 240, 134, + 9, 148, 89, 43, 205, 61, 124, 125, 98, 76, 85, 5, 182, 185, 175, 212, 9, 48, 201, 89, 45, 183, + 104, 130, 89, 32, 4, 230, 12, 106, 169, 13, 106, 222, 118, 104, 250, 215, 212, 230, 34, 131, 8, + 142, 71, 207, 35, 197, 114, 84, 3, 18, 90, 18, 92, 190, 198, 231, 32, 6, 129, 116, 116, 248, + 215, 104, 164, 63, 123, 158, 128, 206, 108, 34, 113, 238, 69, 104, 59, 68, 244, 166, 220, 58, + 209, 172, 56, 49, 246, 59, 251, 32, 5, 86, 230, 229, 219, 163, 126, 239, 151, 183, 79, 219, + 168, 206, 75, 82, 196, 130, 131, 63, 170, 158, 100, 118, 233, 237, 114, 173, 176, 238, 108, + 217, 32, 7, 244, 155, 40, 219, 168, 42, 187, 215, 244, 185, 179, 251, 71, 122, 223, 29, 143, + 140, 84, 83, 0, 21, 74, 227, 57, 248, 25, 153, 112, 255, 181, 32, 7, 207, 113, 72, 180, 24, + 231, 110, 224, 195, 157, 3, 64, 86, 79, 206, 101, 110, 184, 221, 7, 149, 11, 254, 197, 182, + 100, 80, 245, 58, 63, 147, 32, 5, 221, 109, 132, 218, 56, 223, 127, 0, 226, 252, 251, 156, 5, + 76, 45, 87, 250, 6, 120, 225, 77, 119, 106, 247, 192, 187, 108, 242, 39, 4, 167, 32, 5, 115, + 229, 56, 54, 6, 240, 24, 242, 136, 101, 92, 116, 84, 44, 239, 43, 133, 203, 167, 3, 81, 139, 9, + 192, 77, 237, 242, 251, 5, 68, 235, 32, 4, 114, 254, 69, 61, 206, 97, 32, 57, 35, 33, 228, 65, + 18, 161, 248, 118, 151, 197, 161, 100, 84, 220, 9, 162, 222, 214, 19, 5, 249, 5, 5, 32, 6, 72, + 194, 21, 30, 130, 163, 152, 41, 83, 40, 242, 19, 88, 251, 153, 240, 87, 52, 164, 156, 51, 138, + 64, 89, 145, 227, 141, 228, 120, 119, 186, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 252, 129, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, 134, 32, 7, - 255, 255, 255, 255, 255, 219, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 225, 32, 7, 144, 1, 16, 1, 16, 151, 69, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 238, 144, 0, - 16, 0, 16, 8, 230, 32, 7, 255, 255, 255, 255, 255, 202, 240, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 225, 32, 0, - 176, 3, 48, 1, 17, 69, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 246, 253, 176, 0, 48, 0, 16, 19, 37, 32, 7, 255, 255, 255, 255, 255, 245, 112, 255, + 255, 255, 239, 255, 225, 1, 8, 198, 23, 84, 255, 151, 170, 24, 10, 205, 81, 45, 252, 78, 227, + 104, 43, 34, 71, 48, 21, 221, 59, 83, 55, 230, 173, 205, 23, 165, 63, 164, 81, 214, 5, 201, + 144, 116, 141, 237, 183, 99, 166, 133, 98, 237, 150, 237, 117, 64, 62, 158, 103, 219, 38, 180, + 159, 216, 51, 118, 132, 190, 36, 174, 82, 184, 132, 162, 205, 221, 202, 214, 145, 217, 167, 46, + 204, 125, 71, 164, 57, 104, 83, 60, 227, 147, 221, 50, 19, 127, 168, 46, 215, 74, 229, 109, + 113, 208, 168, 36, 249, 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, 186, 134, 137, 48, 43, 33, + 53, 149, 217, 125, 46, 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, 144, 161, 214, 44, 14, 190, + 143, 160, 9, 57, 35, 119, 113, 194, 148, 35, 100, 168, 241, 151, 40, 203, 46, 56, 120, 109, + 133, 0, 249, 165, 107, 85, 164, 80, 243, 242, 36, 235, 226, 199, 191, 219, 14, 217, 84, 59, 78, + 176, 165, 80, 100, 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, 160, 180, 121, 49, 249, 172, + 16, 189, 110, 38, 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, 172, 17, 108, 168, 101, 136, + 252, 118, 147, 242, 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, 12, 53, 221, 137, 122, 171, 78, + 203, 75, 123, 4, 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, 190, 94, 157, 8, 170, 178, 81, + 208, 81, 111, 31, 28, 126, 253, 70, 188, 190, 185, 39, 217, 50, 111, 70, 67, 107, 101, 179, 74, + 138, 102, 107, 71, 148, 178, 73, 169, 214, 5, 201, 144, 116, 141, 237, 183, 99, 166, 133, 98, + 237, 150, 237, 117, 64, 62, 158, 103, 219, 38, 180, 159, 216, 51, 118, 132, 190, 36, 174, 82, + 184, 132, 162, 205, 221, 202, 214, 145, 217, 167, 46, 204, 125, 71, 164, 57, 104, 83, 60, 227, + 147, 221, 50, 19, 127, 168, 46, 215, 74, 229, 109, 113, 208, 168, 36, 249, 140, 75, 236, 88, + 39, 14, 52, 42, 174, 36, 186, 134, 137, 48, 43, 33, 53, 149, 217, 125, 46, 95, 174, 91, 171, + 36, 50, 83, 66, 229, 202, 144, 161, 214, 44, 14, 190, 143, 160, 9, 57, 35, 119, 113, 194, 148, + 35, 100, 168, 241, 151, 40, 203, 46, 56, 120, 109, 133, 0, 249, 165, 107, 85, 164, 80, 243, + 242, 36, 235, 226, 199, 191, 219, 14, 217, 84, 59, 78, 176, 165, 80, 100, 218, 54, 140, 125, + 197, 249, 99, 247, 17, 71, 160, 180, 121, 49, 249, 172, 16, 189, 110, 38, 119, 42, 116, 95, + 138, 195, 26, 201, 251, 87, 172, 17, 108, 168, 101, 136, 252, 118, 147, 242, 33, 11, 16, 200, + 158, 124, 254, 165, 5, 73, 12, 53, 221, 137, 122, 171, 78, 203, 75, 123, 4, 181, 50, 42, 37, + 32, 13, 234, 176, 114, 190, 190, 94, 157, 23, 32, 1, 226, 90, 177, 34, 62, 201, 180, 249, 9, + 215, 203, 139, 66, 88, 169, 207, 196, 116, 47, 36, 127, 53, 131, 194, 0, 71, 133, 68, 125, 203, + 156, 32, 5, 84, 147, 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, 206, 188, 12, 47, 68, 76, + 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, 23, 95, 32, 5, 84, 147, 243, 72, 44, 231, 137, + 95, 82, 224, 204, 154, 206, 188, 12, 47, 68, 76, 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, + 23, 95, 32, 5, 84, 147, 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, 206, 188, 12, 47, 68, 76, + 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, 23, 95, 32, 3, 90, 112, 38, 248, 174, 225, 80, + 47, 99, 208, 220, 166, 201, 154, 71, 62, 141, 42, 104, 172, 207, 250, 140, 201, 249, 74, 88, + 26, 166, 127, 212, 32, 5, 134, 245, 233, 202, 185, 178, 113, 74, 59, 208, 205, 65, 8, 236, 118, + 243, 40, 83, 202, 162, 159, 83, 0, 191, 255, 58, 82, 220, 255, 23, 29, 32, 6, 227, 171, 139, + 219, 139, 164, 90, 41, 98, 199, 188, 177, 9, 186, 202, 145, 64, 5, 126, 143, 190, 146, 169, 7, + 143, 169, 218, 35, 68, 235, 128, 32, 0, 130, 168, 205, 202, 184, 230, 169, 214, 27, 165, 47, + 104, 18, 54, 173, 167, 144, 26, 11, 42, 182, 168, 131, 255, 193, 120, 81, 92, 85, 21, 9, 32, 0, + 198, 81, 52, 192, 76, 178, 36, 188, 172, 159, 24, 73, 243, 74, 123, 37, 10, 41, 255, 32, 109, + 116, 92, 255, 49, 76, 67, 28, 250, 217, 188, 32, 1, 236, 236, 188, 57, 240, 250, 49, 136, 157, + 51, 197, 131, 190, 82, 109, 93, 176, 233, 189, 132, 124, 15, 150, 83, 200, 212, 101, 116, 234, + 125, 71, 32, 2, 65, 199, 238, 237, 253, 238, 0, 191, 87, 58, 174, 152, 1, 151, 187, 90, 87, + 125, 195, 41, 103, 49, 189, 18, 125, 62, 190, 24, 62, 181, 83, 32, 4, 198, 36, 193, 246, 195, + 182, 251, 63, 57, 13, 176, 164, 25, 122, 253, 188, 150, 158, 106, 136, 241, 239, 75, 249, 173, + 62, 56, 58, 118, 253, 98, 32, 7, 152, 239, 4, 75, 94, 140, 52, 205, 198, 243, 9, 254, 146, 160, + 50, 36, 61, 193, 169, 85, 180, 218, 212, 66, 18, 232, 128, 75, 217, 49, 104, 32, 4, 150, 204, + 12, 168, 216, 22, 200, 19, 209, 117, 116, 14, 1, 6, 73, 199, 148, 170, 40, 201, 65, 239, 108, + 97, 54, 161, 208, 73, 96, 40, 77, 32, 0, 89, 1, 107, 47, 187, 250, 147, 133, 134, 49, 97, 130, + 198, 199, 203, 218, 83, 147, 96, 23, 159, 218, 246, 198, 194, 81, 13, 28, 75, 53, 47, 32, 1, + 28, 219, 187, 19, 102, 9, 144, 68, 190, 184, 76, 169, 190, 127, 131, 71, 71, 99, 157, 255, 174, + 167, 193, 86, 169, 29, 171, 160, 168, 87, 20, 32, 4, 220, 160, 34, 62, 11, 188, 248, 175, 118, + 70, 24, 125, 79, 86, 37, 114, 155, 49, 100, 78, 8, 48, 75, 159, 236, 21, 197, 193, 244, 118, + 250, 32, 3, 55, 131, 240, 131, 7, 226, 70, 14, 219, 44, 30, 186, 18, 176, 152, 179, 224, 211, + 175, 71, 201, 115, 172, 16, 8, 196, 103, 206, 249, 150, 82, 32, 1, 77, 168, 185, 25, 164, 38, + 14, 193, 137, 113, 154, 54, 217, 60, 156, 38, 138, 122, 105, 254, 17, 102, 164, 221, 43, 37, + 170, 221, 72, 232, 151, 32, 3, 37, 118, 187, 49, 235, 31, 41, 121, 145, 157, 107, 21, 174, 81, + 106, 253, 208, 11, 111, 44, 169, 123, 199, 86, 247, 34, 146, 159, 78, 78, 134, 32, 6, 25, 172, + 62, 68, 76, 1, 151, 154, 104, 86, 6, 130, 165, 106, 33, 176, 185, 58, 216, 16, 206, 236, 152, + 171, 90, 91, 42, 229, 248, 62, 237, 32, 4, 6, 28, 244, 157, 110, 212, 16, 249, 146, 113, 14, + 89, 246, 188, 124, 152, 104, 62, 116, 143, 17, 229, 77, 42, 21, 54, 36, 127, 143, 46, 180, 32, + 6, 213, 30, 201, 15, 161, 166, 224, 201, 122, 48, 53, 149, 96, 216, 206, 129, 31, 134, 225, 26, + 111, 0, 127, 54, 100, 16, 33, 24, 239, 169, 118, 23, 32, 6, 5, 152, 2, 28, 99, 252, 217, 4, + 193, 210, 48, 97, 240, 47, 63, 19, 117, 140, 245, 109, 151, 204, 220, 129, 39, 253, 49, 242, + 168, 120, 151, 32, 5, 174, 60, 34, 214, 147, 223, 113, 120, 155, 177, 195, 231, 225, 156, 129, + 231, 208, 52, 152, 129, 190, 176, 115, 224, 38, 54, 197, 65, 184, 142, 232, 32, 5, 174, 60, 34, + 214, 147, 223, 113, 120, 155, 177, 195, 231, 225, 156, 129, 231, 208, 52, 152, 129, 190, 176, + 115, 224, 38, 54, 197, 65, 184, 142, 232, 32, 5, 174, 60, 34, 214, 147, 223, 113, 120, 155, + 177, 195, 231, 225, 156, 129, 231, 208, 52, 152, 129, 190, 176, 115, 224, 38, 54, 197, 65, 184, + 142, 232, 32, 7, 121, 221, 240, 129, 105, 88, 243, 253, 201, 60, 90, 139, 168, 81, 179, 130, + 76, 9, 95, 160, 104, 0, 244, 217, 146, 66, 101, 158, 81, 168, 54, 32, 3, 213, 73, 227, 177, + 207, 252, 173, 28, 118, 100, 147, 55, 66, 251, 107, 88, 24, 5, 238, 106, 72, 82, 218, 80, 172, + 99, 216, 235, 159, 151, 50, 32, 7, 166, 82, 123, 107, 43, 164, 75, 210, 180, 109, 36, 0, 35, + 152, 37, 8, 141, 249, 252, 44, 138, 67, 152, 2, 224, 75, 38, 106, 110, 253, 236, 32, 4, 41, 74, + 94, 251, 100, 113, 67, 170, 6, 100, 54, 157, 118, 217, 101, 75, 145, 69, 14, 165, 142, 150, + 236, 107, 5, 130, 59, 182, 189, 116, 1, 32, 7, 53, 87, 13, 68, 248, 17, 250, 175, 182, 241, + 183, 220, 59, 172, 179, 118, 165, 111, 58, 136, 7, 230, 99, 169, 178, 182, 245, 120, 169, 158, + 50, 32, 2, 86, 206, 226, 81, 43, 2, 142, 153, 222, 85, 106, 197, 123, 159, 24, 79, 55, 128, 92, + 124, 80, 81, 214, 171, 62, 227, 253, 77, 143, 102, 42, 32, 0, 71, 65, 188, 141, 114, 57, 192, + 29, 15, 199, 3, 116, 181, 84, 185, 134, 76, 5, 81, 13, 253, 171, 180, 121, 221, 223, 175, 88, + 166, 11, 196, 32, 6, 57, 225, 140, 207, 235, 77, 232, 93, 0, 172, 133, 213, 23, 104, 96, 146, + 118, 242, 175, 33, 244, 78, 83, 99, 173, 165, 213, 204, 30, 253, 95, 32, 4, 83, 171, 84, 96, + 90, 141, 133, 240, 109, 239, 84, 166, 217, 236, 205, 142, 191, 73, 197, 45, 188, 207, 222, 60, + 225, 187, 184, 238, 102, 84, 103, 32, 0, 248, 88, 98, 94, 207, 141, 75, 237, 61, 234, 162, 40, + 176, 186, 22, 52, 93, 138, 100, 151, 157, 182, 64, 247, 146, 176, 157, 203, 35, 208, 185, 32, + 6, 178, 43, 190, 231, 177, 156, 59, 89, 32, 7, 140, 237, 213, 218, 119, 18, 186, 119, 179, 205, + 10, 76, 209, 60, 72, 173, 79, 129, 116, 34, 212, 32, 7, 232, 242, 81, 73, 173, 219, 223, 87, 6, + 238, 88, 194, 11, 217, 198, 236, 17, 49, 50, 99, 140, 203, 66, 62, 38, 52, 62, 34, 39, 73, 208, + 32, 7, 164, 100, 246, 243, 192, 242, 11, 3, 187, 235, 185, 48, 141, 142, 105, 234, 220, 67, + 207, 112, 68, 78, 196, 178, 224, 209, 7, 183, 12, 184, 178, 32, 7, 134, 217, 46, 19, 216, 242, + 5, 148, 52, 225, 134, 144, 179, 143, 210, 3, 36, 53, 103, 68, 180, 1, 170, 39, 27, 89, 253, 40, + 180, 186, 55, 32, 2, 51, 50, 203, 226, 167, 22, 7, 220, 184, 62, 60, 166, 91, 148, 101, 64, 96, + 252, 158, 211, 27, 103, 227, 108, 209, 86, 179, 120, 142, 108, 125, 32, 0, 214, 90, 141, 11, + 83, 126, 234, 160, 200, 182, 133, 217, 156, 187, 122, 199, 33, 63, 147, 194, 184, 189, 155, + 189, 176, 50, 183, 77, 239, 118, 65, 32, 2, 220, 62, 108, 245, 46, 101, 64, 164, 180, 143, 208, + 135, 122, 178, 73, 13, 227, 145, 18, 129, 86, 111, 249, 131, 202, 162, 107, 222, 148, 145, 90, + 32, 0, 220, 193, 44, 213, 8, 251, 223, 112, 251, 65, 71, 90, 159, 20, 177, 98, 21, 184, 150, + 66, 54, 11, 248, 231, 48, 118, 114, 29, 66, 143, 26, 32, 4, 89, 205, 230, 246, 77, 248, 106, + 59, 250, 124, 66, 250, 146, 104, 183, 83, 64, 79, 182, 76, 27, 7, 149, 119, 76, 145, 118, 129, + 21, 32, 98, 7, 223, 176, 137, 56, 34, 20, 220, 117, 204, 42, 101, 37, 254, 127, 159, 184, 45, + 146, 224, 216, 213, 171, 14, 47, 51, 242, 104, 53, 159, 183, 189, 142, 20, 35, 156, 90, 14, + 139, 81, 151, 85, 223, 66, 250, 192, 71, 233, 46, 70, 17, 154, 249, 67, 242, 43, 218, 37, 219, + 8, 233, 176, 45, 219, 183, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, 24, 136, 150, + 9, 10, 132, 241, 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, 231, 102, + 253, 245, 174, 171, 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, 195, 107, + 199, 25, 191, 20, 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, 13, 66, 10, + 104, 202, 169, 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, 8, 27, 237, + 138, 226, 59, 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, 194, 98, 234, + 250, 167, 255, 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, 217, 122, 69, + 188, 177, 88, 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, 238, 168, 55, + 70, 208, 7, 223, 176, 137, 56, 34, 20, 220, 117, 204, 42, 101, 37, 254, 127, 159, 184, 45, 146, + 224, 216, 213, 171, 14, 47, 51, 242, 104, 53, 159, 183, 189, 142, 20, 35, 156, 90, 14, 139, 81, + 151, 85, 223, 66, 250, 192, 71, 233, 46, 70, 17, 154, 249, 67, 242, 43, 218, 37, 219, 8, 233, + 176, 45, 219, 183, 202, 146, 92, 237, 231, 92, 225, 161, 236, 140, 152, 24, 136, 150, 9, 10, + 132, 241, 33, 38, 195, 190, 159, 14, 216, 41, 128, 143, 188, 48, 251, 239, 231, 102, 253, 245, + 174, 171, 190, 149, 90, 248, 5, 170, 199, 101, 38, 205, 66, 24, 120, 101, 195, 107, 199, 25, + 191, 20, 195, 54, 3, 190, 149, 34, 232, 6, 174, 198, 153, 194, 67, 57, 25, 13, 66, 10, 104, + 202, 169, 248, 127, 114, 195, 29, 6, 232, 245, 21, 133, 119, 106, 167, 44, 27, 8, 27, 237, 138, + 226, 59, 242, 120, 11, 188, 127, 143, 128, 243, 155, 247, 151, 78, 29, 103, 194, 98, 234, 250, + 167, 255, 241, 154, 113, 186, 85, 108, 240, 84, 111, 163, 238, 222, 68, 50, 217, 122, 69, 188, + 177, 88, 171, 3, 59, 238, 53, 27, 39, 235, 53, 93, 13, 190, 107, 174, 144, 238, 168, 55, 70, + 208, 2, 32, 3, 217, 169, 177, 240, 4, 196, 128, 119, 155, 197, 53, 156, 199, 129, 214, 199, + 115, 49, 99, 221, 93, 178, 149, 239, 66, 87, 206, 118, 3, 14, 60, 32, 6, 87, 144, 100, 92, 84, + 221, 234, 68, 28, 139, 255, 104, 182, 74, 187, 18, 52, 203, 153, 252, 214, 202, 207, 140, 188, + 228, 92, 76, 181, 68, 146, 2, 32, 7, 72, 105, 187, 61, 213, 88, 1, 200, 104, 118, 116, 175, + 244, 180, 112, 101, 129, 142, 225, 30, 53, 245, 14, 34, 102, 171, 248, 86, 76, 249, 145, 32, 0, + 156, 162, 126, 22, 103, 4, 195, 229, 54, 125, 189, 196, 218, 227, 45, 130, 192, 214, 57, 20, + 131, 93, 239, 121, 38, 1, 128, 14, 153, 174, 205, 8, 44, 48, 189, 171, 247, 67, 41, 148, 159, + 94, 72, 103, 197, 198, 82, 66, 242, 249, 236, 141, 6, 123, 74, 227, 249, 206, 124, 47, 6, 104, + 38, 230, 94, 67, 253, 217, 249, 92, 206, 177, 100, 149, 47, 9, 185, 213, 11, 122, 221, 185, 44, + 217, 47, 88, 67, 78, 150, 36, 16, 37, 108, 107, 8, 115, 164, 164, 228, 237, 174, 82, 103, 190, + 174, 187, 38, 84, 24, 36, 133, 6, 191, 42, 16, 136, 195, 125, 107, 122, 215, 96, 219, 72, 76, + 145, 38, 227, 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, 29, 19, 195, 22, 35, 246, + 10, 106, 109, 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, 22, 144, 60, 121, 121, 64, + 190, 105, 81, 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, 106, 49, 93, 166, 34, 146, + 145, 252, 153, 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, 63, 64, 105, 4, 102, 211, + 3, 43, 180, 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, 197, 66, 155, 36, 12, 34, 61, + 169, 95, 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, 185, 201, 27, 71, 191, 77, 148, + 228, 244, 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, 26, 166, 148, 184, 91, 40, 185, + 45, 214, 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, 186, 141, 6, 8, 6, 66, 38, 16, 51, + 119, 153, 33, 189, 204, 188, 70, 40, 192, 179, 242, 238, 41, 48, 183, 178, 43, 146, 114, 227, + 246, 152, 194, 231, 155, 121, 149, 94, 67, 253, 217, 249, 92, 206, 177, 100, 149, 47, 9, 185, + 213, 11, 122, 221, 185, 44, 217, 47, 88, 67, 78, 150, 36, 16, 37, 108, 107, 8, 115, 164, 164, + 228, 237, 174, 82, 103, 190, 174, 187, 38, 84, 24, 36, 133, 6, 191, 42, 16, 136, 195, 125, 107, + 122, 215, 96, 219, 72, 76, 145, 38, 227, 23, 142, 0, 23, 70, 116, 106, 121, 99, 128, 210, 78, + 29, 19, 195, 22, 35, 246, 10, 106, 109, 28, 115, 181, 223, 237, 182, 4, 20, 128, 109, 23, 191, + 22, 144, 60, 121, 121, 64, 190, 105, 81, 199, 185, 217, 98, 169, 242, 90, 255, 155, 197, 57, + 106, 49, 93, 166, 34, 146, 145, 252, 153, 151, 197, 90, 125, 16, 25, 41, 188, 114, 58, 89, 239, + 63, 64, 105, 4, 102, 211, 3, 43, 180, 109, 69, 210, 168, 47, 199, 3, 231, 17, 41, 201, 253, + 197, 66, 155, 36, 12, 34, 61, 169, 95, 84, 39, 35, 149, 250, 234, 76, 165, 17, 124, 40, 205, + 185, 201, 27, 71, 191, 77, 148, 228, 244, 148, 31, 91, 252, 89, 141, 6, 13, 139, 105, 158, 82, + 26, 166, 148, 184, 91, 40, 185, 45, 214, 150, 78, 230, 134, 109, 250, 24, 78, 249, 49, 197, + 186, 141, 6, 36, 32, 3, 93, 72, 164, 72, 101, 137, 91, 50, 183, 167, 161, 235, 151, 19, 164, + 69, 67, 138, 192, 137, 97, 219, 32, 20, 126, 228, 1, 243, 84, 45, 40, 32, 0, 158, 110, 8, 12, + 109, 214, 114, 60, 23, 15, 171, 234, 87, 10, 24, 85, 255, 37, 163, 17, 166, 2, 189, 34, 250, + 167, 64, 199, 231, 68, 55, 32, 5, 12, 1, 165, 225, 71, 142, 63, 199, 27, 214, 62, 4, 7, 215, + 119, 29, 204, 176, 115, 44, 72, 45, 87, 253, 79, 104, 116, 220, 180, 99, 147, 32, 4, 209, 23, + 205, 180, 18, 18, 5, 133, 190, 98, 214, 252, 241, 64, 183, 234, 66, 9, 110, 133, 133, 252, 73, + 122, 227, 44, 221, 238, 127, 5, 190, 32, 0, 205, 182, 86, 228, 119, 212, 109, 157, 248, 48, + 151, 3, 27, 143, 210, 167, 197, 226, 35, 215, 173, 105, 126, 98, 90, 144, 169, 200, 216, 4, 27, + 32, 7, 182, 153, 192, 164, 94, 213, 7, 81, 178, 161, 104, 1, 253, 131, 118, 69, 226, 98, 112, + 103, 138, 99, 200, 16, 8, 213, 118, 146, 174, 172, 238, 32, 3, 138, 128, 166, 158, 66, 88, 106, + 238, 115, 17, 236, 1, 17, 18, 205, 217, 56, 172, 81, 37, 107, 16, 246, 41, 64, 227, 56, 191, + 103, 249, 87, 32, 5, 197, 64, 83, 79, 33, 44, 61, 247, 57, 136, 246, 0, 136, 137, 102, 236, + 156, 86, 40, 146, 181, 136, 123, 20, 160, 113, 156, 95, 179, 252, 172, 32, 6, 97, 73, 120, 25, + 217, 174, 109, 140, 223, 119, 82, 47, 54, 201, 127, 136, 196, 162, 77, 83, 117, 2, 151, 62, 68, + 58, 246, 118, 115, 38, 101, 32, 2, 157, 38, 116, 98, 175, 2, 6, 138, 30, 94, 136, 19, 148, 154, + 76, 125, 244, 11, 40, 93, 81, 83, 119, 116, 203, 167, 224, 50, 191, 159, 159, 32, 0, 175, 156, + 11, 75, 255, 239, 88, 118, 72, 86, 193, 185, 17, 52, 234, 231, 153, 47, 173, 77, 247, 77, 216, + 57, 191, 20, 5, 200, 213, 74, 117, 32, 0, 62, 153, 65, 208, 118, 182, 23, 174, 164, 155, 159, + 232, 5, 38, 51, 63, 181, 127, 35, 243, 123, 219, 125, 192, 209, 221, 203, 76, 82, 98, 86, 32, + 5, 22, 232, 170, 86, 80, 89, 25, 218, 109, 61, 10, 143, 104, 0, 127, 160, 66, 115, 104, 106, + 128, 168, 41, 111, 29, 36, 98, 131, 0, 67, 246, 32, 1, 206, 169, 179, 80, 23, 138, 158, 196, + 38, 80, 29, 56, 215, 173, 212, 221, 84, 28, 18, 145, 203, 40, 27, 75, 188, 125, 92, 200, 191, + 96, 148, 32, 4, 101, 254, 40, 26, 84, 221, 157, 243, 85, 218, 229, 203, 94, 91, 182, 129, 32, + 133, 66, 82, 255, 210, 103, 89, 210, 64, 214, 170, 248, 216, 89, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 93, 83, 13, 39, + 225, 51, 125, 213, 246, 158, 232, 243, 193, 188, 211, 178, 58, 244, 128, 89, 225, 155, 190, 28, + 228, 120, 50, 132, 29, 234, 244, 32, 7, 219, 227, 185, 248, 229, 236, 137, 91, 50, 171, 67, 42, + 191, 128, 161, 154, 154, 107, 199, 194, 156, 33, 2, 143, 255, 67, 51, 24, 104, 133, 14, 32, 5, + 146, 19, 155, 160, 141, 85, 232, 35, 7, 21, 242, 138, 101, 227, 115, 172, 245, 197, 2, 96, 173, + 89, 196, 94, 161, 149, 172, 204, 213, 77, 241, 32, 7, 249, 80, 4, 89, 1, 199, 167, 27, 139, 62, + 219, 86, 89, 72, 123, 115, 154, 224, 30, 134, 235, 116, 44, 8, 144, 129, 32, 105, 174, 180, 51, + 32, 3, 109, 79, 177, 19, 59, 134, 178, 84, 88, 166, 192, 234, 24, 132, 50, 9, 52, 190, 175, + 103, 0, 114, 130, 129, 252, 85, 105, 80, 13, 215, 44, 32, 0, 65, 132, 220, 157, 57, 141, 161, + 237, 95, 246, 72, 187, 213, 187, 214, 26, 96, 82, 179, 52, 229, 229, 37, 214, 245, 159, 218, + 49, 194, 193, 152, 32, 3, 183, 108, 42, 159, 80, 215, 118, 17, 80, 122, 97, 61, 97, 29, 254, + 39, 135, 228, 135, 94, 107, 106, 221, 63, 255, 38, 138, 6, 196, 241, 139, 32, 4, 138, 187, 53, + 147, 230, 88, 117, 192, 126, 19, 155, 155, 156, 127, 50, 196, 43, 177, 64, 239, 117, 169, 171, + 254, 53, 246, 1, 9, 12, 34, 196, 32, 4, 160, 6, 194, 185, 109, 38, 218, 213, 228, 88, 0, 31, + 246, 241, 176, 177, 119, 122, 108, 5, 184, 189, 59, 198, 144, 139, 221, 103, 10, 165, 2, 32, 2, + 111, 100, 250, 248, 222, 100, 64, 48, 166, 146, 122, 2, 8, 68, 252, 92, 143, 51, 116, 45, 110, + 98, 100, 44, 170, 181, 38, 109, 65, 195, 204, 32, 1, 20, 223, 54, 185, 95, 217, 122, 110, 8, + 220, 187, 76, 74, 65, 22, 142, 193, 220, 144, 118, 178, 193, 68, 147, 34, 251, 2, 23, 86, 59, + 84, 32, 4, 11, 211, 18, 40, 27, 51, 227, 51, 84, 194, 0, 1, 238, 114, 180, 172, 52, 106, 108, + 246, 211, 240, 67, 31, 136, 195, 86, 130, 203, 152, 13, 32, 3, 59, 216, 203, 252, 156, 121, 46, + 5, 129, 98, 56, 105, 47, 215, 70, 255, 253, 194, 22, 92, 185, 162, 24, 189, 240, 19, 167, 129, + 51, 118, 15, 32, 6, 218, 40, 8, 230, 94, 223, 24, 55, 176, 253, 83, 23, 76, 163, 40, 55, 166, + 131, 30, 122, 192, 213, 155, 114, 135, 8, 36, 157, 222, 29, 33, 32, 7, 158, 22, 33, 155, 10, + 170, 45, 59, 39, 103, 96, 105, 160, 76, 244, 221, 24, 125, 27, 163, 37, 121, 209, 200, 87, 71, + 74, 128, 187, 192, 77, 32, 1, 233, 235, 60, 224, 219, 201, 97, 98, 250, 177, 160, 110, 194, 21, + 88, 29, 170, 204, 12, 223, 187, 86, 195, 43, 127, 188, 63, 33, 176, 119, 155, 32, 7, 144, 93, + 56, 141, 120, 249, 210, 19, 205, 141, 91, 105, 178, 112, 63, 196, 79, 204, 39, 17, 230, 127, 8, + 93, 82, 101, 30, 208, 100, 221, 55, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 97, 32, 0, 15, 252, 208, 1, 16, 6, 197, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 223, 15, 255, 208, 0, 16, 0, 102, 32, 7, 255, 255, 255, - 255, 255, 241, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 33, 32, 2, 47, 252, 208, 1, 17, 69, 65, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 255, 47, 255, 208, 0, 16, 19, - 34, 32, 7, 255, 255, 255, 255, 255, 230, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 129, 32, 7, 255, 255, 255, - 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 97, 32, 7, 255, 255, 255, 255, 255, 200, 208, 255, 255, + 255, 239, 255, 225, 36, 32, 4, 198, 99, 47, 102, 150, 146, 77, 205, 39, 228, 73, 208, 170, 131, + 91, 229, 149, 154, 108, 150, 155, 148, 154, 250, 98, 98, 240, 61, 154, 247, 24, 32, 4, 94, 5, + 186, 86, 10, 188, 71, 33, 154, 51, 118, 173, 32, 170, 225, 114, 150, 144, 214, 197, 235, 56, + 211, 75, 119, 174, 112, 233, 134, 174, 246, 32, 6, 214, 144, 50, 68, 104, 213, 16, 190, 137, + 200, 55, 159, 110, 39, 163, 41, 71, 7, 35, 94, 3, 92, 153, 218, 69, 52, 27, 30, 199, 213, 181, + 32, 0, 116, 127, 190, 3, 87, 233, 226, 70, 40, 211, 143, 245, 140, 24, 29, 26, 136, 122, 149, + 187, 102, 54, 132, 255, 0, 112, 16, 195, 254, 74, 70, 32, 3, 133, 116, 230, 181, 99, 254, 244, + 122, 42, 252, 158, 208, 173, 141, 214, 205, 60, 187, 213, 75, 112, 84, 154, 121, 184, 79, 230, + 137, 204, 151, 160, 32, 5, 110, 77, 198, 197, 214, 119, 53, 127, 26, 252, 4, 108, 154, 64, 215, + 160, 192, 232, 92, 43, 150, 104, 189, 48, 198, 58, 17, 232, 126, 121, 248, 32, 0, 96, 162, 110, + 38, 151, 101, 178, 95, 20, 2, 138, 205, 173, 58, 137, 128, 172, 7, 37, 18, 32, 29, 212, 234, + 147, 6, 25, 14, 62, 61, 42, 32, 0, 48, 81, 55, 19, 75, 178, 217, 47, 138, 1, 69, 102, 214, 157, + 68, 192, 86, 3, 146, 137, 16, 14, 234, 117, 73, 131, 12, 135, 31, 30, 149, 32, 0, 70, 215, 101, + 166, 112, 230, 215, 64, 31, 158, 62, 130, 47, 190, 212, 203, 151, 94, 13, 91, 191, 102, 229, + 251, 161, 134, 218, 86, 116, 251, 118, 32, 7, 148, 42, 133, 153, 156, 222, 114, 121, 161, 47, + 246, 4, 104, 253, 82, 143, 222, 175, 136, 17, 84, 9, 185, 73, 24, 157, 55, 77, 203, 206, 170, + 32, 6, 95, 234, 173, 56, 114, 7, 142, 82, 41, 132, 108, 170, 143, 82, 105, 193, 167, 44, 204, + 178, 1, 244, 42, 127, 140, 103, 23, 202, 169, 243, 42, 32, 2, 77, 249, 28, 214, 193, 240, 93, + 31, 25, 106, 39, 150, 196, 222, 90, 225, 108, 105, 128, 180, 141, 3, 101, 242, 62, 156, 7, 172, + 206, 229, 231, 32, 3, 46, 149, 238, 10, 122, 151, 202, 36, 91, 125, 53, 54, 22, 155, 163, 176, + 198, 51, 163, 149, 187, 117, 74, 51, 8, 165, 38, 14, 24, 245, 40, 32, 4, 239, 189, 161, 235, + 217, 214, 24, 228, 113, 16, 30, 82, 45, 123, 159, 104, 103, 91, 25, 207, 207, 250, 116, 228, + 250, 245, 176, 93, 7, 252, 107, 32, 2, 166, 141, 155, 18, 183, 248, 119, 26, 147, 37, 170, 247, + 219, 46, 2, 31, 160, 9, 208, 255, 31, 92, 171, 51, 122, 64, 44, 65, 105, 106, 97, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 2, + 156, 46, 34, 115, 162, 75, 166, 181, 116, 156, 220, 60, 49, 126, 243, 206, 46, 132, 61, 180, + 42, 193, 57, 105, 171, 49, 92, 216, 164, 255, 63, 32, 2, 111, 35, 213, 86, 255, 227, 39, 70, + 26, 138, 73, 140, 112, 19, 17, 180, 33, 56, 2, 82, 70, 96, 99, 52, 178, 58, 41, 184, 87, 179, + 113, 32, 3, 147, 237, 53, 138, 141, 166, 16, 37, 177, 164, 176, 191, 57, 129, 184, 47, 249, + 237, 203, 85, 179, 192, 68, 69, 59, 199, 14, 83, 181, 238, 61, 32, 1, 118, 29, 91, 8, 184, 237, + 60, 139, 126, 142, 141, 59, 114, 71, 146, 78, 93, 78, 242, 123, 222, 219, 252, 43, 225, 140, + 28, 230, 172, 188, 58, 32, 1, 84, 130, 32, 213, 78, 79, 117, 206, 162, 1, 8, 224, 147, 200, + 238, 60, 27, 30, 226, 8, 3, 71, 210, 50, 243, 97, 91, 91, 212, 126, 174, 32, 0, 224, 32, 20, + 176, 200, 99, 47, 72, 141, 39, 151, 121, 176, 140, 240, 31, 227, 173, 200, 71, 167, 168, 90, + 97, 117, 3, 120, 21, 185, 211, 224, 32, 1, 37, 68, 6, 241, 202, 81, 117, 47, 144, 18, 225, 165, + 193, 17, 181, 203, 96, 21, 248, 79, 44, 199, 18, 158, 228, 197, 98, 156, 192, 94, 80, 32, 5, + 185, 214, 73, 108, 240, 134, 9, 148, 89, 43, 205, 61, 124, 125, 98, 76, 85, 5, 182, 185, 175, + 212, 9, 48, 201, 89, 45, 183, 104, 130, 89, 32, 4, 230, 12, 106, 169, 13, 106, 222, 118, 104, + 250, 215, 212, 230, 34, 131, 8, 142, 71, 207, 35, 197, 114, 84, 3, 18, 90, 18, 92, 190, 198, + 231, 32, 6, 129, 116, 116, 248, 215, 104, 164, 63, 123, 158, 128, 206, 108, 34, 113, 238, 69, + 104, 59, 68, 244, 166, 220, 58, 209, 172, 56, 49, 246, 59, 251, 32, 5, 86, 230, 229, 219, 163, + 126, 239, 151, 183, 79, 219, 168, 206, 75, 82, 196, 130, 131, 63, 170, 158, 100, 118, 233, 237, + 114, 173, 176, 238, 108, 217, 32, 7, 244, 155, 40, 219, 168, 42, 187, 215, 244, 185, 179, 251, + 71, 122, 223, 29, 143, 140, 84, 83, 0, 21, 74, 227, 57, 248, 25, 153, 112, 255, 181, 32, 7, + 207, 113, 72, 180, 24, 231, 110, 224, 195, 157, 3, 64, 86, 79, 206, 101, 110, 184, 221, 7, 149, + 11, 254, 197, 182, 100, 80, 245, 58, 63, 147, 32, 5, 221, 109, 132, 218, 56, 223, 127, 0, 226, + 252, 251, 156, 5, 76, 45, 87, 250, 6, 120, 225, 77, 119, 106, 247, 192, 187, 108, 242, 39, 4, + 167, 32, 5, 115, 229, 56, 54, 6, 240, 24, 242, 136, 101, 92, 116, 84, 44, 239, 43, 133, 203, + 167, 3, 81, 139, 9, 192, 77, 237, 242, 251, 5, 68, 235, 32, 4, 114, 254, 69, 61, 206, 97, 32, + 57, 35, 33, 228, 65, 18, 161, 248, 118, 151, 197, 161, 100, 84, 220, 9, 162, 222, 214, 19, 5, + 249, 5, 5, 32, 6, 72, 194, 21, 30, 130, 163, 152, 41, 83, 40, 242, 19, 88, 251, 153, 240, 87, + 52, 164, 156, 51, 138, 64, 89, 145, 227, 141, 228, 120, 119, 186, 32, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, + 255, 254, 239, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 239, 255, 225, 1, 8, 198, 23, 84, 255, 151, 170, 24, 10, + 205, 81, 45, 252, 78, 227, 104, 43, 34, 71, 48, 21, 221, 59, 83, 55, 230, 173, 205, 23, 165, + 63, 164, 81, 214, 5, 201, 144, 116, 141, 237, 183, 99, 166, 133, 98, 237, 150, 237, 117, 64, + 62, 158, 103, 219, 38, 180, 159, 216, 51, 118, 132, 190, 36, 174, 82, 184, 132, 162, 205, 221, + 202, 214, 145, 217, 167, 46, 204, 125, 71, 164, 57, 104, 83, 60, 227, 147, 221, 50, 19, 127, + 168, 46, 215, 74, 229, 109, 113, 208, 168, 36, 249, 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, + 186, 134, 137, 48, 43, 33, 53, 149, 217, 125, 46, 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, + 144, 161, 214, 44, 14, 190, 143, 160, 9, 57, 35, 119, 113, 194, 148, 35, 100, 168, 241, 151, + 40, 203, 46, 56, 120, 109, 133, 0, 249, 165, 107, 85, 164, 80, 243, 242, 36, 235, 226, 199, + 191, 219, 14, 217, 84, 59, 78, 176, 165, 80, 100, 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, + 160, 180, 121, 49, 249, 172, 16, 189, 110, 38, 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, + 172, 17, 108, 168, 101, 136, 252, 118, 147, 242, 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, + 12, 53, 221, 137, 122, 171, 78, 203, 75, 123, 4, 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, + 190, 94, 157, 8, 170, 178, 81, 208, 81, 111, 31, 28, 126, 253, 70, 188, 190, 185, 39, 217, 50, + 111, 70, 67, 107, 101, 179, 74, 138, 102, 107, 71, 148, 178, 73, 169, 214, 5, 201, 144, 116, + 141, 237, 183, 99, 166, 133, 98, 237, 150, 237, 117, 64, 62, 158, 103, 219, 38, 180, 159, 216, + 51, 118, 132, 190, 36, 174, 82, 184, 132, 162, 205, 221, 202, 214, 145, 217, 167, 46, 204, 125, + 71, 164, 57, 104, 83, 60, 227, 147, 221, 50, 19, 127, 168, 46, 215, 74, 229, 109, 113, 208, + 168, 36, 249, 140, 75, 236, 88, 39, 14, 52, 42, 174, 36, 186, 134, 137, 48, 43, 33, 53, 149, + 217, 125, 46, 95, 174, 91, 171, 36, 50, 83, 66, 229, 202, 144, 161, 214, 44, 14, 190, 143, 160, + 9, 57, 35, 119, 113, 194, 148, 35, 100, 168, 241, 151, 40, 203, 46, 56, 120, 109, 133, 0, 249, + 165, 107, 85, 164, 80, 243, 242, 36, 235, 226, 199, 191, 219, 14, 217, 84, 59, 78, 176, 165, + 80, 100, 218, 54, 140, 125, 197, 249, 99, 247, 17, 71, 160, 180, 121, 49, 249, 172, 16, 189, + 110, 38, 119, 42, 116, 95, 138, 195, 26, 201, 251, 87, 172, 17, 108, 168, 101, 136, 252, 118, + 147, 242, 33, 11, 16, 200, 158, 124, 254, 165, 5, 73, 12, 53, 221, 137, 122, 171, 78, 203, 75, + 123, 4, 181, 50, 42, 37, 32, 13, 234, 176, 114, 190, 190, 94, 157, 23, 32, 1, 226, 90, 177, 34, + 62, 201, 180, 249, 9, 215, 203, 139, 66, 88, 169, 207, 196, 116, 47, 36, 127, 53, 131, 194, 0, + 71, 133, 68, 125, 203, 156, 32, 5, 84, 147, 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, 206, + 188, 12, 47, 68, 76, 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, 23, 95, 32, 5, 84, 147, + 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, 206, 188, 12, 47, 68, 76, 47, 63, 17, 175, 206, + 61, 111, 0, 64, 60, 146, 23, 95, 32, 5, 84, 147, 243, 72, 44, 231, 137, 95, 82, 224, 204, 154, + 206, 188, 12, 47, 68, 76, 47, 63, 17, 175, 206, 61, 111, 0, 64, 60, 146, 23, 95, 32, 3, 90, + 112, 38, 248, 174, 225, 80, 47, 99, 208, 220, 166, 201, 154, 71, 62, 141, 42, 104, 172, 207, + 250, 140, 201, 249, 74, 88, 26, 166, 127, 212, 32, 5, 134, 245, 233, 202, 185, 178, 113, 74, + 59, 208, 205, 65, 8, 236, 118, 243, 40, 83, 202, 162, 159, 83, 0, 191, 255, 58, 82, 220, 255, + 23, 29, 32, 6, 227, 171, 139, 219, 139, 164, 90, 41, 98, 199, 188, 177, 9, 186, 202, 145, 64, + 5, 126, 143, 190, 146, 169, 7, 143, 169, 218, 35, 68, 235, 128, 32, 0, 130, 168, 205, 202, 184, + 230, 169, 214, 27, 165, 47, 104, 18, 54, 173, 167, 144, 26, 11, 42, 182, 168, 131, 255, 193, + 120, 81, 92, 85, 21, 9, 32, 0, 198, 81, 52, 192, 76, 178, 36, 188, 172, 159, 24, 73, 243, 74, + 123, 37, 10, 41, 255, 32, 109, 116, 92, 255, 49, 76, 67, 28, 250, 217, 188, 32, 1, 236, 236, + 188, 57, 240, 250, 49, 136, 157, 51, 197, 131, 190, 82, 109, 93, 176, 233, 189, 132, 124, 15, + 150, 83, 200, 212, 101, 116, 234, 125, 71, 32, 2, 65, 199, 238, 237, 253, 238, 0, 191, 87, 58, + 174, 152, 1, 151, 187, 90, 87, 125, 195, 41, 103, 49, 189, 18, 125, 62, 190, 24, 62, 181, 83, + 32, 4, 198, 36, 193, 246, 195, 182, 251, 63, 57, 13, 176, 164, 25, 122, 253, 188, 150, 158, + 106, 136, 241, 239, 75, 249, 173, 62, 56, 58, 118, 253, 98, 32, 7, 152, 239, 4, 75, 94, 140, + 52, 205, 198, 243, 9, 254, 146, 160, 50, 36, 61, 193, 169, 85, 180, 218, 212, 66, 18, 232, 128, + 75, 217, 49, 104, 32, 4, 150, 204, 12, 168, 216, 22, 200, 19, 209, 117, 116, 14, 1, 6, 73, 199, + 148, 170, 40, 201, 65, 239, 108, 97, 54, 161, 208, 73, 96, 40, 77, 32, 0, 89, 1, 107, 47, 187, + 250, 147, 133, 134, 49, 97, 130, 198, 199, 203, 218, 83, 147, 96, 23, 159, 218, 246, 198, 194, + 81, 13, 28, 75, 53, 47, 32, 1, 28, 219, 187, 19, 102, 9, 144, 68, 190, 184, 76, 169, 190, 127, + 131, 71, 71, 99, 157, 255, 174, 167, 193, 86, 169, 29, 171, 160, 168, 87, 20, 32, 4, 220, 160, + 34, 62, 11, 188, 248, 175, 118, 70, 24, 125, 79, 86, 37, 114, 155, 49, 100, 78, 8, 48, 75, 159, + 236, 21, 197, 193, 244, 118, 250, 32, 3, 55, 131, 240, 131, 7, 226, 70, 14, 219, 44, 30, 186, + 18, 176, 152, 179, 224, 211, 175, 71, 201, 115, 172, 16, 8, 196, 103, 206, 249, 150, 82, 32, 1, + 77, 168, 185, 25, 164, 38, 14, 193, 137, 113, 154, 54, 217, 60, 156, 38, 138, 122, 105, 254, + 17, 102, 164, 221, 43, 37, 170, 221, 72, 232, 151, 32, 3, 37, 118, 187, 49, 235, 31, 41, 121, + 145, 157, 107, 21, 174, 81, 106, 253, 208, 11, 111, 44, 169, 123, 199, 86, 247, 34, 146, 159, + 78, 78, 134, 32, 6, 25, 172, 62, 68, 76, 1, 151, 154, 104, 86, 6, 130, 165, 106, 33, 176, 185, + 58, 216, 16, 206, 236, 152, 171, 90, 91, 42, 229, 248, 62, 237, 32, 4, 6, 28, 244, 157, 110, + 212, 16, 249, 146, 113, 14, 89, 246, 188, 124, 152, 104, 62, 116, 143, 17, 229, 77, 42, 21, 54, + 36, 127, 143, 46, 180, 32, 6, 213, 30, 201, 15, 161, 166, 224, 201, 122, 48, 53, 149, 96, 216, + 206, 129, 31, 134, 225, 26, 111, 0, 127, 54, 100, 16, 33, 24, 239, 169, 118, 23, 32, 6, 5, 152, + 2, 28, 99, 252, 217, 4, 193, 210, 48, 97, 240, 47, 63, 19, 117, 140, 245, 109, 151, 204, 220, + 129, 39, 253, 49, 242, 168, 120, 151, 32, 5, 174, 60, 34, 214, 147, 223, 113, 120, 155, 177, + 195, 231, 225, 156, 129, 231, 208, 52, 152, 129, 190, 176, 115, 224, 38, 54, 197, 65, 184, 142, + 232, 32, 5, 174, 60, 34, 214, 147, 223, 113, 120, 155, 177, 195, 231, 225, 156, 129, 231, 208, + 52, 152, 129, 190, 176, 115, 224, 38, 54, 197, 65, 184, 142, 232, 32, 5, 174, 60, 34, 214, 147, + 223, 113, 120, 155, 177, 195, 231, 225, 156, 129, 231, 208, 52, 152, 129, 190, 176, 115, 224, + 38, 54, 197, 65, 184, 142, 232, 32, 7, 121, 221, 240, 129, 105, 88, 243, 253, 201, 60, 90, 139, + 168, 81, 179, 130, 76, 9, 95, 160, 104, 0, 244, 217, 146, 66, 101, 158, 81, 168, 54, 32, 3, + 213, 73, 227, 177, 207, 252, 173, 28, 118, 100, 147, 55, 66, 251, 107, 88, 24, 5, 238, 106, 72, + 82, 218, 80, 172, 99, 216, 235, 159, 151, 50, 32, 7, 166, 82, 123, 107, 43, 164, 75, 210, 180, + 109, 36, 0, 35, 152, 37, 8, 141, 249, 252, 44, 138, 67, 152, 2, 224, 75, 38, 106, 110, 253, + 236, 32, 4, 41, 74, 94, 251, 100, 113, 67, 170, 6, 100, 54, 157, 118, 217, 101, 75, 145, 69, + 14, 165, 142, 150, 236, 107, 5, 130, 59, 182, 189, 116, 1, 32, 7, 53, 87, 13, 68, 248, 17, 250, + 175, 182, 241, 183, 220, 59, 172, 179, 118, 165, 111, 58, 136, 7, 230, 99, 169, 178, 182, 245, + 120, 169, 158, 50, 32, 2, 86, 206, 226, 81, 43, 2, 142, 153, 222, 85, 106, 197, 123, 159, 24, + 79, 55, 128, 92, 124, 80, 81, 214, 171, 62, 227, 253, 77, 143, 102, 42, 32, 0, 71, 65, 188, + 141, 114, 57, 192, 29, 15, 199, 3, 116, 181, 84, 185, 134, 76, 5, 81, 13, 253, 171, 180, 121, + 221, 223, 175, 88, 166, 11, 196, 32, 6, 57, 225, 140, 207, 235, 77, 232, 93, 0, 172, 133, 213, + 23, 104, 96, 146, 118, 242, 175, 33, 244, 78, 83, 99, 173, 165, 213, 204, 30, 253, 95, 32, 4, + 83, 171, 84, 96, 90, 141, 133, 240, 109, 239, 84, 166, 217, 236, 205, 142, 191, 73, 197, 45, + 188, 207, 222, 60, 225, 187, 184, 238, 102, 84, 103, 32, 0, 248, 88, 98, 94, 207, 141, 75, 237, + 61, 234, 162, 40, 176, 186, 22, 52, 93, 138, 100, 151, 157, 182, 64, 247, 146, 176, 157, 203, + 35, 208, 185, 32, 6, 178, 43, 190, 231, 177, 156, 59, 89, 32, 7, 140, 237, 213, 218, 119, 18, + 186, 119, 179, 205, 10, 76, 209, 60, 72, 173, 79, 129, 116, 34, 212, 32, 7, 232, 242, 81, 73, + 173, 219, 223, 87, 6, 238, 88, 194, 11, 217, 198, 236, 17, 49, 50, 99, 140, 203, 66, 62, 38, + 52, 62, 34, 39, 73, 208, 32, 7, 164, 100, 246, 243, 192, 242, 11, 3, 187, 235, 185, 48, 141, + 142, 105, 234, 220, 67, 207, 112, 68, 78, 196, 178, 224, 209, 7, 183, 12, 184, 178, 32, 7, 134, + 217, 46, 19, 216, 242, 5, 148, 52, 225, 134, 144, 179, 143, 210, 3, 36, 53, 103, 68, 180, 1, + 170, 39, 27, 89, 253, 40, 180, 186, 55, 32, 2, 51, 50, 203, 226, 167, 22, 7, 220, 184, 62, 60, + 166, 91, 148, 101, 64, 96, 252, 158, 211, 27, 103, 227, 108, 209, 86, 179, 120, 142, 108, 125, + 32, 0, 214, 90, 141, 11, 83, 126, 234, 160, 200, 182, 133, 217, 156, 187, 122, 199, 33, 63, + 147, 194, 184, 189, 155, 189, 176, 50, 183, 77, 239, 118, 65, 32, 2, 220, 62, 108, 245, 46, + 101, 64, 164, 180, 143, 208, 135, 122, 178, 73, 13, 227, 145, 18, 129, 86, 111, 249, 131, 202, + 162, 107, 222, 148, 145, 90, 32, 0, 220, 193, 44, 213, 8, 251, 223, 112, 251, 65, 71, 90, 159, + 20, 177, 98, 21, 184, 150, 66, 54, 11, 248, 231, 48, 118, 114, 29, 66, 143, 26, 32, 4, 89, 205, + 230, 246, 77, 248, 106, 59, 250, 124, 66, 250, 146, 104, 183, 83, 64, 79, 182, 76, 27, 7, 149, + 119, 76, 145, 118, 129, 21, 32, 98, 1, 1, 32, 7, 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 252, 193, 32, 6, 47, 252, 208, 5, 81, 69, 218, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 246, 251, 47, 255, 208, 0, 80, 19, 43, 32, 7, 255, 255, 255, - 255, 255, 207, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 253, 33, 32, 5, 176, 7, 112, 9, 145, 69, 235, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 250, 176, 0, 112, 0, 144, 19, - 44, 32, 7, 255, 255, 255, 255, 255, 192, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 65, 32, 7, 144, 1, 16, 1, 16, - 151, 69, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 238, - 144, 0, 16, 0, 16, 8, 230, 32, 7, 255, 255, 255, 255, 255, 234, 208, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 193, - 32, 7, 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 7, 255, 255, 255, - 255, 255, 222, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 254, 1, 32, 7, 255, 255, 255, 255, 255, 228, 112, 255, 255, + 255, 255, 225, 32, 7, 255, 255, 255, 255, 255, 185, 240, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 225, 32, 7, + 255, 255, 255, 255, 255, 185, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 225, 32, 7, 255, 255, 255, 255, 255, + 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 97, 32, 7, 255, 255, 255, 255, 255, 103, 16, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 247, + 1, 1, 251, 251, 127, 1, 251, 1, 128, 2, 2, 1, 5, 3, 33, 72, 32, 32, 7, 255, 255, 255, 255, 255, + 190, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 252, 33, 32, 7, 255, 255, 255, 255, 255, 185, 240, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, + 225, 32, 7, 255, 255, 255, 255, 255, 239, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 32, 7, 255, 255, 255, 255, + 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 225, 32, 7, 255, 255, 255, 255, 255, 213, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 254, 97, 32, 7, 255, 255, 255, 255, 255, 224, 48, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 33, 32, 2, 47, 252, - 208, 1, 17, 35, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 247, 255, 47, 255, 208, 0, 16, 17, 34, 32, 7, 255, 255, 255, 255, 255, 247, 144, 255, 255, 255, + 255, 253, 129, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, + 255, 194, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 252, 97, 32, 0, 0, 0, 0, 0, 0, 21, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 64, 32, 7, 255, 255, 255, 255, 255, 249, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 129, 32, 7, 255, 255, 255, 255, 255, 247, 144, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 129, 32, 7, 255, - 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 0, 15, 252, 208, 1, 16, 20, 81, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 15, 255, 208, - 0, 16, 1, 50, 32, 7, 255, 255, 255, 255, 255, 243, 80, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 65, 32, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, - 255, 255, 255, 255, 232, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 161, 32, 2, 47, 252, 208, 1, 17, 69, 65, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 255, 47, 255, 208, - 0, 16, 19, 34, 32, 7, 255, 255, 255, 255, 255, 205, 16, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 1, 32, 1, 176, 7, - 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, 255, 211, 112, 255, 255, 255, + 255, 255, 255, 161, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, 134, 32, 7, 255, + 255, 255, 255, 255, 198, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 161, 32, 0, 0, 0, 0, 0, 0, 2, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 7, 255, 255, 255, 255, 255, + 196, 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 252, 129, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, + 134, 32, 7, 255, 255, 255, 255, 255, 219, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 225, 32, 7, 144, 1, 16, + 1, 16, 151, 69, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, + 238, 144, 0, 16, 0, 16, 8, 230, 32, 7, 255, 255, 255, 255, 255, 202, 240, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, + 225, 32, 0, 176, 3, 48, 1, 17, 69, 116, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 246, 253, 176, 0, 48, 0, 16, 19, 37, 32, 7, 255, 255, 255, 255, 255, 241, + 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 33, 32, 2, 47, 252, 208, 1, 17, 69, 65, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 255, 47, 255, 208, 0, 16, 19, 34, 32, 7, 255, + 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 32, 0, 15, 252, 208, 1, 16, 6, 197, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 223, 15, 255, 208, + 0, 16, 0, 102, 32, 7, 255, 255, 255, 255, 255, 230, 144, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 129, 32, 7, + 255, 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 32, 7, 255, 255, 255, 255, 255, 200, + 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 252, 193, 32, 6, 47, 252, 208, 5, 81, 69, 218, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 251, 47, 255, 208, 0, 80, 19, 43, 32, 7, + 255, 255, 255, 255, 255, 207, 48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 33, 32, 5, 176, 7, 112, 9, 145, 69, 235, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 250, 176, 0, + 112, 0, 144, 19, 44, 32, 7, 255, 255, 255, 255, 255, 192, 80, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 65, 32, 7, + 144, 1, 16, 1, 16, 151, 69, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 251, 238, 144, 0, 16, 0, 16, 8, 230, 32, 7, 255, 255, 255, 255, 255, 234, 208, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 253, 97, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, 255, + 255, 255, 254, 193, 32, 7, 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, + 7, 255, 255, 255, 255, 255, 222, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 1, 32, 7, 255, 255, 255, 255, 255, 228, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 254, 97, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, - 134, 32, 7, 255, 255, 255, 255, 255, 236, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 225, 32, 2, 47, 252, 208, + 255, 255, 255, 255, 255, 254, 97, 32, 7, 255, 255, 255, 255, 255, 224, 48, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, + 33, 32, 2, 47, 252, 208, 1, 17, 35, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 247, 255, 47, 255, 208, 0, 16, 17, 34, 32, 7, 255, 255, 255, 255, 255, 247, + 144, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 129, 32, 7, 255, 255, 255, 255, 255, 247, 144, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 129, 32, 7, 255, 255, 255, 255, 255, 253, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 225, 32, 0, 15, 252, 208, + 1, 16, 20, 81, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 127, 15, 255, 208, 0, 16, 1, 50, 32, 7, 255, 255, 255, 255, 255, 243, 80, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 65, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 32, 7, 255, 255, 255, 255, 255, 232, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 161, 32, 2, 47, 252, 208, 1, 17, 69, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, + 255, 47, 255, 208, 0, 16, 19, 34, 32, 7, 255, 255, 255, 255, 255, 205, 16, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, + 1, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, 255, 255, 255, 255, 211, + 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 253, 97, 32, 1, 176, 7, 112, 1, 17, 69, 82, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 254, 176, 0, 112, 0, 16, 19, 35, 32, 7, 255, + 255, 255, 255, 255, 188, 16, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 252, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 7, 255, 255, 255, 255, 255, 228, + 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 254, 97, 32, 6, 111, 252, 207, 252, 208, 76, 229, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 223, 111, 255, 207, 255, 208, 4, 134, + 32, 7, 255, 255, 255, 255, 255, 236, 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 225, 32, 2, 47, 252, 208, 1, + 17, 69, 65, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 246, 255, 47, 255, 208, 0, 16, 19, 34, 32, 7, 255, 255, 255, 255, 255, 226, 80, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 65, 32, 7, 255, 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, @@ -1234,5 +1236,5 @@ static PROOF: [u8; 25396] = [ 234, 32, 7, 255, 255, 255, 255, 255, 215, 176, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 161, 32, 7, 255, 255, 255, 255, 255, 245, 112, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 64, 30, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 97, 64, ]; diff --git a/provers/groth16/src/qap.rs b/provers/groth16/src/qap.rs index 35bb8f76d5..d2433c4f5b 100644 --- a/provers/groth16/src/qap.rs +++ b/provers/groth16/src/qap.rs @@ -1,4 +1,4 @@ -use lambdaworks_math::{fft::polynomial::FFTPoly, polynomial::Polynomial}; +use lambdaworks_math::polynomial::Polynomial; use crate::common::*; @@ -56,9 +56,12 @@ impl QuadraticArithmeticProgram { let [l, r, o] = self.scale_and_accumulate_variable_polynomials(w, degree, offset); // TODO: Change to a vector of offsetted evaluations of x^N-1 - let mut t = (Polynomial::new_monomial(FrElement::one(), self.num_of_gates()) - - FrElement::one()) - .evaluate_offset_fft(1, Some(degree), offset) + let mut t = Polynomial::evaluate_offset_fft( + &(Polynomial::new_monomial(FrElement::one(), self.num_of_gates()) - FrElement::one()), + 1, + Some(degree), + offset, + ) .unwrap(); FrElement::inplace_batch_inverse(&mut t).unwrap(); @@ -91,7 +94,7 @@ impl QuadraticArithmeticProgram { fn build_variable_polynomials(from_matrix: &[Vec]) -> Vec> { from_matrix .iter() - .map(|row| Polynomial::interpolate_fft(row).unwrap()) + .map(|row| Polynomial::interpolate_fft::(row).unwrap()) .collect() } @@ -105,14 +108,20 @@ impl QuadraticArithmeticProgram { offset: &FrElement, ) -> [Vec; 3] { [&self.l, &self.r, &self.o].map(|var_polynomials| { - var_polynomials - .iter() - .zip(w) - .map(|(poly, coeff)| poly.mul_with_ref(&Polynomial::new_monomial(coeff.clone(), 0))) - .reduce(|poly1, poly2| poly1 + poly2) - .unwrap() - .evaluate_offset_fft(1, Some(degree), offset) - .unwrap() + Polynomial::evaluate_offset_fft( + &(var_polynomials + .iter() + .zip(w) + .map(|(poly, coeff)| { + poly.mul_with_ref(&Polynomial::new_monomial(coeff.clone(), 0)) + }) + .reduce(|poly1, poly2| poly1 + poly2) + .unwrap()), + 1, + Some(degree), + offset, + ) + .unwrap() }) } } diff --git a/provers/groth16/src/setup.rs b/provers/groth16/src/setup.rs index 4847d89221..f1611cc3cb 100644 --- a/provers/groth16/src/setup.rs +++ b/provers/groth16/src/setup.rs @@ -88,7 +88,7 @@ pub fn setup(qap: &QuadraticArithmeticProgram) -> (ProvingKey, VerifyingKey) { let alpha_g1 = g1.operate_with_self(tw.alpha.representative()); let beta_g2 = g2.operate_with_self(tw.beta.representative()); - let alpha_g1_times_beta_g2 = Pairing::compute(&alpha_g1, &beta_g2); + let alpha_g1_times_beta_g2 = Pairing::compute(&alpha_g1, &beta_g2).unwrap(); let delta_g2 = g2.operate_with_self(tw.delta.representative()); diff --git a/provers/groth16/src/verifier.rs b/provers/groth16/src/verifier.rs index e5619907fd..b83c4b215e 100644 --- a/provers/groth16/src/verifier.rs +++ b/provers/groth16/src/verifier.rs @@ -15,8 +15,8 @@ pub fn verify(vk: &VerifyingKey, proof: &Proof, pub_inputs: &[FrElement]) -> boo ) .unwrap(); - Pairing::compute(&proof.pi3, &vk.delta_g2) + Pairing::compute(&proof.pi3, &vk.delta_g2).unwrap() * vk.alpha_g1_times_beta_g2.clone() - * Pairing::compute(&k_tau_assigned_verifier_g1, &vk.gamma_g2) - == Pairing::compute(&proof.pi1, &proof.pi2) + * Pairing::compute(&k_tau_assigned_verifier_g1, &vk.gamma_g2).unwrap() + == Pairing::compute(&proof.pi1, &proof.pi2).unwrap() } diff --git a/provers/plonk/src/prover.rs b/provers/plonk/src/prover.rs index c8bee06666..77d4eb7f63 100644 --- a/provers/plonk/src/prover.rs +++ b/provers/plonk/src/prover.rs @@ -1,6 +1,5 @@ use lambdaworks_crypto::fiat_shamir::transcript::Transcript; use lambdaworks_math::errors::DeserializationError; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::traits::{Deserializable, IsRandomFieldElementGenerator, Serializable}; use std::marker::PhantomData; @@ -10,7 +9,10 @@ use crate::setup::{ new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey, Witness, }; use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; -use lambdaworks_math::{field::element::FieldElement, polynomial::Polynomial}; +use lambdaworks_math::{ + field::element::FieldElement, + polynomial::{self, Polynomial}, +}; use lambdaworks_math::{field::traits::IsField, traits::ByteConversion}; /// Plonk proof. @@ -311,15 +313,15 @@ where witness: &Witness, common_preprocessed_input: &CommonPreprocessedInput, ) -> Round1Result { - let p_a = Polynomial::interpolate_fft(&witness.a) + let p_a = Polynomial::interpolate_fft::(&witness.a) .expect("xs and ys have equal length and xs are unique"); - let p_b = Polynomial::interpolate_fft(&witness.b) + let p_b = Polynomial::interpolate_fft::(&witness.b) .expect("xs and ys have equal length and xs are unique"); - let p_c = Polynomial::interpolate_fft(&witness.c) + let p_c = Polynomial::interpolate_fft::(&witness.c) .expect("xs and ys have equal length and xs are unique"); let z_h = Polynomial::new_monomial(FieldElement::one(), common_preprocessed_input.n) - - FieldElement::one(); + - FieldElement::::one(); let p_a = self.blind_polynomial(&p_a, &z_h, 2); let p_b = self.blind_polynomial(&p_b, &z_h, 2); let p_c = self.blind_polynomial(&p_c, &z_h, 2); @@ -364,10 +366,10 @@ where coefficients.push(new_term); } - let p_z = Polynomial::interpolate_fft(&coefficients) + let p_z = Polynomial::interpolate_fft::(&coefficients) .expect("xs and ys have equal length and xs are unique"); let z_h = Polynomial::new_monomial(FieldElement::one(), common_preprocessed_input.n) - - FieldElement::one(); + - FieldElement::::one(); let p_z = self.blind_polynomial(&p_z, &z_h, 3); let z_1 = self.commitment_scheme.commit(&p_z); Round2Result { @@ -392,8 +394,8 @@ where let k2 = &cpi.k1 * &cpi.k1; let one = Polynomial::new_monomial(FieldElement::one(), 0); - let p_x = &Polynomial::new_monomial(FieldElement::one(), 1); - let zh = Polynomial::new_monomial(FieldElement::one(), cpi.n) - &one; + let p_x = &Polynomial::new_monomial(FieldElement::::one(), 1); + let zh = Polynomial::new_monomial(FieldElement::::one(), cpi.n) - &one; let z_x_omega_coefficients: Vec> = p_z .coefficients() @@ -402,13 +404,13 @@ where .map(|(i, x)| x * &cpi.domain[i % cpi.n]) .collect(); let z_x_omega = Polynomial::new(&z_x_omega_coefficients); - let mut e1 = vec![FieldElement::zero(); cpi.domain.len()]; + let mut e1 = vec![FieldElement::::zero(); cpi.domain.len()]; e1[0] = FieldElement::one(); - let l1 = Polynomial::interpolate_fft(&e1) + let l1 = Polynomial::interpolate_fft::(&e1) .expect("xs and ys have equal length and xs are unique"); let mut p_pi_y = public_input.to_vec(); p_pi_y.append(&mut vec![FieldElement::zero(); cpi.n - public_input.len()]); - let p_pi = Polynomial::interpolate_fft(&p_pi_y) + let p_pi = Polynomial::interpolate_fft::(&p_pi_y) .expect("xs and ys have equal length and xs are unique"); // Compute p @@ -417,24 +419,23 @@ where // TODO: check a factor of 4 is a sensible upper bound let degree = 4 * cpi.n; let offset = &cpi.k1; - let p_a_eval = p_a.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_b_eval = p_b.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_c_eval = p_c.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let ql_eval = cpi.ql.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let qr_eval = cpi.qr.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let qm_eval = cpi.qm.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let qo_eval = cpi.qo.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let qc_eval = cpi.qc.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_pi_eval = p_pi.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_x_eval = p_x.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_z_eval = p_z.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_z_x_omega_eval = z_x_omega - .evaluate_offset_fft(1, Some(degree), offset) - .unwrap(); - let p_s1_eval = cpi.s1.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_s2_eval = cpi.s2.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let p_s3_eval = cpi.s3.evaluate_offset_fft(1, Some(degree), offset).unwrap(); - let l1_eval = l1.evaluate_offset_fft(1, Some(degree), offset).unwrap(); + let p_a_eval = Polynomial::evaluate_offset_fft(p_a, 1, Some(degree), offset).unwrap(); + let p_b_eval = Polynomial::evaluate_offset_fft(p_b, 1, Some(degree), offset).unwrap(); + let p_c_eval = Polynomial::evaluate_offset_fft(p_c, 1, Some(degree), offset).unwrap(); + let ql_eval = Polynomial::evaluate_offset_fft(&cpi.ql, 1, Some(degree), offset).unwrap(); + let qr_eval = Polynomial::evaluate_offset_fft(&cpi.qr, 1, Some(degree), offset).unwrap(); + let qm_eval = Polynomial::evaluate_offset_fft(&cpi.qm, 1, Some(degree), offset).unwrap(); + let qo_eval = Polynomial::evaluate_offset_fft(&cpi.qo, 1, Some(degree), offset).unwrap(); + let qc_eval = Polynomial::evaluate_offset_fft(&cpi.qc, 1, Some(degree), offset).unwrap(); + let p_pi_eval = Polynomial::evaluate_offset_fft(&p_pi, 1, Some(degree), offset).unwrap(); + let p_x_eval = Polynomial::evaluate_offset_fft(p_x, 1, Some(degree), offset).unwrap(); + let p_z_eval = Polynomial::evaluate_offset_fft(p_z, 1, Some(degree), offset).unwrap(); + let p_z_x_omega_eval = + Polynomial::evaluate_offset_fft(&z_x_omega, 1, Some(degree), offset).unwrap(); + let p_s1_eval = Polynomial::evaluate_offset_fft(&cpi.s1, 1, Some(degree), offset).unwrap(); + let p_s2_eval = Polynomial::evaluate_offset_fft(&cpi.s2, 1, Some(degree), offset).unwrap(); + let p_s3_eval = Polynomial::evaluate_offset_fft(&cpi.s3, 1, Some(degree), offset).unwrap(); + let l1_eval = Polynomial::evaluate_offset_fft(&l1, 1, Some(degree), offset).unwrap(); let p_constraints_eval: Vec<_> = p_a_eval .iter() @@ -486,7 +487,7 @@ where let p_permutation_2_eval: Vec<_> = p_z_eval .iter() .zip(l1_eval.iter()) - .map(|(z, l)| (z - FieldElement::one()) * l) + .map(|(z, l)| (z - FieldElement::::one()) * l) .collect(); let p_eval: Vec<_> = p_permutation_2_eval @@ -496,7 +497,7 @@ where .map(|((p2, p1), co)| (p2 * &alpha + p1) * &alpha + co) .collect(); - let mut zh_eval = zh.evaluate_offset_fft(1, Some(degree), offset).unwrap(); + let mut zh_eval = Polynomial::evaluate_offset_fft(&zh, 1, Some(degree), offset).unwrap(); FieldElement::inplace_batch_inverse(&mut zh_eval).unwrap(); let c: Vec<_> = p_eval .iter() @@ -505,7 +506,7 @@ where .collect(); let mut t = Polynomial::interpolate_offset_fft(&c, offset).unwrap(); - Polynomial::pad_with_zero_coefficients_to_length(&mut t, 3 * (&cpi.n + 2)); + polynomial::pad_with_zero_coefficients_to_length(&mut t, 3 * (&cpi.n + 2)); let p_t_lo = Polynomial::new(&t.coefficients[..&cpi.n + 2]); let p_t_mid = Polynomial::new(&t.coefficients[&cpi.n + 2..2 * (&cpi.n + 2)]); let p_t_hi = Polynomial::new(&t.coefficients[2 * (&cpi.n + 2)..3 * (&cpi.n + 2)]); @@ -573,9 +574,9 @@ where let zeta_raised_n = Polynomial::new_monomial(r4.zeta.pow(cpi.n + 2), 0); // TODO: Paper says n and 2n, but Gnark uses n+2 and 2n+4 let zeta_raised_2n = Polynomial::new_monomial(r4.zeta.pow(2 * cpi.n + 4), 0); - let l1_zeta = (&r4.zeta.pow(cpi.n as u64) - FieldElement::one()) - / (&r4.zeta - FieldElement::one()) - / FieldElement::from(cpi.n as u64); + let l1_zeta = (&r4.zeta.pow(cpi.n as u64) - FieldElement::::one()) + / (&r4.zeta - FieldElement::::one()) + / FieldElement::::from(cpi.n as u64); let mut p_non_constant = &cpi.qm * &r4.a_zeta * &r4.b_zeta + &r4.a_zeta * &cpi.ql diff --git a/provers/plonk/src/setup.rs b/provers/plonk/src/setup.rs index 974c50e3ff..93d8fc41a5 100644 --- a/provers/plonk/src/setup.rs +++ b/provers/plonk/src/setup.rs @@ -5,7 +5,6 @@ use crate::test_utils::utils::{generate_domain, generate_permutation_coefficient use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; use lambdaworks_crypto::fiat_shamir::transcript::Transcript; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::field::{element::FieldElement, traits::IsField}; use lambdaworks_math::polynomial::Polynomial; @@ -86,14 +85,14 @@ impl CommonPreprocessedInput { n, omega, k1: order_r_minus_1_root_unity.clone(), - ql: Polynomial::interpolate_fft(&ql).unwrap(), // TODO: Remove unwraps - qr: Polynomial::interpolate_fft(&qr).unwrap(), - qo: Polynomial::interpolate_fft(&qo).unwrap(), - qm: Polynomial::interpolate_fft(&qm).unwrap(), - qc: Polynomial::interpolate_fft(&qc).unwrap(), - s1: Polynomial::interpolate_fft(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft(&s3_lagrange).unwrap(), + ql: Polynomial::interpolate_fft::(&ql).unwrap(), // TODO: Remove unwraps + qr: Polynomial::interpolate_fft::(&qr).unwrap(), + qo: Polynomial::interpolate_fft::(&qo).unwrap(), + qm: Polynomial::interpolate_fft::(&qm).unwrap(), + qc: Polynomial::interpolate_fft::(&qc).unwrap(), + s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), + s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), + s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), s1_lagrange, s2_lagrange, s3_lagrange, diff --git a/provers/plonk/src/test_utils/circuit_1.rs b/provers/plonk/src/test_utils/circuit_1.rs index bc95c89623..929f735839 100644 --- a/provers/plonk/src/test_utils/circuit_1.rs +++ b/provers/plonk/src/test_utils/circuit_1.rs @@ -2,7 +2,6 @@ use super::utils::{ generate_domain, generate_permutation_coefficients, ORDER_R_MINUS_1_ROOT_UNITY, }; use crate::setup::{CommonPreprocessedInput, Witness}; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::{ elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, field::{element::FieldElement, traits::IsFFTField}, @@ -40,7 +39,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { domain, k1: ORDER_R_MINUS_1_ROOT_UNITY, // domain: domain.clone(), - ql: Polynomial::interpolate_fft(&[ + ql: Polynomial::interpolate_fft::(&[ -FieldElement::one(), -FieldElement::one(), FieldElement::zero(), @@ -48,7 +47,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - qr: Polynomial::interpolate_fft(&[ + qr: Polynomial::interpolate_fft::(&[ FieldElement::zero(), FieldElement::zero(), FieldElement::zero(), @@ -56,7 +55,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - qo: Polynomial::interpolate_fft(&[ + qo: Polynomial::interpolate_fft::(&[ FieldElement::zero(), FieldElement::zero(), -FieldElement::one(), @@ -64,7 +63,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - qm: Polynomial::interpolate_fft(&[ + qm: Polynomial::interpolate_fft::(&[ FieldElement::zero(), FieldElement::zero(), FieldElement::one(), @@ -72,7 +71,7 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - qc: Polynomial::interpolate_fft(&[ + qc: Polynomial::interpolate_fft::(&[ FieldElement::from(0_u64), FieldElement::from(0_u64), FieldElement::zero(), @@ -80,9 +79,9 @@ pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { ]) .unwrap(), - s1: Polynomial::interpolate_fft(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft(&s3_lagrange).unwrap(), + s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), + s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), + s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), s1_lagrange, s2_lagrange, diff --git a/provers/plonk/src/test_utils/circuit_json.rs b/provers/plonk/src/test_utils/circuit_json.rs index 13aea759e8..43913c844c 100644 --- a/provers/plonk/src/test_utils/circuit_json.rs +++ b/provers/plonk/src/test_utils/circuit_json.rs @@ -2,7 +2,6 @@ use super::utils::{ generate_domain, generate_permutation_coefficients, ORDER_R_MINUS_1_ROOT_UNITY, }; use crate::setup::{CommonPreprocessedInput, Witness}; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::field::traits::IsFFTField; use lambdaworks_math::{ elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, @@ -62,19 +61,39 @@ pub fn common_preprocessed_input_from_json( domain, omega, k1: ORDER_R_MINUS_1_ROOT_UNITY, - ql: Polynomial::interpolate_fft(&process_vector(json_input.Ql, &FrElement::zero(), n)) - .unwrap(), - qr: Polynomial::interpolate_fft(&process_vector(json_input.Qr, &FrElement::zero(), n)) - .unwrap(), - qo: Polynomial::interpolate_fft(&process_vector(json_input.Qo, &FrElement::zero(), n)) - .unwrap(), - qm: Polynomial::interpolate_fft(&process_vector(json_input.Qm, &FrElement::zero(), n)) - .unwrap(), - qc: Polynomial::interpolate_fft(&process_vector(json_input.Qc, &FrElement::zero(), n)) - .unwrap(), - s1: Polynomial::interpolate_fft(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft(&s3_lagrange).unwrap(), + ql: Polynomial::interpolate_fft::(&process_vector( + json_input.Ql, + &FrElement::zero(), + n, + )) + .unwrap(), + qr: Polynomial::interpolate_fft::(&process_vector( + json_input.Qr, + &FrElement::zero(), + n, + )) + .unwrap(), + qo: Polynomial::interpolate_fft::(&process_vector( + json_input.Qo, + &FrElement::zero(), + n, + )) + .unwrap(), + qm: Polynomial::interpolate_fft::(&process_vector( + json_input.Qm, + &FrElement::zero(), + n, + )) + .unwrap(), + qc: Polynomial::interpolate_fft::(&process_vector( + json_input.Qc, + &FrElement::zero(), + n, + )) + .unwrap(), + s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), + s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), + s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), s1_lagrange, s2_lagrange, s3_lagrange, diff --git a/provers/plonk/src/verifier.rs b/provers/plonk/src/verifier.rs index 6da8e83d0d..8ee15c5747 100644 --- a/provers/plonk/src/verifier.rs +++ b/provers/plonk/src/verifier.rs @@ -76,13 +76,13 @@ impl> Verifier { { // TODO: First three steps are validations: belonging to main subgroup, belonging to prime field. let [beta, gamma, alpha, zeta, upsilon] = self.compute_challenges(p, vk, public_input); - let zh_zeta = zeta.pow(input.n) - FieldElement::one(); + let zh_zeta = zeta.pow(input.n) - FieldElement::::one(); let k1 = &input.k1; let k2 = k1 * k1; - let l1_zeta = (zeta.pow(input.n as u64) - FieldElement::one()) - / (&zeta - FieldElement::one()) + let l1_zeta = (zeta.pow(input.n as u64) - FieldElement::::one()) + / (&zeta - FieldElement::::one()) / FieldElement::from(input.n as u64); // Use the following equality to compute PI(ฮถ) diff --git a/provers/stark/Cargo.toml b/provers/stark/Cargo.toml index 26f6e25c98..033e1a9c9a 100644 --- a/provers/stark/Cargo.toml +++ b/provers/stark/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] lambdaworks-math = { workspace = true , features = ["lambdaworks-serde-binary"] } lambdaworks-crypto.workspace = true +miden-core = { git="https://github.com/lambdaclass/miden-vm", optional=true} rand = "0.8.5" thiserror = "1.0.38" @@ -45,8 +46,9 @@ wasm-bindgen-test = "0.3.0" test_fiat_shamir = [] instruments = [] # This enables timing prints in prover and verifier metal = ["lambdaworks-math/metal"] -parallel = ["dep:rayon"] +parallel = ["dep:rayon", "lambdaworks-crypto/parallel"] wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:web-sys"] +winter_compatibility = ["miden-core"] [target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies] proptest = "1.2.0" diff --git a/provers/stark/src/constraints/boundary.rs b/provers/stark/src/constraints/boundary.rs index 6fc6e97c53..50a4bbb988 100644 --- a/provers/stark/src/constraints/boundary.rs +++ b/provers/stark/src/constraints/boundary.rs @@ -14,19 +14,45 @@ pub struct BoundaryConstraint { pub col: usize, pub step: usize, pub value: FieldElement, + pub is_aux: bool, } impl BoundaryConstraint { - pub fn new(col: usize, step: usize, value: FieldElement) -> Self { - Self { col, step, value } + pub fn new_main(col: usize, step: usize, value: FieldElement) -> Self { + Self { + col, + step, + value, + is_aux: false, + } + } + + pub fn new_aux(col: usize, step: usize, value: FieldElement) -> Self { + Self { + col, + step, + value, + is_aux: true, + } + } + + /// Used for creating boundary constraints for a trace with only one column + pub fn new_simple_main(step: usize, value: FieldElement) -> Self { + Self { + col: 0, + step, + value, + is_aux: false, + } } /// Used for creating boundary constraints for a trace with only one column - pub fn new_simple(step: usize, value: FieldElement) -> Self { + pub fn new_simple_aux(step: usize, value: FieldElement) -> Self { Self { col: 0, step, value, + is_aux: true, } } } @@ -150,9 +176,9 @@ mod test { // * a0 = 1 // * a1 = 1 // * a7 = 32 - let a0 = BoundaryConstraint::new_simple(0, one); - let a1 = BoundaryConstraint::new_simple(1, one); - let result = BoundaryConstraint::new_simple(7, FieldElement::::from(32)); + let a0 = BoundaryConstraint::new_simple_main(0, one); + let a1 = BoundaryConstraint::new_simple_main(1, one); + let result = BoundaryConstraint::new_simple_main(7, FieldElement::::from(32)); let constraints = BoundaryConstraints::from_constraints(vec![a0, a1, result]); diff --git a/provers/stark/src/constraints/evaluator.rs b/provers/stark/src/constraints/evaluator.rs index 51926e098b..1cc889b73b 100644 --- a/provers/stark/src/constraints/evaluator.rs +++ b/provers/stark/src/constraints/evaluator.rs @@ -1,7 +1,10 @@ use itertools::Itertools; use lambdaworks_math::{ fft::{cpu::roots_of_unity::get_powers_of_primitive_root_coset, errors::FFTError}, - field::{element::FieldElement, traits::IsFFTField}, + field::{ + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, + }, polynomial::Polynomial, traits::Serializable, }; @@ -14,16 +17,15 @@ use rayon::prelude::{ use super::boundary::BoundaryConstraints; #[cfg(all(debug_assertions, not(feature = "parallel")))] use crate::debug::check_boundary_polys_divisibility; -use crate::domain::Domain; -use crate::trace::TraceTable; use crate::traits::AIR; +use crate::{domain::Domain, table::EvaluationTable}; use crate::{frame::Frame, prover::evaluate_polynomial_on_lde_domain}; -pub struct ConstraintEvaluator { - boundary_constraints: BoundaryConstraints, +pub struct ConstraintEvaluator { + boundary_constraints: BoundaryConstraints, } -impl ConstraintEvaluator { - pub fn new>(air: &A, rap_challenges: &A::RAPChallenges) -> Self { +impl ConstraintEvaluator { + pub fn new(air: &A, rap_challenges: &A::RAPChallenges) -> Self { let boundary_constraints = air.boundary_constraints(rap_challenges); Self { @@ -31,23 +33,24 @@ impl ConstraintEvaluator { } } - pub fn evaluate>( + pub(crate) fn evaluate( &self, air: &A, - lde_trace: &TraceTable, - domain: &Domain, - transition_coefficients: &[FieldElement], - boundary_coefficients: &[FieldElement], + lde_table: &EvaluationTable, + domain: &Domain, + transition_coefficients: &[FieldElement], + boundary_coefficients: &[FieldElement], rap_challenges: &A::RAPChallenges, - ) -> Vec> + ) -> Vec> where - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, A: Send + Sync, A::RAPChallenges: Send + Sync, { let boundary_constraints = &self.boundary_constraints; let number_of_b_constraints = boundary_constraints.constraints.len(); - let boundary_zerofiers_inverse_evaluations: Vec>> = + let boundary_zerofiers_inverse_evaluations: Vec>> = boundary_constraints .constraints .iter() @@ -57,16 +60,16 @@ impl ConstraintEvaluator { .lde_roots_of_unity_coset .iter() .map(|v| v.clone() - point) - .collect::>>(); + .collect::>>(); FieldElement::inplace_batch_inverse(&mut evals).unwrap(); evals }) - .collect::>>>(); + .collect::>>>(); let trace_length = air.trace_length(); #[cfg(all(debug_assertions, not(feature = "parallel")))] - let boundary_polys: Vec>> = Vec::new(); + let boundary_polys: Vec>> = Vec::new(); let lde_periodic_columns = air .get_periodic_column_polynomials() @@ -82,24 +85,27 @@ impl ConstraintEvaluator { .collect::>>, FFTError>>() .unwrap(); - let n_col = lde_trace.n_cols(); - let n_elem = domain.lde_roots_of_unity_coset.len(); let boundary_polys_evaluations = boundary_constraints .constraints .iter() .map(|constraint| { - let col = constraint.col; - lde_trace - .table - .data - .iter() - .skip(col) - .step_by(n_col) - .take(n_elem) - .map(|v| v - &constraint.value) - .collect::>>() + if constraint.is_aux { + (0..lde_table.n_rows()) + .map(|row| { + let v = lde_table.get_aux(row, constraint.col); + v - &constraint.value + }) + .collect() + } else { + (0..lde_table.n_rows()) + .map(|row| { + let v = lde_table.get_main(row, constraint.col); + v - &constraint.value + }) + .collect() + } }) - .collect::>>>(); + .collect::>>>(); #[cfg(feature = "parallel")] let boundary_eval_iter = (0..domain.lde_roots_of_unity_coset.len()).into_par_iter(); @@ -117,7 +123,7 @@ impl ConstraintEvaluator { * &boundary_polys_evaluations[constraint_index][domain_index] }) }) - .collect::>>(); + .collect::>>(); #[cfg(all(debug_assertions, not(feature = "parallel")))] let boundary_zerofiers = Vec::new(); @@ -138,10 +144,10 @@ impl ConstraintEvaluator { let blowup_factor_order = u64::from(blowup_factor.trailing_zeros()); - let offset = FieldElement::::from(air.context().proof_options.coset_offset); + let offset = FieldElement::::from(air.context().proof_options.coset_offset); let offset_pow = offset.pow(trace_length); - let one = FieldElement::::one(); - let mut zerofier_evaluations = get_powers_of_primitive_root_coset( + let one = FieldElement::one(); + let mut zerofier_evaluations = get_powers_of_primitive_root_coset::( blowup_factor_order, blowup_factor as usize, &offset_pow, @@ -178,8 +184,8 @@ impl ConstraintEvaluator { .zip(&boundary_evaluation) .zip(zerofier_iter) .map(|((i, boundary), zerofier)| { - let frame = Frame::read_from_trace( - lde_trace, + let frame = Frame::::read_from_lde_table( + lde_table, i, blowup_factor, &air.context().transition_offsets, @@ -193,7 +199,7 @@ impl ConstraintEvaluator { // Compute all the transition constraints at this // point of the LDE domain. let evaluations_transition = - air.compute_transition(&frame, &periodic_values, rap_challenges); + air.compute_transition_prover(&frame, &periodic_values, rap_challenges); #[cfg(all(debug_assertions, not(feature = "parallel")))] transition_evaluations.push(evaluations_transition.clone()); @@ -212,14 +218,14 @@ impl ConstraintEvaluator { // If there's no exemption, then // the zerofier remains as it was. if *exemption == 0 { - acc + zerofier * beta * eval + acc + eval * zerofier * beta } else { //TODO: change how exemptions are indexed! if num_exemptions == 1 { - acc + zerofier - * beta - * eval + acc + eval * &transition_exemptions_evaluations[0][i] + * beta + * zerofier } else { // This case is not used for Cairo Programs, it can be improved in the future let vector = air @@ -235,10 +241,10 @@ impl ConstraintEvaluator { .position(|elem_2| elem_2 == exemption) .expect("is there"); - acc + zerofier - * beta - * eval + acc + eval * &transition_exemptions_evaluations[index][i] + * zerofier + * beta } } }); @@ -246,18 +252,19 @@ impl ConstraintEvaluator { acc_transition + boundary }) - .collect::>>(); + .collect::>>(); evaluations_t } } -fn evaluate_transition_exemptions( - transition_exemptions: Vec>>, +fn evaluate_transition_exemptions, E: IsField>( + transition_exemptions: Vec>>, domain: &Domain, -) -> Vec>> +) -> Vec>> where FieldElement: Send + Sync + Serializable, + FieldElement: Send + Sync + Serializable, Polynomial>: Send + Sync, { #[cfg(feature = "parallel")] diff --git a/provers/stark/src/debug.rs b/provers/stark/src/debug.rs index d1abd51d26..85fc3a1af5 100644 --- a/provers/stark/src/debug.rs +++ b/provers/stark/src/debug.rs @@ -1,41 +1,59 @@ -use crate::frame::Frame; -use crate::trace::TraceTable; +use crate::{frame::Frame, table::EvaluationTable}; use super::domain::Domain; use super::traits::AIR; -use lambdaworks_math::fft::polynomial::FFTPoly; use lambdaworks_math::{ - field::{element::FieldElement, traits::IsFFTField}, + field::{ + element::FieldElement, + traits::{IsFFTField, IsField}, + }, polynomial::Polynomial, }; use log::{error, info}; /// Validates that the trace is valid with respect to the supplied AIR constraints -pub fn validate_trace>( +pub fn validate_trace( air: &A, - trace_polys: &[Polynomial>], + main_trace_polys: &[Polynomial>], + aux_trace_polys: &[Polynomial>], domain: &Domain, rap_challenges: &A::RAPChallenges, ) -> bool { info!("Starting constraints validation over trace..."); let mut ret = true; - let trace_columns: Vec<_> = trace_polys + let main_trace_columns: Vec<_> = main_trace_polys .iter() .map(|poly| { - poly.evaluate_fft(1, Some(domain.interpolation_domain_size)) + Polynomial::>::evaluate_fft::( + poly, + 1, + Some(domain.interpolation_domain_size), + ) + .unwrap() + }) + .collect(); + let aux_trace_columns: Vec<_> = aux_trace_polys + .iter() + .map(|poly| { + Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)) .unwrap() }) .collect(); - let trace = TraceTable::from_columns(trace_columns, A::STEP_SIZE); + let lde_table = + EvaluationTable::from_columns(main_trace_columns, aux_trace_columns, A::STEP_SIZE); let periodic_columns: Vec<_> = air .get_periodic_column_polynomials() .iter() .map(|poly| { - poly.evaluate_fft(1, Some(domain.interpolation_domain_size)) - .unwrap() + Polynomial::>::evaluate_fft::( + poly, + 1, + Some(domain.interpolation_domain_size), + ) + .unwrap() }) .collect(); @@ -47,11 +65,16 @@ pub fn validate_trace>( let col = constraint.col; let step = constraint.step; let boundary_value = constraint.value.clone(); - let trace_value = trace.get(step, col); - if &boundary_value != trace_value { + let trace_value = if !constraint.is_aux { + lde_table.get_main(step, col).clone().to_extension() + } else { + lde_table.get_aux(step, col).clone() + }; + + if boundary_value.clone().to_extension() != trace_value { ret = false; - error!("Boundary constraint inconsistency - Expected value {} in step {} and column {}, found: {}", boundary_value.representative(), step, col, trace_value.representative()); + error!("Boundary constraint inconsistency - Expected value {:?} in step {} and column {}, found: {:?}", boundary_value, step, col, trace_value); } }); @@ -59,21 +82,22 @@ pub fn validate_trace>( let n_transition_constraints = air.context().num_transition_constraints(); let transition_exemptions = &air.context().transition_exemptions; - let exemption_steps: Vec = vec![trace.n_rows(); n_transition_constraints] + let exemption_steps: Vec = vec![lde_table.n_rows(); n_transition_constraints] .iter() .zip(transition_exemptions) .map(|(trace_steps, exemptions)| trace_steps - exemptions) .collect(); // Iterate over trace and compute transitions - for step in 0..trace.num_steps() { - let frame = Frame::read_from_trace(&trace, step, 1, &air.context().transition_offsets); + for step in 0..lde_table.num_steps() { + let frame = + Frame::read_from_lde_table(&lde_table, step, 1, &air.context().transition_offsets); let periodic_values: Vec<_> = periodic_columns .iter() .map(|col| col[step].clone()) .collect(); - let evaluations = air.compute_transition(&frame, &periodic_values, rap_challenges); + let evaluations = air.compute_transition_prover(&frame, &periodic_values, rap_challenges); // Iterate over each transition evaluation. When the evaluated step is not from // the exemption steps corresponding to the transition, it should have zero as a @@ -81,13 +105,11 @@ pub fn validate_trace>( evaluations.iter().enumerate().for_each(|(i, eval)| { // Check that all the transition constraint evaluations of the trace are zero. // We don't take into account the transition exemptions. - if step < exemption_steps[i] && eval != &FieldElement::::zero() { + if step < exemption_steps[i] && eval != &FieldElement::zero() { ret = false; error!( - "Inconsistent evaluation of transition {} in step {} - expected 0, got {}", - i, - step, - eval.representative() + "Inconsistent evaluation of transition {} in step {} - expected 0, got {:?}", + i, step, eval ); } }) @@ -116,7 +138,7 @@ pub fn check_boundary_polys_divisibility( /// array, returning a true when valid and false when not. pub fn validate_2d_structure(data: &[FieldElement], width: usize) -> bool where - F: IsFFTField, + F: IsField, { let rows: Vec>> = data.chunks(width).map(|c| c.to_vec()).collect(); rows.iter().all(|r| r.len() == rows[0].len()) diff --git a/provers/stark/src/examples/dummy_air.rs b/provers/stark/src/examples/dummy_air.rs index 9986e49657..b85c49e9c0 100644 --- a/provers/stark/src/examples/dummy_air.rs +++ b/provers/stark/src/examples/dummy_air.rs @@ -21,6 +21,7 @@ pub struct DummyAIR { impl AIR for DummyAIR { type Field = Stark252PrimeField; + type FieldExtension = Stark252PrimeField; type RAPChallenges = (); type PublicInputs = (); @@ -55,23 +56,23 @@ impl AIR for DummyAIR { fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, - ) -> Vec> { + ) -> Vec> { let first_step = frame.get_evaluation_step(0); let second_step = frame.get_evaluation_step(1); let third_step = frame.get_evaluation_step(2); - let flag = first_step.get_evaluation_element(0, 0); - let a0 = first_step.get_evaluation_element(0, 1); - let a1 = second_step.get_evaluation_element(0, 1); - let a2 = third_step.get_evaluation_element(0, 1); + let flag = first_step.get_main_evaluation_element(0, 0); + let a0 = first_step.get_main_evaluation_element(0, 1); + let a1 = second_step.get_main_evaluation_element(0, 1); + let a2 = third_step.get_main_evaluation_element(0, 1); let f_constraint = flag * (flag - FieldElement::one()); @@ -84,8 +85,8 @@ impl AIR for DummyAIR { &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new(1, 0, FieldElement::::one()); - let a1 = BoundaryConstraint::new(1, 1, FieldElement::::one()); + let a0 = BoundaryConstraint::new_main(1, 0, FieldElement::::one()); + let a1 = BoundaryConstraint::new_main(1, 1, FieldElement::::one()); BoundaryConstraints::from_constraints(vec![a0, a1]) } @@ -109,6 +110,15 @@ impl AIR for DummyAIR { fn pub_inputs(&self) -> &Self::PublicInputs { &() } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn dummy_trace(trace_length: usize) -> TraceTable { diff --git a/provers/stark/src/examples/fibonacci_2_cols_shifted.rs b/provers/stark/src/examples/fibonacci_2_cols_shifted.rs index 5b24fdf0cd..d5c220d01a 100644 --- a/provers/stark/src/examples/fibonacci_2_cols_shifted.rs +++ b/provers/stark/src/examples/fibonacci_2_cols_shifted.rs @@ -53,6 +53,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = PublicInputs; @@ -88,24 +89,24 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { let first_row = frame.get_evaluation_step(0); let second_row = frame.get_evaluation_step(1); - let a0_0 = first_row.get_evaluation_element(0, 0); - let a0_1 = first_row.get_evaluation_element(0, 1); + let a0_0 = first_row.get_main_evaluation_element(0, 0); + let a0_1 = first_row.get_main_evaluation_element(0, 1); - let a1_0 = second_row.get_evaluation_element(0, 0); - let a1_1 = second_row.get_evaluation_element(0, 1); + let a1_0 = second_row.get_main_evaluation_element(0, 0); + let a1_1 = second_row.get_main_evaluation_element(0, 1); let first_transition = a1_0 - a0_1; let second_transition = a1_1 - a0_0 - a0_1; @@ -121,8 +122,8 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let initial_condition = BoundaryConstraint::new(0, 0, FieldElement::one()); - let claimed_value_constraint = BoundaryConstraint::new( + let initial_condition = BoundaryConstraint::new_main(0, 0, FieldElement::one()); + let claimed_value_constraint = BoundaryConstraint::new_main( 0, self.pub_inputs.claimed_index, self.pub_inputs.claimed_value.clone(), @@ -146,6 +147,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn compute_trace( diff --git a/provers/stark/src/examples/fibonacci_2_columns.rs b/provers/stark/src/examples/fibonacci_2_columns.rs index 1d4b7712d6..98c93b72b3 100644 --- a/provers/stark/src/examples/fibonacci_2_columns.rs +++ b/provers/stark/src/examples/fibonacci_2_columns.rs @@ -29,6 +29,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = FibonacciPublicInputs; @@ -64,13 +65,13 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { @@ -80,10 +81,10 @@ where // constraints of Fibonacci sequence (2 terms per step): // s_{0, i+1} = s_{0, i} + s_{1, i} // s_{1, i+1} = s_{1, i} + s_{0, i+1} - let s0_0 = first_step.get_evaluation_element(0, 0); - let s0_1 = first_step.get_evaluation_element(0, 1); - let s1_0 = second_step.get_evaluation_element(0, 0); - let s1_1 = second_step.get_evaluation_element(0, 1); + let s0_0 = first_step.get_main_evaluation_element(0, 0); + let s0_1 = first_step.get_main_evaluation_element(0, 1); + let s1_0 = second_step.get_main_evaluation_element(0, 0); + let s1_1 = second_step.get_main_evaluation_element(0, 1); let first_transition = s1_0 - s0_0 - s0_1; let second_transition = s1_1 - s0_1 - s1_0; @@ -99,8 +100,8 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new(0, 0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new(1, 0, self.pub_inputs.a1.clone()); + let a0 = BoundaryConstraint::new_main(0, 0, self.pub_inputs.a0.clone()); + let a1 = BoundaryConstraint::new_main(1, 0, self.pub_inputs.a1.clone()); BoundaryConstraints::from_constraints(vec![a0, a1]) } @@ -120,6 +121,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn compute_trace( diff --git a/provers/stark/src/examples/fibonacci_rap.rs b/provers/stark/src/examples/fibonacci_rap.rs index 643966725c..b960ae97c0 100644 --- a/provers/stark/src/examples/fibonacci_rap.rs +++ b/provers/stark/src/examples/fibonacci_rap.rs @@ -42,6 +42,7 @@ where FieldElement: ByteConversion, { type Field = F; + type FieldExtension = F; type RAPChallenges = FieldElement; type PublicInputs = FibonacciRAPPublicInputs; @@ -106,9 +107,9 @@ where 1 } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], gamma: &Self::RAPChallenges, ) -> Vec> { @@ -117,18 +118,18 @@ where let second_step = frame.get_evaluation_step(1); let third_step = frame.get_evaluation_step(2); - let a0 = first_step.get_evaluation_element(0, 0); - let a1 = second_step.get_evaluation_element(0, 0); - let a2 = third_step.get_evaluation_element(0, 0); + let a0 = first_step.get_main_evaluation_element(0, 0); + let a1 = second_step.get_main_evaluation_element(0, 0); + let a2 = third_step.get_main_evaluation_element(0, 0); let mut constraints = vec![a2 - a1 - a0]; // Auxiliary constraints - let z_i = first_step.get_evaluation_element(0, 2); - let z_i_plus_one = second_step.get_evaluation_element(0, 2); + let z_i = first_step.get_aux_evaluation_element(0, 0); + let z_i_plus_one = second_step.get_aux_evaluation_element(0, 0); - let a_i = first_step.get_evaluation_element(0, 0); - let b_i = first_step.get_evaluation_element(0, 1); + let a_i = first_step.get_main_evaluation_element(0, 0); + let b_i = first_step.get_main_evaluation_element(0, 1); let eval = z_i_plus_one * (b_i + gamma) - z_i * (a_i + gamma); @@ -141,11 +142,11 @@ where _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { // Main boundary constraints - let a0 = BoundaryConstraint::new_simple(0, FieldElement::::one()); - let a1 = BoundaryConstraint::new_simple(1, FieldElement::::one()); + let a0 = BoundaryConstraint::new_simple_main(0, FieldElement::::one()); + let a1 = BoundaryConstraint::new_simple_main(1, FieldElement::::one()); // Auxiliary boundary constraints - let a0_aux = BoundaryConstraint::new(2, 0, FieldElement::::one()); + let a0_aux = BoundaryConstraint::new_aux(0, 0, FieldElement::::one()); BoundaryConstraints::from_constraints(vec![a0, a1, a0_aux]) } @@ -165,6 +166,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn fibonacci_rap_trace( diff --git a/provers/stark/src/examples/quadratic_air.rs b/provers/stark/src/examples/quadratic_air.rs index 5d8e70f244..ef7c294099 100644 --- a/provers/stark/src/examples/quadratic_air.rs +++ b/provers/stark/src/examples/quadratic_air.rs @@ -33,6 +33,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = QuadraticPublicInputs; @@ -68,21 +69,21 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { let first_step = frame.get_evaluation_step(0); let second_step = frame.get_evaluation_step(1); - let x = first_step.get_evaluation_element(0, 0); - let x_squared = second_step.get_evaluation_element(0, 0); + let x = first_step.get_main_evaluation_element(0, 0); + let x_squared = second_step.get_main_evaluation_element(0, 0); vec![x_squared - x * x] } @@ -95,7 +96,7 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); + let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); BoundaryConstraints::from_constraints(vec![a0]) } @@ -115,6 +116,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn quadratic_trace( diff --git a/provers/stark/src/examples/simple_fibonacci.rs b/provers/stark/src/examples/simple_fibonacci.rs index c90e8296cf..b255ee3185 100644 --- a/provers/stark/src/examples/simple_fibonacci.rs +++ b/provers/stark/src/examples/simple_fibonacci.rs @@ -34,6 +34,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = FibonacciPublicInputs; @@ -73,13 +74,13 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, _periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { @@ -87,9 +88,9 @@ where let second_step = frame.get_evaluation_step(1); let third_step = frame.get_evaluation_step(2); - let a0 = first_step.get_evaluation_element(0, 0); - let a1 = second_step.get_evaluation_element(0, 0); - let a2 = third_step.get_evaluation_element(0, 0); + let a0 = first_step.get_main_evaluation_element(0, 0); + let a1 = second_step.get_main_evaluation_element(0, 0); + let a2 = third_step.get_main_evaluation_element(0, 0); vec![a2 - a1 - a0] } @@ -98,8 +99,8 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new_simple(1, self.pub_inputs.a1.clone()); + let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); + let a1 = BoundaryConstraint::new_simple_main(1, self.pub_inputs.a1.clone()); BoundaryConstraints::from_constraints(vec![a0, a1]) } @@ -119,6 +120,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn fibonacci_trace( diff --git a/provers/stark/src/examples/simple_periodic_cols.rs b/provers/stark/src/examples/simple_periodic_cols.rs index 9347dd9028..3ca7b68b2a 100644 --- a/provers/stark/src/examples/simple_periodic_cols.rs +++ b/provers/stark/src/examples/simple_periodic_cols.rs @@ -48,6 +48,7 @@ where F: IsFFTField, { type Field = F; + type FieldExtension = F; type RAPChallenges = (); type PublicInputs = SimplePeriodicPublicInputs; @@ -87,13 +88,13 @@ where fn build_rap_challenges( &self, - _transcript: &mut impl IsStarkTranscript, + _transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges { } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, periodic_values: &[FieldElement], _rap_challenges: &Self::RAPChallenges, ) -> Vec> { @@ -101,9 +102,9 @@ where let second_step = frame.get_evaluation_step(1); let third_step = frame.get_evaluation_step(2); - let a0 = first_step.get_evaluation_element(0, 0); - let a1 = second_step.get_evaluation_element(0, 0); - let a2 = third_step.get_evaluation_element(0, 0); + let a0 = first_step.get_main_evaluation_element(0, 0); + let a1 = second_step.get_main_evaluation_element(0, 0); + let a2 = third_step.get_main_evaluation_element(0, 0); let s = &periodic_values[0]; @@ -114,9 +115,11 @@ where &self, _rap_challenges: &Self::RAPChallenges, ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); - let a1 = - BoundaryConstraint::new_simple(self.trace_length() - 1, self.pub_inputs.a1.clone()); + let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); + let a1 = BoundaryConstraint::new_simple_main( + self.trace_length() - 1, + self.pub_inputs.a1.clone(), + ); BoundaryConstraints::from_constraints(vec![a0, a1]) } @@ -140,6 +143,15 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.pub_inputs } + + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + self.compute_transition_prover(frame, periodic_values, rap_challenges) + } } pub fn simple_periodic_trace(trace_length: usize) -> TraceTable { diff --git a/provers/stark/src/frame.rs b/provers/stark/src/frame.rs index 6b3442c401..51d24e3950 100644 --- a/provers/stark/src/frame.rs +++ b/provers/stark/src/frame.rs @@ -1,41 +1,50 @@ -use super::trace::TraceTable; -use crate::trace::StepView; -use lambdaworks_math::field::traits::IsFFTField; +use crate::{table::EvaluationTable, trace::StepView}; +use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; /// A frame represents a collection of trace steps. /// The collected steps are all the necessary steps for /// all transition costraints over a trace to be evaluated. #[derive(Clone, Debug, PartialEq)] -pub struct Frame<'t, F: IsFFTField> { - steps: Vec>, +pub struct Frame<'t, F: IsSubFieldOf, E: IsField> { + steps: Vec>, } -impl<'t, F: IsFFTField> Frame<'t, F> { - pub fn new(steps: Vec>) -> Self { +impl<'t, F: IsSubFieldOf, E: IsField> Frame<'t, F, E> { + pub fn new(steps: Vec>) -> Self { Self { steps } } - pub fn get_evaluation_step(&self, step: usize) -> &StepView { + pub fn get_evaluation_step(&self, step: usize) -> &StepView { &self.steps[step] } - pub fn read_from_trace( - trace: &'t TraceTable, + pub fn read_from_lde_table( + lde_table: &'t EvaluationTable, step: usize, blowup: u8, offsets: &[usize], ) -> Self { // Get trace length to apply module with it when getting elements of // the frame from the trace. - let trace_steps = trace.num_steps(); + let trace_steps = lde_table.num_steps(); let steps = offsets .iter() .map(|eval_offset| { - trace.step_view((step + (eval_offset * blowup as usize)) % trace_steps) + lde_table.step_view((step + (eval_offset * blowup as usize)) % trace_steps) }) .collect(); Self::new(steps) } } + +impl<'t, E: IsField> Frame<'t, E, E> { + pub fn read_from_ood_table(ood_table: &'t EvaluationTable, offsets: &[usize]) -> Self { + let steps: Vec<_> = offsets + .iter() + .map(|offset| ood_table.step_view(*offset)) + .collect(); + Self::new(steps) + } +} diff --git a/provers/stark/src/fri/fri_commitment.rs b/provers/stark/src/fri/fri_commitment.rs index b39f42f3f7..83f24cbadd 100644 --- a/provers/stark/src/fri/fri_commitment.rs +++ b/provers/stark/src/fri/fri_commitment.rs @@ -1,9 +1,6 @@ use lambdaworks_crypto::merkle_tree::{merkle::MerkleTree, traits::IsMerkleTreeBackend}; use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsFFTField, IsField}, - }, + field::{element::FieldElement, traits::IsField}, traits::Serializable, }; @@ -22,7 +19,7 @@ where impl FriLayer where - F: IsField + IsFFTField, + F: IsField, FieldElement: Serializable, B: IsMerkleTreeBackend, { diff --git a/provers/stark/src/fri/fri_decommit.rs b/provers/stark/src/fri/fri_decommit.rs index 63da9ec5fd..8bc378ac82 100644 --- a/provers/stark/src/fri/fri_decommit.rs +++ b/provers/stark/src/fri/fri_decommit.rs @@ -2,12 +2,12 @@ pub use lambdaworks_crypto::fiat_shamir::transcript::Transcript; use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::IsPrimeField; +use lambdaworks_math::field::traits::IsField; use crate::config::Commitment; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct FriDecommitment { +pub struct FriDecommitment { pub layers_auth_paths: Vec>, pub layers_evaluations_sym: Vec>, } diff --git a/provers/stark/src/fri/fri_functions.rs b/provers/stark/src/fri/fri_functions.rs index 500fcecf9d..33e8a8b431 100644 --- a/provers/stark/src/fri/fri_functions.rs +++ b/provers/stark/src/fri/fri_functions.rs @@ -1,5 +1,8 @@ use super::Polynomial; -use lambdaworks_math::field::{element::FieldElement, traits::IsField}; +use lambdaworks_math::{ + field::{element::FieldElement, traits::IsField}, + polynomial, +}; pub fn fold_polynomial( poly: &Polynomial>, @@ -19,7 +22,7 @@ where .map(|v| (v.clone()) * beta) .collect(); - let (even_poly, odd_poly) = Polynomial::pad_with_zero_coefficients( + let (even_poly, odd_poly) = polynomial::pad_with_zero_coefficients( &Polynomial::new(&even_coef), &Polynomial::new(&odd_coef_mul_beta), ); diff --git a/provers/stark/src/fri/mod.rs b/provers/stark/src/fri/mod.rs index 55c5f06a8c..ffb8b1ff38 100644 --- a/provers/stark/src/fri/mod.rs +++ b/provers/stark/src/fri/mod.rs @@ -2,10 +2,11 @@ pub mod fri_commitment; pub mod fri_decommit; mod fri_functions; -use lambdaworks_math::fft::cpu::bit_reversing::in_place_bit_reverse_permute; -use lambdaworks_math::fft::polynomial::FFTPoly; -use lambdaworks_math::field::traits::IsFFTField; +use lambdaworks_math::field::traits::{IsFFTField, IsField}; use lambdaworks_math::traits::Serializable; +use lambdaworks_math::{ + fft::cpu::bit_reversing::in_place_bit_reverse_permute, field::traits::IsSubFieldOf, +}; pub use lambdaworks_math::{ field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}, polynomial::Polynomial, @@ -18,23 +19,24 @@ use self::fri_commitment::FriLayer; use self::fri_decommit::FriDecommitment; use self::fri_functions::fold_polynomial; -pub fn commit_phase( +pub fn commit_phase, E: IsField>( number_layers: usize, - p_0: Polynomial>, - transcript: &mut impl IsStarkTranscript, + p_0: Polynomial>, + transcript: &mut impl IsStarkTranscript, coset_offset: &FieldElement, domain_size: usize, ) -> ( - FieldElement, - Vec>>, + FieldElement, + Vec>>, ) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let mut domain_size = domain_size; let mut fri_layer_list = Vec::with_capacity(number_layers); - let mut current_layer: FriLayer>; + let mut current_layer: FriLayer>; let mut current_poly = p_0; let mut coset_offset = coset_offset.clone(); @@ -46,7 +48,7 @@ where domain_size /= 2; // Compute layer polynomial and domain - current_poly = fold_polynomial(¤t_poly, &zeta) * FieldElement::from(2); + current_poly = FieldElement::::from(2) * fold_polynomial(¤t_poly, &zeta); current_layer = new_fri_layer(¤t_poly, &coset_offset, domain_size); let new_data = ¤t_layer.merkle_tree.root; fri_layer_list.push(current_layer.clone()); // TODO: remove this clone @@ -58,11 +60,11 @@ where // <<<< Receive challenge: ๐œโ‚™โ‚‹โ‚ let zeta = transcript.sample_field_element(); - let last_poly = fold_polynomial(¤t_poly, &zeta) * FieldElement::from(2); + let last_poly = FieldElement::::from(2) * fold_polynomial(¤t_poly, &zeta); let last_value = last_poly .coefficients() - .get(0) + .first() .unwrap_or(&FieldElement::zero()) .clone(); @@ -72,12 +74,12 @@ where (last_value, fri_layer_list) } -pub fn query_phase( +pub fn query_phase( fri_layers: &Vec>>, iotas: &[usize], ) -> Vec> where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, { if !fri_layers.is_empty() { let query_list = iotas @@ -110,18 +112,17 @@ where } } -pub fn new_fri_layer( - poly: &Polynomial>, +pub fn new_fri_layer, E: IsField>( + poly: &Polynomial>, coset_offset: &FieldElement, domain_size: usize, -) -> crate::fri::fri_commitment::FriLayer> +) -> crate::fri::fri_commitment::FriLayer> where - F: IsFFTField, - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - let mut evaluation = poly - .evaluate_offset_fft(1, Some(domain_size), coset_offset) - .unwrap(); // TODO: return error + let mut evaluation = + Polynomial::evaluate_offset_fft(poly, 1, Some(domain_size), coset_offset).unwrap(); // TODO: return error in_place_bit_reverse_permute(&mut evaluation); @@ -132,5 +133,10 @@ where let merkle_tree = BatchedMerkleTree::build(&to_commit); - FriLayer::new(&evaluation, merkle_tree, coset_offset.clone(), domain_size) + FriLayer::new( + &evaluation, + merkle_tree, + coset_offset.clone().to_extension(), + domain_size, + ) } diff --git a/provers/stark/src/proof/stark.rs b/provers/stark/src/proof/stark.rs index 318f337b26..893643537c 100644 --- a/provers/stark/src/proof/stark.rs +++ b/provers/stark/src/proof/stark.rs @@ -3,8 +3,9 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::{ field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsFFTField, + element::FieldElement, + fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, + traits::{IsField, IsSubFieldOf}, }, traits::Serializable, }; @@ -13,7 +14,7 @@ use crate::{ config::Commitment, domain::Domain, fri::fri_decommit::FriDecommitment, - table::Table, + table::EvaluationTable, traits::AIR, transcript::StoneProverTranscript, verifier::{IsStarkVerifier, Verifier}, @@ -22,38 +23,47 @@ use crate::{ use super::options::ProofOptions; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct DeepPolynomialOpening { - pub lde_composition_poly_proof: Proof, - pub lde_composition_poly_parts_evaluation: Vec>, - pub lde_trace_merkle_proofs: Vec>, - pub lde_trace_evaluations: Vec>, +pub struct PolynomialOpenings { + pub proof: Proof, + pub proof_sym: Proof, + pub evaluations: Vec>, + pub evaluations_sym: Vec>, } -pub type DeepPolynomialOpenings = Vec>; +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct DeepPolynomialOpening, E: IsField> { + pub composition_poly: PolynomialOpenings, + pub main_trace_polys: PolynomialOpenings, + pub aux_trace_polys: Option>, +} + +pub type DeepPolynomialOpenings = Vec>; #[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct StarkProof { +pub struct StarkProof, E: IsField> { // Length of the execution trace pub trace_length: usize, // Commitments of the trace columns // [tโฑผ] - pub lde_trace_merkle_roots: Vec, + pub lde_trace_main_merkle_root: Commitment, + // Commitments of auxiliary trace columns + // [tโฑผ] + pub lde_trace_aux_merkle_root: Option, // tโฑผ(zgแต) - pub trace_ood_evaluations: Table, + pub trace_ood_evaluations: EvaluationTable, // Commitments to Hแตข pub composition_poly_root: Commitment, // Hแตข(z^N) - pub composition_poly_parts_ood_evaluation: Vec>, + pub composition_poly_parts_ood_evaluation: Vec>, // [pโ‚–] pub fri_layers_merkle_roots: Vec, // pโ‚™ - pub fri_last_value: FieldElement, + pub fri_last_value: FieldElement, // Open(pโ‚–(Dโ‚–), โˆ’๐œโ‚›^(2แต)) - pub query_list: Vec>, + pub query_list: Vec>, // Open(Hโ‚(D_LDE, ๐œแตข), Open(Hโ‚‚(D_LDE, ๐œแตข), Open(tโฑผ(D_LDE), ๐œแตข) - pub deep_poly_openings: DeepPolynomialOpenings, // Open(Hโ‚(D_LDE, -๐œแตข), Open(Hโ‚‚(D_LDE, -๐œแตข), Open(tโฑผ(D_LDE), -๐œแตข) - pub deep_poly_openings_sym: DeepPolynomialOpenings, + pub deep_poly_openings: DeepPolynomialOpenings, // nonce obtained from grinding pub nonce: Option, } @@ -64,12 +74,12 @@ pub struct StoneCompatibleSerializer; impl StoneCompatibleSerializer { pub fn serialize_proof( - proof: &StarkProof, + proof: &StarkProof, public_inputs: &A::PublicInputs, options: &ProofOptions, ) -> Vec where - A: AIR, + A: AIR, A::PublicInputs: Serializable, { let mut output = Vec::new(); @@ -89,20 +99,20 @@ impl StoneCompatibleSerializer { /// Appends the root bytes of the Merkle tree for the main trace, and if there is a RAP round, /// it also appends the root bytes of the Merkle tree for the extended columns. - fn append_trace_commitment(proof: &StarkProof, output: &mut Vec) { - output.extend_from_slice( - &proof - .lde_trace_merkle_roots - .iter() - .flatten() - .cloned() - .collect::>(), - ); + fn append_trace_commitment( + proof: &StarkProof, + output: &mut Vec, + ) { + output.extend_from_slice(&proof.lde_trace_main_merkle_root); + + if let Some(lde_trace_aux_merkle_root) = proof.lde_trace_aux_merkle_root { + output.extend_from_slice(&lde_trace_aux_merkle_root); + } } /// Appends the root bytes of the Merkle tree for the composition polynomial. fn append_composition_polynomial_commitment( - proof: &StarkProof, + proof: &StarkProof, output: &mut Vec, ) { output.extend_from_slice(&proof.composition_poly_root); @@ -118,12 +128,13 @@ impl StoneCompatibleSerializer { /// /// Here, K is the length of the frame size. fn append_out_of_domain_evaluations( - proof: &StarkProof, + proof: &StarkProof, output: &mut Vec, ) { - for i in 0..proof.trace_ood_evaluations.width { - for j in 0..proof.trace_ood_evaluations.height { - output.extend_from_slice(&proof.trace_ood_evaluations.get_row(j)[i].serialize()); + for i in 0..proof.trace_ood_evaluations.n_cols() { + for j in 0..proof.trace_ood_evaluations.n_rows() { + output + .extend_from_slice(&proof.trace_ood_evaluations.get_row_main(j)[i].serialize()); } } @@ -134,7 +145,7 @@ impl StoneCompatibleSerializer { /// Appends the commitments to the inner layers of FRI followed by the element of the last layer. fn append_fri_commit_phase_commitments( - proof: &StarkProof, + proof: &StarkProof, output: &mut Vec, ) { output.extend_from_slice( @@ -151,7 +162,10 @@ impl StoneCompatibleSerializer { /// Appends the proof of work nonce in case there is one. There could be none if the `grinding_factor` /// was set to 0 during proof generation. In that case nothing is appended. - fn append_proof_of_work_nonce(proof: &StarkProof, output: &mut Vec) { + fn append_proof_of_work_nonce( + proof: &StarkProof, + output: &mut Vec, + ) { if let Some(nonce_value) = proof.nonce { output.extend_from_slice(&nonce_value.to_be_bytes()); } @@ -163,10 +177,10 @@ impl StoneCompatibleSerializer { /// Each FRI query index `i` determines a pair of elements `d_i` and `-d_i` on the domain of the /// first layer. /// Let BT_i be the concatenation of the bytes of the following values - /// t_1(d_i), t_1(-d_i), t_2(d_i), t_2(-d_i), ..., t_m(d_i), t_m(-d_i), + /// t_1(d_i), t_2(d_i), ..., t_m(d_i), t_1(-d_i), t_2(-d_i), ..., t_m(-d_i), /// where m is the total number of columns, including RAP extended ones. /// Similarly, let BH_i be the concatenation of the bytes of the following elements - /// H_1(d_i), H_1(-d_i), ..., H_s(d_i), H_s(-d_i), + /// H_1(d_i), ..., H_s(d_i), H_1(-d_i), ..., H_s(-d_i), /// where s is the number of parts into which the composition polynomial was broken. /// /// If i_1, ..., i_k are all the FRI query indexes sorted in increasing order and without repeated @@ -183,14 +197,13 @@ impl StoneCompatibleSerializer { /// following to the output: /// `BT_1 | BT_2 | BT_3 | BT_5 | TraceMergedPaths | BH_1 | BH_2 | BH_3 | BH_5 | CompositionMergedPaths` fn append_fri_query_phase_first_layer( - proof: &StarkProof, + proof: &StarkProof, fri_query_indexes: &[usize], output: &mut Vec, ) { let mut fri_first_layer_openings: Vec<_> = proof .deep_poly_openings .iter() - .zip(proof.deep_poly_openings_sym.iter()) .zip(fri_query_indexes.iter()) .collect(); // Remove repeated values @@ -200,13 +213,24 @@ impl StoneCompatibleSerializer { fri_first_layer_openings.sort_by(|a, b| a.1.cmp(b.1)); // Append BT_{i_1} | BT_{i_2} | ... | BT_{i_k} - for ((opening, opening_sym), _) in fri_first_layer_openings.iter() { - for elem in opening.lde_trace_evaluations.iter() { + for (opening, _) in fri_first_layer_openings.iter() { + for elem in opening.main_trace_polys.evaluations.iter() { output.extend_from_slice(&elem.serialize()); } - for elem in opening_sym.lde_trace_evaluations.iter() { + if let Some(aux) = &opening.aux_trace_polys { + for elem in aux.evaluations.iter() { + output.extend_from_slice(&elem.serialize()); + } + } + + for elem in opening.main_trace_polys.evaluations_sym.iter() { output.extend_from_slice(&elem.serialize()); } + if let Some(aux) = &opening.aux_trace_polys { + for elem in aux.evaluations_sym.iter() { + output.extend_from_slice(&elem.serialize()); + } + } } let fri_trace_query_indexes: Vec<_> = fri_query_indexes @@ -215,18 +239,34 @@ impl StoneCompatibleSerializer { .collect(); // Append TraceMergedPaths - for i in 0..proof.deep_poly_openings[0].lde_trace_merkle_proofs.len() { - let fri_trace_paths: Vec<_> = proof - .deep_poly_openings - .iter() - .zip(proof.deep_poly_openings_sym.iter()) - .flat_map(|(opening, opening_sym)| { - vec![ - &opening.lde_trace_merkle_proofs[i], - &opening_sym.lde_trace_merkle_proofs[i], - ] - }) - .collect(); + // Main trace + let fri_trace_paths: Vec<_> = proof + .deep_poly_openings + .iter() + .flat_map(|opening| { + vec![ + &opening.main_trace_polys.proof, + &opening.main_trace_polys.proof_sym, + ] + }) + .collect(); + let nodes = Self::merge_authentication_paths(&fri_trace_paths, &fri_trace_query_indexes); + for node in nodes.iter() { + output.extend_from_slice(node); + } + + // Aux trace + let mut all_openings_aux_trace_polys_are_some = true; + let mut fri_trace_paths: Vec<&Proof> = Vec::new(); + for opening in proof.deep_poly_openings.iter() { + if let Some(aux_trace_polys) = &opening.aux_trace_polys { + fri_trace_paths.push(&aux_trace_polys.proof); + fri_trace_paths.push(&aux_trace_polys.proof_sym); + } else { + all_openings_aux_trace_polys_are_some = false; + } + } + if all_openings_aux_trace_polys_are_some { let nodes = Self::merge_authentication_paths(&fri_trace_paths, &fri_trace_query_indexes); for node in nodes.iter() { @@ -235,11 +275,11 @@ impl StoneCompatibleSerializer { } // Append BH_{i_1} | BH_{i_2} | ... | B_{i_k} - for ((opening, opening_sym), _) in fri_first_layer_openings.iter() { - for elem in opening.lde_composition_poly_parts_evaluation.iter() { + for (opening, _) in fri_first_layer_openings.iter() { + for elem in opening.composition_poly.evaluations.iter() { output.extend_from_slice(&elem.serialize()); } - for elem in opening_sym.lde_composition_poly_parts_evaluation.iter() { + for elem in opening.composition_poly.evaluations_sym.iter() { output.extend_from_slice(&elem.serialize()); } } @@ -248,7 +288,7 @@ impl StoneCompatibleSerializer { let fri_composition_paths: Vec<_> = proof .deep_poly_openings .iter() - .map(|opening| &opening.lde_composition_poly_proof) + .map(|opening| &opening.composition_poly.proof) .collect(); let nodes = Self::merge_authentication_paths(&fri_composition_paths, fri_query_indexes); for node in nodes.iter() { @@ -278,7 +318,7 @@ impl StoneCompatibleSerializer { /// /// where n is the total number of FRI layers. fn append_fri_query_phase_inner_layers( - proof: &StarkProof, + proof: &StarkProof, fri_query_indexes: &[usize], output: &mut Vec, ) { @@ -404,12 +444,12 @@ impl StoneCompatibleSerializer { result } fn get_fri_query_indexes( - proof: &StarkProof, + proof: &StarkProof, public_inputs: &A::PublicInputs, proof_options: &ProofOptions, ) -> Vec where - A: AIR, + A: AIR, A::PublicInputs: Serializable, { let mut transcript = StoneProverTranscript::new(&public_inputs.serialize()); @@ -454,7 +494,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -532,7 +572,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -624,7 +664,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -888,7 +928,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -966,7 +1006,7 @@ mod tests { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, diff --git a/provers/stark/src/prover.rs b/provers/stark/src/prover.rs index 4cea852e70..93ff6d359c 100644 --- a/provers/stark/src/prover.rs +++ b/provers/stark/src/prover.rs @@ -1,10 +1,11 @@ +use std::marker::PhantomData; #[cfg(feature = "instruments")] use std::time::Instant; -use lambdaworks_crypto::merkle_tree::proof::Proof; use lambdaworks_math::fft::cpu::bit_reversing::{in_place_bit_reverse_permute, reverse_index}; -use lambdaworks_math::fft::{errors::FFTError, polynomial::FFTPoly}; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; +use lambdaworks_math::fft::errors::FFTError; + +use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; use lambdaworks_math::traits::Serializable; use lambdaworks_math::{ field::{element::FieldElement, traits::IsFFTField}, @@ -18,8 +19,8 @@ use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelI #[cfg(debug_assertions)] use crate::debug::validate_trace; use crate::fri; -use crate::proof::stark::DeepPolynomialOpenings; -use crate::table::Table; +use crate::proof::stark::{DeepPolynomialOpenings, PolynomialOpenings}; +use crate::table::{EvaluationTable, Table}; use crate::transcript::IsStarkTranscript; use super::config::{BatchedMerkleTree, Commitment}; @@ -32,65 +33,129 @@ use super::proof::stark::{DeepPolynomialOpening, StarkProof}; use super::trace::TraceTable; use super::traits::AIR; -pub struct Prover; - -impl IsStarkProver for Prover { - type Field = Stark252PrimeField; +/// A default STARK prover implementing `IsStarkProver`. +pub struct Prover { + phantom: PhantomData, } +impl IsStarkProver for Prover {} + #[derive(Debug)] pub enum ProvingError { WrongParameter(String), } -pub struct Round1 +/// A container for the intermediate results of the commitments to a trace table, main or auxiliary in case of RAP, +/// in the first round of the STARK Prove protocol. +pub struct Round1CommitmentData where - F: IsFFTField, - A: AIR, - FieldElement: Serializable, + F: IsField, + FieldElement: Serializable + Send + Sync, { + /// The result of the interpolation of the columns of the trace table. pub(crate) trace_polys: Vec>>, - pub(crate) lde_trace: TraceTable, - pub(crate) lde_trace_merkle_trees: Vec>, - pub(crate) lde_trace_merkle_roots: Vec, + /// The Merkle trees constructed to obtain the commitment of the entire trace table. + pub(crate) lde_trace_merkle_tree: BatchedMerkleTree, + /// The root of the Merkle tree in `lde_trace_merkle_tree`. + pub(crate) lde_trace_merkle_root: Commitment, +} + +/// A container for the results of the first round of the STARK Prove protocol. +pub struct Round1 +where + A: AIR, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, +{ + /// The table of evaluations over the LDE of the main and auxiliary trace tables. + pub(crate) lde_table: EvaluationTable, + /// The intermediate results of the commitment to the main trace table. + pub(crate) main: Round1CommitmentData, + /// The intermediate results of the commitment to the auxiliary trace table in case of RAP. + pub(crate) aux: Option>, + /// The challenges of the RAP round. pub(crate) rap_challenges: A::RAPChallenges, } +impl Round1 +where + A: AIR, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, +{ + /// Returns the full list of the polynomials interpolating the trace. It includes both + /// main and auxiliary trace polynomials. The main trace polynomials are casted to + /// polynomials with coefficients over `Self::FieldExtension`. + fn all_trace_polys(&self) -> Vec>> { + let mut trace_polys: Vec<_> = self + .main + .trace_polys + .clone() + .into_iter() + .map(|poly| poly.to_extension()) + .collect(); + + if let Some(aux) = &self.aux { + trace_polys.extend_from_slice(&aux.trace_polys.to_owned()) + } + trace_polys + } +} + +/// A container for the results of the second round of the STARK Prove protocol. pub struct Round2 where - F: IsFFTField, - FieldElement: Serializable, + F: IsField, + FieldElement: Serializable + Sync + Send, { + /// The list of polynomials `Hโ‚€, ..., Hโ‚™` such that `H = โˆ‘แตขXโฑH(Xโฟ)`, where H is the composition polynomial. pub(crate) composition_poly_parts: Vec>>, + /// Evaluations of the composition polynomial parts over the LDE domain. pub(crate) lde_composition_poly_evaluations: Vec>>, + /// The Merkle tree built to compute the commitment to the composition polynomial parts. pub(crate) composition_poly_merkle_tree: BatchedMerkleTree, + /// The commitment to the composition polynomial parts. pub(crate) composition_poly_root: Commitment, } -pub struct Round3 { - trace_ood_evaluations: Vec>>, +/// A container for the results of the third round of the STARK Prove protocol. +pub struct Round3 { + /// Evaluations of the trace polynomials, main ans auxiliary, at the out-of-domain challenge. + trace_ood_evaluations: EvaluationTable, + /// Evaluations of the composition polynomial parts at the out-of-domain challenge. composition_poly_parts_ood_evaluation: Vec>, } -pub struct Round4 { - fri_last_value: FieldElement, +/// A container for the results of the fourth round of the STARK Prove protocol. +pub struct Round4, E: IsField> { + /// The final value resulting from folding the Deep composition polynomial all the way down to a constant value. + fri_last_value: FieldElement, + /// The commitments to the fold polynomials of the inner layers of FRI. fri_layers_merkle_roots: Vec, - deep_poly_openings: DeepPolynomialOpenings, - deep_poly_openings_sym: DeepPolynomialOpenings, - query_list: Vec>, + /// The values and proofs of validity of the evaluations of the trace polynomials and the composition polynomials + /// parts at the domain values corresponding to the FRI query challenges and their symmetric counterparts. + deep_poly_openings: DeepPolynomialOpenings, + /// The values and proofs of validity of the evaluations of the fold polynomials of the inner + /// layers of FRI at the values corresponding to the symmetrics of the FRI query challenges. + query_list: Vec>, + /// The proof of work nonce. nonce: Option, } -pub fn evaluate_polynomial_on_lde_domain( - p: &Polynomial>, + +/// Returns the evaluations of the polynomial `p` over the lde domain defined by the given +/// `blowup_factor`, `domain_size` and `offset`. The number of evaluations returned is `domain_size +/// * blowup_factor`. The domain generator used is the one given by the implementation of `F` as `IsFFTField`. +pub fn evaluate_polynomial_on_lde_domain( + p: &Polynomial>, blowup_factor: usize, domain_size: usize, offset: &FieldElement, -) -> Result>, FFTError> +) -> Result>, FFTError> where - F: IsFFTField, - Polynomial>: FFTPoly, + F: IsFFTField + IsSubFieldOf, + E: IsField, { - let evaluations = p.evaluate_offset_fft(blowup_factor, Some(domain_size), offset)?; + let evaluations = Polynomial::evaluate_offset_fft(p, blowup_factor, Some(domain_size), offset)?; let step = evaluations.len() / (domain_size * blowup_factor); match step { 1 => Ok(evaluations), @@ -98,36 +163,52 @@ where } } -pub trait IsStarkProver { - type Field: IsFFTField; - - fn batch_commit( - vectors: &[Vec>], - ) -> (BatchedMerkleTree, Commitment) +/// The functionality of a STARK prover providing methods to run the STARK Prove protocol +/// https://lambdaclass.github.io/lambdaworks/starks/protocol.html +/// The default implementation is complete and is compatible with Stone prover +/// https://github.com/starkware-libs/stone-prover +pub trait IsStarkProver { + /// Returns the Merkle tree and the commitment to the vectors `vectors`. + fn batch_commit(vectors: &[Vec>]) -> (BatchedMerkleTree, Commitment) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + E: IsSubFieldOf, + A::Field: IsSubFieldOf, { - let tree = BatchedMerkleTree::::build(vectors); + let tree = BatchedMerkleTree::::build(vectors); let commitment = tree.root; (tree, commitment) } + /// Given a `TraceTable`, this method interpolates its columns, computes the commitment to the + /// table and appends it to the transcript. + /// Output: a touple of length 4 with the following: + /// โ€ข The polynomials interpolating the columns of `trace`. + /// โ€ข The evaluations of the above polynomials over the domain `domain`. + /// โ€ข The Merkle tree of evaluations of the above polynomials over the domain `domain`. + /// โ€ข The roots of the above Merkle trees. #[allow(clippy::type_complexity)] - fn interpolate_and_commit( - trace: &TraceTable, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, + fn interpolate_and_commit( + trace: &TraceTable, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, ) -> ( - Vec>>, - Vec>>, - BatchedMerkleTree, + Vec>>, + Vec>>, + BatchedMerkleTree, Commitment, ) where - A: AIR, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + E: IsSubFieldOf, + A::Field: IsSubFieldOf, { - let trace_polys = trace.compute_trace_polys(); + // Interpolate columns of `trace`. + let trace_polys = trace.compute_trace_polys::(); // Evaluate those polynomials t_j on the large domain D_LDE. let lde_trace_evaluations = Self::compute_lde_trace_evaluations(&trace_polys, domain); @@ -138,11 +219,11 @@ pub trait IsStarkProver { in_place_bit_reverse_permute(col); } - // Compute commitments [t_j]. + // Compute commitment. let lde_trace = TraceTable::from_columns(lde_trace_permuted, A::STEP_SIZE); let (lde_trace_merkle_tree, lde_trace_merkle_root) = Self::batch_commit(&lde_trace.rows()); - // >>>> Send commitments: [tโฑผ] + // >>>> Send commitment. transcript.append_bytes(&lde_trace_merkle_root); ( @@ -153,12 +234,17 @@ pub trait IsStarkProver { ) } - fn compute_lde_trace_evaluations( - trace_polys: &[Polynomial>], - domain: &Domain, - ) -> Vec>> + /// Evaluate polynomials `trace_polys` over the domain `domain`. + /// The i-th entry of the returned vector contains the evaluations of the i-th polynomial in `trace_polys`. + fn compute_lde_trace_evaluations( + trace_polys: &[Polynomial>], + domain: &Domain, + ) -> Vec>> where - FieldElement: Send + Sync, + FieldElement: Send + Sync, + FieldElement: Send + Sync, + E: IsSubFieldOf, + A::Field: IsSubFieldOf, { #[cfg(not(feature = "parallel"))] let trace_polys_iter = trace_polys.iter(); @@ -174,55 +260,64 @@ pub trait IsStarkProver { &domain.coset_offset, ) }) - .collect::>>, FFTError>>() + .collect::>>, FFTError>>() .unwrap() } - fn round_1_randomized_air_with_preprocessing( + /// Returns the result of the first round of the STARK Prove protocol. + fn round_1_randomized_air_with_preprocessing( air: &A, - main_trace: &TraceTable, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, - ) -> Result, ProvingError> + main_trace: &TraceTable, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, + ) -> Result, ProvingError> where - A: AIR, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { - let (mut trace_polys, mut evaluations, main_merkle_tree, main_merkle_root) = - Self::interpolate_and_commit::(main_trace, domain, transcript); + let (trace_polys, evaluations, main_merkle_tree, main_merkle_root) = + Self::interpolate_and_commit::(main_trace, domain, transcript); + + let main = Round1CommitmentData:: { + trace_polys, + lde_trace_merkle_tree: main_merkle_tree, + lde_trace_merkle_root: main_merkle_root, + }; let rap_challenges = air.build_rap_challenges(transcript); let aux_trace = air.build_auxiliary_trace(main_trace, &rap_challenges); - - let mut lde_trace_merkle_trees = vec![main_merkle_tree]; - let mut lde_trace_merkle_roots = vec![main_merkle_root]; - if !aux_trace.is_empty() { - // Check that this is valid for interpolation + let (aux, aux_evaluations) = if !aux_trace.is_empty() { let (aux_trace_polys, aux_trace_polys_evaluations, aux_merkle_tree, aux_merkle_root) = - Self::interpolate_and_commit::(&aux_trace, domain, transcript); - trace_polys.extend_from_slice(&aux_trace_polys); - evaluations.extend_from_slice(&aux_trace_polys_evaluations); - lde_trace_merkle_trees.push(aux_merkle_tree); - lde_trace_merkle_roots.push(aux_merkle_root); - } - - let lde_trace = TraceTable::from_columns(evaluations, A::STEP_SIZE); + Self::interpolate_and_commit(&aux_trace, domain, transcript); + let aux_evaluations = aux_trace_polys_evaluations; + let aux = Some(Round1CommitmentData:: { + trace_polys: aux_trace_polys, + lde_trace_merkle_tree: aux_merkle_tree, + lde_trace_merkle_root: aux_merkle_root, + }); + (aux, aux_evaluations) + } else { + (None, Vec::new()) + }; + let lde_table = EvaluationTable::from_columns(evaluations, aux_evaluations, A::STEP_SIZE); Ok(Round1 { - trace_polys, - lde_trace, - lde_trace_merkle_roots, - lde_trace_merkle_trees, + lde_table, + main, + aux, rap_challenges, }) } + /// Returns the Merkle tree and the commitment to the evaluations of the parts of the + /// composition polynomial. fn commit_composition_polynomial( - lde_composition_poly_parts_evaluations: &[Vec>], - ) -> (BatchedMerkleTree, Commitment) + lde_composition_poly_parts_evaluations: &[Vec>], + ) -> (BatchedMerkleTree, Commitment) where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { // TODO: Remove clones let mut lde_composition_poly_evaluations = Vec::new(); @@ -246,31 +341,32 @@ pub trait IsStarkProver { Self::batch_commit(&lde_composition_poly_evaluations_merged) } - fn round_2_compute_composition_polynomial( + /// Returns the result of the second round of the STARK Prove protocol. + fn round_2_compute_composition_polynomial( air: &A, - domain: &Domain, - round_1_result: &Round1, - transition_coefficients: &[FieldElement], - boundary_coefficients: &[FieldElement], - ) -> Round2 + domain: &Domain, + round_1_result: &Round1, + transition_coefficients: &[FieldElement], + boundary_coefficients: &[FieldElement], + ) -> Round2 where - A: AIR + Send + Sync, + A: Send + Sync, A::RAPChallenges: Send + Sync, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { - // Create evaluation table + // Compute the evaluations of the composition polynomial on the LDE domain. let evaluator = ConstraintEvaluator::new(air, &round_1_result.rap_challenges); - let constraint_evaluations = evaluator.evaluate( air, - &round_1_result.lde_trace, + &round_1_result.lde_table, domain, transition_coefficients, boundary_coefficients, &round_1_result.rap_challenges, ); - // Get the composition poly H + // Get coefficients of the composition poly H let composition_poly = Polynomial::interpolate_offset_fft(&constraint_evaluations, &domain.coset_offset) .unwrap(); @@ -302,15 +398,17 @@ pub trait IsStarkProver { } } - fn round_3_evaluate_polynomials_in_out_of_domain_element>( + /// Returns the result of the third round of the STARK Prove protocol. + fn round_3_evaluate_polynomials_in_out_of_domain_element( air: &A, - domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, - z: &FieldElement, - ) -> Round3 + domain: &Domain, + round_1_result: &Round1, + round_2_result: &Round2, + z: &FieldElement, + ) -> Round3 where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let z_power = z.pow(round_2_result.composition_poly_parts.len()); @@ -329,11 +427,18 @@ pub trait IsStarkProver { // // In the fibonacci example, the ood frame is simply the evaluations `[t(z), t(z * g), t(z * g^2)]`, where `t` is the trace // polynomial and `g` is the primitive root of unity used when interpolating `t`. + let trace_ood_evaluations = crate::trace::get_trace_evaluations( - &round_1_result.trace_polys, + &round_1_result.main.trace_polys, + round_1_result + .aux + .as_ref() + .map(|aux| &aux.trace_polys) + .unwrap_or(&vec![]), z, &air.context().transition_offsets, &domain.trace_primitive_root, + A::STEP_SIZE, ); Round3 { @@ -342,20 +447,22 @@ pub trait IsStarkProver { } } - fn round_4_compute_and_run_fri_on_the_deep_composition_polynomial>( + /// Returns the result of the fourth round of the STARK Prove protocol. + fn round_4_compute_and_run_fri_on_the_deep_composition_polynomial( air: &A, - domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, - round_3_result: &Round3, - z: &FieldElement, - transcript: &mut impl IsStarkTranscript, - ) -> Round4 + domain: &Domain, + round_1_result: &Round1, + round_2_result: &Round2, + round_3_result: &Round3, + z: &FieldElement, + transcript: &mut impl IsStarkTranscript, + ) -> Round4 where - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let coset_offset_u64 = air.context().proof_options.coset_offset; - let coset_offset = FieldElement::::from(coset_offset_u64); + let coset_offset = FieldElement::::from(coset_offset_u64); let gamma = transcript.sample_field_element(); let n_terms_composition_poly = round_2_result.lde_composition_poly_evaluations.len(); @@ -377,7 +484,7 @@ pub trait IsStarkProver { // Compute pโ‚€ (deep composition polynomial) let deep_composition_poly = Self::compute_deep_composition_poly( air, - &round_1_result.trace_polys, + &round_1_result.all_trace_polys(), round_2_result, round_3_result, z, @@ -389,7 +496,7 @@ pub trait IsStarkProver { let domain_size = domain.lde_roots_of_unity_coset.len(); // FRI commit and query phases - let (fri_last_value, fri_layers) = fri::commit_phase( + let (fri_last_value, fri_layers) = fri::commit_phase::( domain.root_order as usize, deep_composition_poly, transcript, @@ -416,14 +523,13 @@ pub trait IsStarkProver { .map(|layer| layer.merkle_tree.root) .collect(); - let (deep_poly_openings, deep_poly_openings_sym) = + let deep_poly_openings = Self::open_deep_composition_poly(domain, round_1_result, round_2_result, &iotas); Round4 { fri_last_value, fri_layers_merkle_roots, deep_poly_openings, - deep_poly_openings_sym, query_list, nonce, } @@ -431,8 +537,8 @@ pub trait IsStarkProver { fn sample_query_indexes( number_of_queries: usize, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, ) -> Vec { let domain_size = domain.lde_roots_of_unity_coset.len() as u64; (0..number_of_queries) @@ -444,19 +550,19 @@ pub trait IsStarkProver { /// FRI. This polynomial is a linear combination of the trace polynomial and the /// composition polynomial, with coefficients sampled by the verifier (i.e. using Fiat-Shamir). #[allow(clippy::too_many_arguments)] - fn compute_deep_composition_poly( + fn compute_deep_composition_poly( air: &A, - trace_polys: &[Polynomial>], - round_2_result: &Round2, - round_3_result: &Round3, - z: &FieldElement, - primitive_root: &FieldElement, - composition_poly_gammas: &[FieldElement], - trace_terms_gammas: &[FieldElement], - ) -> Polynomial> + trace_polys: &[Polynomial>], + round_2_result: &Round2, + round_3_result: &Round3, + z: &FieldElement, + primitive_root: &FieldElement, + composition_poly_gammas: &[FieldElement], + trace_terms_gammas: &[FieldElement], + ) -> Polynomial> where - A: AIR, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let z_power = z.pow(round_2_result.composition_poly_parts.len()); @@ -481,7 +587,7 @@ pub trait IsStarkProver { // โˆ‘ โฑผโ‚– [ ๐›พโ‚– ( tโฑผ โˆ’ tโฑผ(z) ) / ( X โˆ’ zgแต )] // @@@ this could be const - let trace_frame_length = trace_frame_evaluations.len(); + let trace_frame_length = trace_frame_evaluations.n_rows(); #[cfg(feature = "parallel")] let trace_terms = trace_polys @@ -495,7 +601,7 @@ pub trait IsStarkProver { (i, t_j), trace_frame_length, trace_terms_gammas, - trace_frame_evaluations, + &trace_frame_evaluations.columns(), transition_offsets, (z, primitive_root), ) @@ -514,7 +620,7 @@ pub trait IsStarkProver { (i, t_j), trace_frame_length, trace_terms_gammas, - trace_frame_evaluations, + &trace_frame_evaluations.columns(), transition_offsets, (z, primitive_root), ) @@ -523,49 +629,52 @@ pub trait IsStarkProver { h_terms + trace_terms } + /// Adds to `accumulator` the term corresponding to the trace polynomial `t_j` of the Deep + /// composition polynomial. That is, returns `accumulator + \sum_i \gamma_i \frac{ t_j - t_j(zg^i) }{ X - zg^i }`, + /// where `i` ranges from `T * j` to `T * j + T - 1`, where `T` is the number of offsets in every frame. fn compute_trace_term( - trace_terms: &Polynomial>, - (i, t_j): (usize, &Polynomial>), + accumulator: &Polynomial>, + (j, t_j): (usize, &Polynomial>), trace_frame_length: usize, - trace_terms_gammas: &[FieldElement], - trace_frame_evaluations: &[Vec>], + trace_terms_gammas: &[FieldElement], + trace_frame_evaluations: &[Vec>], transition_offsets: &[usize], - (z, primitive_root): (&FieldElement, &FieldElement), - ) -> Polynomial> + (z, primitive_root): (&FieldElement, &FieldElement), + ) -> Polynomial> where - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { - let i_times_trace_frame_evaluation = i * trace_frame_length; - let iter_trace_gammas = trace_terms_gammas - .iter() - .skip(i_times_trace_frame_evaluation); - let trace_int = trace_frame_evaluations + let iter_trace_gammas = trace_terms_gammas.iter().skip(j * trace_frame_length); + let trace_int = trace_frame_evaluations[j] .iter() .zip(transition_offsets) .zip(iter_trace_gammas) .fold( Polynomial::zero(), - |trace_agg, ((eval, offset), trace_gamma)| { - // @@@ we can avoid this clone - let t_j_z = &eval[i]; + |trace_agg, ((t_j_z, offset), trace_gamma)| { // @@@ this can be pre-computed - let z_shifted = z * primitive_root.pow(*offset); + let z_shifted = primitive_root.pow(*offset) * z; let mut poly = t_j - t_j_z; poly.ruffini_division_inplace(&z_shifted); trace_agg + poly * trace_gamma }, ); - trace_terms + trace_int + accumulator + trace_int } + /// Computes values and validity proofs of the evaluations of the composition polynomial parts + /// at the domain value corresponding to the FRI query challenge `index` and its symmetric + /// element. fn open_composition_poly( - composition_poly_merkle_tree: &BatchedMerkleTree, - lde_composition_poly_evaluations: &[Vec>], + composition_poly_merkle_tree: &BatchedMerkleTree, + lde_composition_poly_evaluations: &[Vec>], index: usize, - ) -> (Proof, Vec>) + ) -> PolynomialOpenings where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let proof = composition_poly_merkle_tree .get_proof_by_pos(index) @@ -581,112 +690,113 @@ pub trait IsStarkProver { }) .collect(); - (proof, lde_composition_poly_parts_evaluation) + PolynomialOpenings { + proof: proof.clone(), + proof_sym: proof, + evaluations: lde_composition_poly_parts_evaluation + .clone() + .into_iter() + .step_by(2) + .collect(), + evaluations_sym: lde_composition_poly_parts_evaluation + .into_iter() + .skip(1) + .step_by(2) + .collect(), + } } - fn open_trace_polys( - domain: &Domain, - lde_trace_merkle_trees: &[BatchedMerkleTree], - lde_trace: &TraceTable, - index: usize, - ) -> (Vec>, Vec>) + /// Computes values and validity proofs of the evaluations of the trace polynomials + /// at the domain value corresponding to the FRI query challenge `index` and its symmetric + /// element. + fn open_trace_polys( + domain: &Domain, + tree: &BatchedMerkleTree, + lde_trace: &Table, + challenge: usize, + ) -> PolynomialOpenings where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + A::Field: IsSubFieldOf, + E: IsField, { let domain_size = domain.lde_roots_of_unity_coset.len(); - let lde_trace_evaluations = lde_trace - .get_row(reverse_index(index, domain_size as u64)) - .to_vec(); - - // Trace polynomials openings - #[cfg(feature = "parallel")] - let merkle_trees_iter = lde_trace_merkle_trees.par_iter(); - #[cfg(not(feature = "parallel"))] - let merkle_trees_iter = lde_trace_merkle_trees.iter(); - - let lde_trace_merkle_proofs: Vec> = merkle_trees_iter - .map(|tree| tree.get_proof_by_pos(index).unwrap()) - .collect(); - (lde_trace_merkle_proofs, lde_trace_evaluations) + let index = challenge * 2; + let index_sym = challenge * 2 + 1; + PolynomialOpenings { + proof: tree.get_proof_by_pos(index).unwrap(), + proof_sym: tree.get_proof_by_pos(index_sym).unwrap(), + evaluations: lde_trace + .get_row(reverse_index(index, domain_size as u64)) + .to_vec(), + evaluations_sym: lde_trace + .get_row(reverse_index(index_sym, domain_size as u64)) + .to_vec(), + } } - /// Open the deep composition polynomial on a list of indexes - /// and their symmetric elements. - fn open_deep_composition_poly>( - domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, + /// Open the deep composition polynomial on a list of indexes and their symmetric elements. + fn open_deep_composition_poly( + domain: &Domain, + round_1_result: &Round1, + round_2_result: &Round2, indexes_to_open: &[usize], - ) -> ( - DeepPolynomialOpenings, - DeepPolynomialOpenings, - ) + ) -> DeepPolynomialOpenings where - FieldElement: Serializable, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { let mut openings = Vec::new(); - let mut openings_symmetric = Vec::new(); for index in indexes_to_open.iter() { - let (lde_trace_merkle_proofs, lde_trace_evaluations) = Self::open_trace_polys( + let main_trace_opening = Self::open_trace_polys::( domain, - &round_1_result.lde_trace_merkle_trees, - &round_1_result.lde_trace, - index * 2, + &round_1_result.main.lde_trace_merkle_tree, + &round_1_result.lde_table.main_table, + *index, ); - let (lde_trace_sym_merkle_proofs, lde_trace_sym_evaluations) = Self::open_trace_polys( - domain, - &round_1_result.lde_trace_merkle_trees, - &round_1_result.lde_trace, - index * 2 + 1, + let composition_openings = Self::open_composition_poly( + &round_2_result.composition_poly_merkle_tree, + &round_2_result.lde_composition_poly_evaluations, + *index, ); - let (lde_composition_poly_proof, lde_composition_poly_parts_evaluation) = - Self::open_composition_poly( - &round_2_result.composition_poly_merkle_tree, - &round_2_result.lde_composition_poly_evaluations, + let aux_trace_polys = round_1_result.aux.as_ref().map(|aux| { + Self::open_trace_polys::( + domain, + &aux.lde_trace_merkle_tree, + &round_1_result.lde_table.aux_table, *index, - ); - - openings.push(DeepPolynomialOpening { - lde_composition_poly_proof: lde_composition_poly_proof.clone(), - lde_composition_poly_parts_evaluation: lde_composition_poly_parts_evaluation - .clone() - .into_iter() - .step_by(2) - .collect(), - lde_trace_merkle_proofs, - lde_trace_evaluations, + ) }); - openings_symmetric.push(DeepPolynomialOpening { - lde_composition_poly_proof, - lde_composition_poly_parts_evaluation: lde_composition_poly_parts_evaluation - .into_iter() - .skip(1) - .step_by(2) - .collect(), - lde_trace_merkle_proofs: lde_trace_sym_merkle_proofs, - lde_trace_evaluations: lde_trace_sym_evaluations, + openings.push(DeepPolynomialOpening { + composition_poly: composition_openings, + main_trace_polys: main_trace_opening, + aux_trace_polys, }); } - (openings, openings_symmetric) + openings } // FIXME remove unwrap() calls and return errors - fn prove( - main_trace: &TraceTable, + /// Generates a STARK proof for the trace `main_trace` with public inputs `pub_inputs`. + /// Warning: the transcript must be safely initializated before passing it to this method. + fn prove( + main_trace: &TraceTable, pub_inputs: &A::PublicInputs, proof_options: &ProofOptions, - mut transcript: impl IsStarkTranscript, - ) -> Result, ProvingError> + mut transcript: impl IsStarkTranscript, + ) -> Result, ProvingError> where - A: AIR + Send + Sync, + A: Send + Sync, A::RAPChallenges: Send + Sync, - FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, + FieldElement: Serializable + Send + Sync, { info!("Started proof generation..."); #[cfg(feature = "instruments")] @@ -711,7 +821,7 @@ pub trait IsStarkProver { #[cfg(feature = "instruments")] let timer1 = Instant::now(); - let round_1_result = Self::round_1_randomized_air_with_preprocessing::( + let round_1_result = Self::round_1_randomized_air_with_preprocessing( &air, main_trace, &domain, @@ -721,7 +831,12 @@ pub trait IsStarkProver { #[cfg(debug_assertions)] validate_trace( &air, - &round_1_result.trace_polys, + &round_1_result.main.trace_polys, + round_1_result + .aux + .as_ref() + .map(|a| &a.trace_polys) + .unwrap_or(&vec![]), &domain, &round_1_result.rap_challenges, ); @@ -798,9 +913,10 @@ pub trait IsStarkProver { ); // >>>> Send values: tโฑผ(zgแต) - for i in 0..round_3_result.trace_ood_evaluations[0].len() { - for j in 0..round_3_result.trace_ood_evaluations.len() { - transcript.append_field_element(&round_3_result.trace_ood_evaluations[j][i]); + let trace_ood_evaluations_columns = round_3_result.trace_ood_evaluations.columns(); + for col in trace_ood_evaluations_columns.iter() { + for elem in col.iter() { + transcript.append_field_element(elem); } } @@ -856,20 +972,13 @@ pub trait IsStarkProver { info!("End proof generation"); - let trace_ood_evaluations = Table::new( - round_3_result - .trace_ood_evaluations - .into_iter() - .flatten() - .collect(), - round_1_result.trace_polys.len(), - ); - - Ok(StarkProof { - // [tโฑผ] - lde_trace_merkle_roots: round_1_result.lde_trace_merkle_roots, + Ok(StarkProof:: { + // [t] + lde_trace_main_merkle_root: round_1_result.main.lde_trace_merkle_root, + // [t] + lde_trace_aux_merkle_root: round_1_result.aux.map(|x| x.lde_trace_merkle_root), // tโฑผ(zgแต) - trace_ood_evaluations, + trace_ood_evaluations: round_3_result.trace_ood_evaluations, // [Hโ‚] and [Hโ‚‚] composition_poly_root: round_2_result.composition_poly_root, // Hแตข(z^N) @@ -882,9 +991,8 @@ pub trait IsStarkProver { // Open(pโ‚€(Dโ‚€), ๐œโ‚›), Open(pโ‚–(Dโ‚–), โˆ’๐œโ‚›^(2แต)) query_list: round_4_result.query_list, // Open(Hโ‚(D_LDE, ๐œโ‚€), Open(Hโ‚‚(D_LDE, ๐œโ‚€), Open(tโฑผ(D_LDE), ๐œโ‚€) + // Open(Hโ‚(D_LDE, -๐œแตข), Open(Hโ‚‚(D_LDE, -๐œแตข), Open(tโฑผ(D_LDE), -๐œแตข) deep_poly_openings: round_4_result.deep_poly_openings, - // Open(Hโ‚(D_LDE, ๐œโ‚€), Open(Hโ‚‚(D_LDE, ๐œโ‚€), Open(tโฑผ(D_LDE), ๐œโ‚€) - deep_poly_openings_sym: round_4_result.deep_poly_openings_sym, // nonce obtained from grinding nonce: round_4_result.nonce, @@ -964,7 +1072,7 @@ mod tests { for i in 0..(trace_length * blowup_factor) { assert_eq!( domain.lde_roots_of_unity_coset[i], - FieldElement::from(coset_offset) * primitive_root.pow(i) + primitive_root.pow(i) * FieldElement::from(coset_offset) ); } } @@ -973,7 +1081,7 @@ mod tests { fn test_evaluate_polynomial_on_lde_domain_on_trace_polys() { let trace = simple_fibonacci::fibonacci_trace([Felt252::from(1), Felt252::from(1)], 8); let trace_length = trace.n_rows(); - let trace_polys = trace.compute_trace_polys(); + let trace_polys = trace.compute_trace_polys::(); let coset_offset = Felt252::from(3); let blowup_factor: usize = 2; let domain_size = 8; @@ -1017,7 +1125,7 @@ mod tests { } fn proof_parts_stone_compatibility_case_1() -> ( - StarkProof, + StarkProof, fibonacci_2_cols_shifted::PublicInputs, ProofOptions, [u8; 4], @@ -1039,7 +1147,7 @@ mod tests { let transcript_init_seed = [0xca, 0xfe, 0xca, 0xfe]; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -1049,7 +1157,7 @@ mod tests { (proof, pub_inputs, proof_options, transcript_init_seed) } - fn stone_compatibility_case_1_proof() -> StarkProof { + fn stone_compatibility_case_1_proof() -> StarkProof { let (proof, _, _, _) = proof_parts_stone_compatibility_case_1(); proof } @@ -1071,7 +1179,7 @@ mod tests { #[test] fn stone_compatibility_case_1_proof_is_valid() { let (proof, public_inputs, options, seed) = proof_parts_stone_compatibility_case_1(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &public_inputs, &options, @@ -1084,7 +1192,7 @@ mod tests { let proof = stone_compatibility_case_1_proof(); assert_eq!( - proof.lde_trace_merkle_roots[0].to_vec(), + proof.lde_trace_main_merkle_root.to_vec(), decode_hex("0eb9dcc0fb1854572a01236753ce05139d392aa3aeafe72abff150fe21175594").unwrap() ); } @@ -1132,25 +1240,25 @@ mod tests { let proof = stone_compatibility_case_1_proof(); assert_eq!( - proof.trace_ood_evaluations.get_row(0)[0], + proof.trace_ood_evaluations.get_row_main(0)[0], FieldElement::from_hex_unchecked( "70d8181785336cc7e0a0a1078a79ee6541ca0803ed3ff716de5a13c41684037", ) ); assert_eq!( - proof.trace_ood_evaluations.get_row(1)[0], + proof.trace_ood_evaluations.get_row_main(1)[0], FieldElement::from_hex_unchecked( "29808fc8b7480a69295e4b61600480ae574ca55f8d118100940501b789c1630", ) ); assert_eq!( - proof.trace_ood_evaluations.get_row(0)[1], + proof.trace_ood_evaluations.get_row_main(0)[1], FieldElement::from_hex_unchecked( "7d8110f21d1543324cc5e472ab82037eaad785707f8cae3d64c5b9034f0abd2", ) ); assert_eq!( - proof.trace_ood_evaluations.get_row(1)[1], + proof.trace_ood_evaluations.get_row_main(1)[1], FieldElement::from_hex_unchecked( "1b58470130218c122f71399bf1e04cf75a6e8556c4751629d5ce8c02cc4e62d", ) @@ -1248,7 +1356,7 @@ mod tests { // Trace Col 0 assert_eq!( - proof.deep_poly_openings[0].lde_trace_evaluations[0], + proof.deep_poly_openings[0].main_trace_polys.evaluations[0], FieldElement::from_hex_unchecked( "4de0d56f9cf97dff326c26592fbd4ae9ee756080b12c51cfe4864e9b8734f43" ) @@ -1256,7 +1364,7 @@ mod tests { // Trace Col 1 assert_eq!( - proof.deep_poly_openings[0].lde_trace_evaluations[1], + proof.deep_poly_openings[0].main_trace_polys.evaluations[1], FieldElement::from_hex_unchecked( "1bc1aadf39f2faee64d84cb25f7a95d3dceac1016258a39fc90c9d370e69e8e" ) @@ -1264,7 +1372,7 @@ mod tests { // Trace Col 0 symmetric assert_eq!( - proof.deep_poly_openings_sym[0].lde_trace_evaluations[0], + proof.deep_poly_openings[0].main_trace_polys.evaluations_sym[0], FieldElement::from_hex_unchecked( "321f2a9063068310cd93d9a6d042b516118a9f7f4ed3ae301b79b16478cb0c6" ) @@ -1272,7 +1380,7 @@ mod tests { // Trace Col 1 symmetric assert_eq!( - proof.deep_poly_openings_sym[0].lde_trace_evaluations[1], + proof.deep_poly_openings[0].main_trace_polys.evaluations_sym[1], FieldElement::from_hex_unchecked( "643e5520c60d06219b27b34da0856a2c23153efe9da75c6036f362c8f196186" ) @@ -1285,19 +1393,31 @@ mod tests { // Trace poly auth path level 1 assert_eq!( - proof.deep_poly_openings[0].lde_trace_merkle_proofs[0].merkle_path[1].to_vec(), + proof.deep_poly_openings[0] + .main_trace_polys + .proof + .merkle_path[1] + .to_vec(), decode_hex("91b0c0b24b9d00067b0efab50832b76cf97192091624d42b86740666c5d369e6").unwrap() ); // Trace poly auth path level 2 assert_eq!( - proof.deep_poly_openings[0].lde_trace_merkle_proofs[0].merkle_path[2].to_vec(), + proof.deep_poly_openings[0] + .main_trace_polys + .proof + .merkle_path[2] + .to_vec(), decode_hex("993b044db22444c0c0ebf1095b9a51faeb001c9b4dea36abe905f7162620dbbd").unwrap() ); // Trace poly auth path level 3 assert_eq!( - proof.deep_poly_openings[0].lde_trace_merkle_proofs[0].merkle_path[3].to_vec(), + proof.deep_poly_openings[0] + .main_trace_polys + .proof + .merkle_path[3] + .to_vec(), decode_hex("5017abeca33fa82576b5c5c2c61792693b48c9d4414a407eef66b6029dae07ea").unwrap() ); } @@ -1308,14 +1428,14 @@ mod tests { // Composition poly assert_eq!( - proof.deep_poly_openings[0].lde_composition_poly_parts_evaluation[0], + proof.deep_poly_openings[0].composition_poly.evaluations[0], FieldElement::from_hex_unchecked( "2b54852557db698e97253e9d110d60e9bf09f1d358b4c1a96f9f3cf9d2e8755" ) ); // Composition poly sym assert_eq!( - proof.deep_poly_openings_sym[0].lde_composition_poly_parts_evaluation[0], + proof.deep_poly_openings[0].composition_poly.evaluations_sym[0], FieldElement::from_hex_unchecked( "190f1b0acb7858bd3f5285b68befcf32b436a5f1e3a280e1f42565c1f35c2c3" ) @@ -1329,7 +1449,8 @@ mod tests { // Composition poly auth path level 0 assert_eq!( proof.deep_poly_openings[0] - .lde_composition_poly_proof + .composition_poly + .proof .merkle_path[0] .to_vec(), decode_hex("403b75a122eaf90a298e5d3db2cc7ca096db478078122379a6e3616e72da7546").unwrap() @@ -1338,7 +1459,8 @@ mod tests { // Composition poly auth path level 1 assert_eq!( proof.deep_poly_openings[0] - .lde_composition_poly_proof + .composition_poly + .proof .merkle_path[1] .to_vec(), decode_hex("07950888c0355c204a1e83ecbee77a0a6a89f93d41cc2be6b39ddd1e727cc965").unwrap() @@ -1347,7 +1469,8 @@ mod tests { // Composition poly auth path level 2 assert_eq!( proof.deep_poly_openings[0] - .lde_composition_poly_proof + .composition_poly + .proof .merkle_path[2] .to_vec(), decode_hex("58befe2c5de74cc5a002aa82ea219c5b242e761b45fd266eb95521e9f53f44eb").unwrap() @@ -1398,7 +1521,7 @@ mod tests { } fn proof_parts_stone_compatibility_case_2() -> ( - StarkProof, + StarkProof, fibonacci_2_cols_shifted::PublicInputs, ProofOptions, [u8; 4], @@ -1420,7 +1543,7 @@ mod tests { let transcript_init_seed = [0xfa, 0xfa, 0xfa, 0xee]; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, @@ -1430,7 +1553,7 @@ mod tests { (proof, pub_inputs, proof_options, transcript_init_seed) } - fn stone_compatibility_case_2_proof() -> StarkProof { + fn stone_compatibility_case_2_proof() -> StarkProof { let (proof, _, _, _) = proof_parts_stone_compatibility_case_2(); proof } @@ -1454,7 +1577,7 @@ mod tests { let proof = stone_compatibility_case_2_proof(); assert_eq!( - proof.lde_trace_merkle_roots[0].to_vec(), + proof.lde_trace_main_merkle_root.to_vec(), decode_hex("6d31dd00038974bde5fe0c5e3a765f8ddc822a5df3254fca85a1950ae0208cbe").unwrap() ); } diff --git a/provers/stark/src/table.rs b/provers/stark/src/table.rs index c285740892..87d3febc0f 100644 --- a/provers/stark/src/table.rs +++ b/provers/stark/src/table.rs @@ -1,6 +1,9 @@ -use lambdaworks_math::field::{element::FieldElement, traits::IsFFTField}; +use lambdaworks_math::field::{ + element::FieldElement, + traits::{IsField, IsSubFieldOf}, +}; -use crate::{frame::Frame, trace::StepView}; +use crate::trace::StepView; /// A two-dimensional Table holding field elements, arranged in a row-major order. /// This is the basic underlying data structure used for any two-dimensional component in the @@ -8,13 +11,32 @@ use crate::{frame::Frame, trace::StepView}; /// Since this struct is a representation of a two-dimensional table, all rows should have the same /// length. #[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct Table { +pub struct Table { pub data: Vec>, pub width: usize, pub height: usize, } -impl<'t, F: IsFFTField> Table { +/// A view of a contiguos subset of rows of a table. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TableView<'t, F: IsField> { + pub data: &'t [FieldElement], + pub table_row_idx: usize, + pub width: usize, + pub height: usize, +} + +/// A pair of tables corresponding to evaluations of the main and auxiliary traces. +/// It supports main and auxiliary tables taking values in different fields. +/// Both tables must have the same number of rows. +#[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct EvaluationTable, E: IsField> { + pub(crate) main_table: Table, + pub(crate) aux_table: Table, + pub(crate) step_size: usize, +} + +impl<'t, F: IsField> Table { /// Crates a new Table instance from a one-dimensional array in row major order /// and the intended width of the table. pub fn new(data: Vec>, width: usize) -> Self { @@ -122,33 +144,120 @@ impl<'t, F: IsFFTField> Table { let idx = row * self.width + col; &self.data[idx] } +} - /// Given a step size, converts the given table into a `Frame`. - pub fn into_frame(&'t self, step_size: usize) -> Frame<'t, F> { - debug_assert!(self.height % step_size == 0); - let steps = (0..self.height) - .step_by(step_size) - .enumerate() - .map(|(step_idx, row_idx)| { - let table_view = self.table_view(row_idx, step_size); - StepView::new(table_view, step_idx) - }) - .collect(); +impl EvaluationTable { + /// Returns a single vector of elements with the concatenation of the corresponding rows + /// in the main and auxiliary tables. + pub fn get_row(&self, row_idx: usize) -> Vec> { + let mut row: Vec<_> = self.get_row_main(row_idx).to_vec(); + row.extend_from_slice(self.get_row_aux(row_idx)); + row + } - Frame::new(steps) + /// Returns the values of the tables as a single list of columns containing both main and + /// auxiliary tables. The first `self.n_main_cols()` are the columns of the main trace and the + /// rest are the auxiliary table columns. + pub fn columns(&self) -> Vec>> { + let mut columns = self.main_table.columns(); + let aux_columns = self.aux_table.columns(); + columns.extend(aux_columns); + columns } } -/// A view of a contiguos subset of rows of a table. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TableView<'t, F: IsFFTField> { - pub data: &'t [FieldElement], - pub table_row_idx: usize, - pub width: usize, - pub height: usize, +impl, E: IsField> EvaluationTable { + /// Creates an EvaluationTable instance from a vector of columns. + pub fn from_columns( + main_columns: Vec>>, + aux_columns: Vec>>, + step_size: usize, + ) -> Self { + let main_table = Table::from_columns(main_columns); + let aux_table = Table::from_columns(aux_columns); + debug_assert!(aux_table.height == 0 || (main_table.height == aux_table.height)); + Self { + main_table, + aux_table, + step_size, + } + } + + /// Returns the number of columns of the main table. + pub fn n_main_cols(&self) -> usize { + self.main_table.width + } + + /// Returns the number of columns of the auxiliary table. + pub fn n_aux_cols(&self) -> usize { + self.aux_table.width + } + + /// Returns the total number of columns in both tables. + pub fn n_cols(&self) -> usize { + self.n_main_cols() + self.n_aux_cols() + } + + /// Returns the total number of rows. + pub fn n_rows(&self) -> usize { + debug_assert!( + self.aux_table.height == 0 || (self.main_table.height == self.aux_table.height) + ); + self.main_table.height + } + + /// Returns the total number of steps. + pub fn num_steps(&self) -> usize { + debug_assert!((self.main_table.height % self.step_size) == 0); + debug_assert!( + self.aux_table.height == 0 || (self.main_table.height == self.aux_table.height) + ); + self.main_table.height / self.step_size + } + + /// Given a particular step of the computation represented on the trace, + /// returns the row of the underlying table. + pub fn step_to_row(&self, step: usize) -> usize { + self.step_size * step + } + + /// Given a step index, return the step view of the trace for that index + pub fn step_view(&self, step_idx: usize) -> StepView<'_, F, E> { + let row_idx = self.step_to_row(step_idx); + let main_table_view = self.main_table.table_view(row_idx, self.step_size); + let aux_table_view = self.aux_table.table_view(row_idx, self.step_size); + + StepView { + main_table_view, + aux_table_view, + step_idx, + } + } + + /// Given a row and a column index, gives stored value in that position + pub fn get_main(&self, row: usize, col: usize) -> &FieldElement { + self.main_table.get(row, col) + } + + /// Given a row and a column index, gives stored value in that position + pub fn get_aux(&self, row: usize, col: usize) -> &FieldElement { + self.aux_table.get(row, col) + } + + /// Given a row index, returns a reference to that row in the main table as a slice of field elements. + pub fn get_row_main(&self, row_idx: usize) -> &[FieldElement] { + let row_offset = row_idx * self.main_table.width; + &self.main_table.data[row_offset..row_offset + self.main_table.width] + } + + /// Given a row index, returns a reference to that row in the aux table as a slice of field elements. + pub fn get_row_aux(&self, row_idx: usize) -> &[FieldElement] { + let row_offset = row_idx * self.aux_table.width; + &self.aux_table.data[row_offset..row_offset + self.aux_table.width] + } } -impl<'t, F: IsFFTField> TableView<'t, F> { +impl<'t, F: IsField> TableView<'t, F> { pub fn new( data: &'t [FieldElement], table_row_idx: usize, diff --git a/provers/stark/src/tests/integration_tests.rs b/provers/stark/src/tests/integration_tests.rs index c1fd2c7022..9cdafccf62 100644 --- a/provers/stark/src/tests/integration_tests.rs +++ b/provers/stark/src/tests/integration_tests.rs @@ -30,14 +30,14 @@ fn test_prove_fib() { a1: Felt252::one(), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -62,14 +62,14 @@ fn test_prove_fib17() { a1: FE::one(), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -88,14 +88,14 @@ fn test_prove_simple_periodic_8() { a1: Felt252::from(8), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -114,14 +114,14 @@ fn test_prove_simple_periodic_32() { a1: Felt252::from(32768), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -140,14 +140,14 @@ fn test_prove_fib_2_cols() { a1: Felt252::one(), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -168,14 +168,14 @@ fn test_prove_fib_2_cols_shifted() { claimed_index, }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -193,14 +193,14 @@ fn test_prove_quadratic() { a0: Felt252::from(3), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -221,14 +221,14 @@ fn test_prove_rap_fib() { a1: Felt252::one(), }; - let proof = Prover::prove::>( + let proof = Prover::>::prove( &trace, &pub_inputs, &proof_options, StoneProverTranscript::new(&[]), ) .unwrap(); - assert!(Verifier::verify::>( + assert!(Verifier::>::verify( &proof, &pub_inputs, &proof_options, @@ -244,9 +244,9 @@ fn test_prove_dummy() { let proof_options = ProofOptions::default_test_options(); let proof = - Prover::prove::(&trace, &(), &proof_options, StoneProverTranscript::new(&[])) + Prover::::prove(&trace, &(), &proof_options, StoneProverTranscript::new(&[])) .unwrap(); - assert!(Verifier::verify::( + assert!(Verifier::::verify( &proof, &(), &proof_options, diff --git a/provers/stark/src/trace.rs b/provers/stark/src/trace.rs index 7c8d7a333f..e7ef297de4 100644 --- a/provers/stark/src/trace.rs +++ b/provers/stark/src/trace.rs @@ -1,6 +1,6 @@ -use crate::table::{Table, TableView}; +use crate::table::{EvaluationTable, Table, TableView}; use lambdaworks_math::fft::errors::FFTError; -use lambdaworks_math::fft::polynomial::FFTPoly; +use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; use lambdaworks_math::{ field::{element::FieldElement, traits::IsFFTField}, polynomial::Polynomial, @@ -16,12 +16,12 @@ use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; /// STARK protocol, such as the step size (number of consecutive rows of the table) /// of the computation being proven. #[derive(Clone, Default, Debug, PartialEq, Eq)] -pub struct TraceTable { +pub struct TraceTable { pub table: Table, pub step_size: usize, } -impl<'t, F: IsFFTField> TraceTable { +impl TraceTable { pub fn new(data: Vec>, n_columns: usize, step_size: usize) -> Self { let table = Table::new(data, n_columns); Self { table, step_size } @@ -55,17 +55,6 @@ impl<'t, F: IsFFTField> TraceTable { self.step_size * step } - /// Given a step index, return the step view of the trace for that index - pub fn step_view(&'t self, step_idx: usize) -> StepView<'t, F> { - let row_idx = self.step_to_row(step_idx); - let table_view = self.table.table_view(row_idx, self.step_size); - - StepView { - table_view, - step_idx, - } - } - pub fn n_cols(&self) -> usize { self.table.width } @@ -106,12 +95,9 @@ impl<'t, F: IsFFTField> TraceTable { data } - /// Given a row and a column index, gives stored value in that position - pub fn get(&self, row: usize, col: usize) -> &FieldElement { - self.table.get(row, col) - } - - pub fn compute_trace_polys(&self) -> Vec>> + pub fn compute_trace_polys>( + &self, + ) -> Vec>> where FieldElement: Send + Sync, { @@ -121,7 +107,7 @@ impl<'t, F: IsFFTField> TraceTable { #[cfg(not(feature = "parallel"))] let iter = columns.iter(); - iter.map(|col| Polynomial::interpolate_fft(col)) + iter.map(|col| Polynomial::interpolate_fft::(col)) .collect::>>, FFTError>>() .unwrap() } @@ -168,26 +154,41 @@ impl<'t, F: IsFFTField> TraceTable { /// access the steps in a trace, in order to grab elements to calculate /// constraint evaluations. #[derive(Debug, Clone, PartialEq)] -pub struct StepView<'t, F: IsFFTField> { - pub table_view: TableView<'t, F>, +pub struct StepView<'t, F: IsSubFieldOf, E: IsField> { + pub main_table_view: TableView<'t, F>, + pub aux_table_view: TableView<'t, E>, pub step_idx: usize, } -impl<'t, F: IsFFTField> StepView<'t, F> { - pub fn new(table_view: TableView<'t, F>, step_idx: usize) -> Self { +impl<'t, F: IsSubFieldOf, E: IsField> StepView<'t, F, E> { + pub fn new( + main_table_view: TableView<'t, F>, + aux_table_view: TableView<'t, E>, + step_idx: usize, + ) -> Self { StepView { - table_view, + main_table_view, + aux_table_view, step_idx, } } - /// Gets the evaluation element specified by `row_idx` and `col_idx` of this step - pub fn get_evaluation_element(&self, row_idx: usize, col_idx: usize) -> &FieldElement { - self.table_view.get(row_idx, col_idx) + /// Gets the evaluation element of the main table specified by `row_idx` and `col_idx` of this step + pub fn get_main_evaluation_element(&self, row_idx: usize, col_idx: usize) -> &FieldElement { + self.main_table_view.get(row_idx, col_idx) } - pub fn get_row(&self, row_idx: usize) -> &[FieldElement] { - self.table_view.get_row(row_idx) + /// Gets the evaluation element of the aux table specified by `row_idx` and `col_idx` of this step + pub fn get_aux_evaluation_element(&self, row_idx: usize, col_idx: usize) -> &FieldElement { + self.aux_table_view.get(row_idx, col_idx) + } + + pub fn get_row_main(&self, row_idx: usize) -> &[FieldElement] { + self.main_table_view.get_row(row_idx) + } + + pub fn get_row_aux(&self, row_idx: usize) -> &[FieldElement] { + self.aux_table_view.get_row(row_idx) } } @@ -197,22 +198,38 @@ impl<'t, F: IsFFTField> StepView<'t, F> { /// compute a transition. /// Example: For a simple Fibonacci computation, if t(x) is the trace polynomial of /// the computation, this will output evaluations t(x), t(g * x), t(g^2 * z). -pub fn get_trace_evaluations( - trace_polys: &[Polynomial>], - x: &FieldElement, +pub(crate) fn get_trace_evaluations, E: IsField>( + main_trace_polys: &[Polynomial>], + aux_trace_polys: &[Polynomial>], + x: &FieldElement, frame_offsets: &[usize], primitive_root: &FieldElement, -) -> Vec>> { - frame_offsets + step_size: usize, +) -> EvaluationTable { + let evaluation_points: Vec<_> = frame_offsets + .iter() + .map(|offset| primitive_root.pow(*offset) * x) + .collect(); + let main_evaluations = main_trace_polys .iter() - .map(|offset| x * primitive_root.pow(*offset)) - .map(|eval_point| { - trace_polys + .map(|poly| { + evaluation_points .iter() - .map(|poly| poly.evaluate(&eval_point)) - .collect::>>() + .map(|eval_point| poly.evaluate(eval_point)) + .collect() }) - .collect() + .collect(); + let aux_evaluations = aux_trace_polys + .iter() + .map(|poly| { + evaluation_points + .iter() + .map(|eval_point| poly.evaluate(eval_point)) + .collect() + }) + .collect(); + + EvaluationTable::from_columns(main_evaluations, aux_evaluations, step_size) } #[cfg(test)] diff --git a/provers/stark/src/traits.rs b/provers/stark/src/traits.rs index 4755fc12e0..82efa64099 100644 --- a/provers/stark/src/traits.rs +++ b/provers/stark/src/traits.rs @@ -1,7 +1,10 @@ use itertools::Itertools; use lambdaworks_math::{ - fft::{cpu::roots_of_unity::get_powers_of_primitive_root_coset, polynomial::FFTPoly}, - field::{element::FieldElement, traits::IsFFTField}, + fft::cpu::roots_of_unity::get_powers_of_primitive_root_coset, + field::{ + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, + }, polynomial::Polynomial, }; @@ -14,7 +17,8 @@ use super::{ /// AIR is a representation of the Constraints pub trait AIR { - type Field: IsFFTField; + type Field: IsFFTField + IsSubFieldOf; + type FieldExtension: IsField; type RAPChallenges; type PublicInputs; @@ -30,30 +34,46 @@ pub trait AIR { &self, main_trace: &TraceTable, rap_challenges: &Self::RAPChallenges, - ) -> TraceTable; + ) -> TraceTable; fn build_rap_challenges( &self, - transcript: &mut impl IsStarkTranscript, + transcript: &mut impl IsStarkTranscript, ) -> Self::RAPChallenges; fn number_auxiliary_rap_columns(&self) -> usize; fn composition_poly_degree_bound(&self) -> usize; - fn compute_transition( + /// The method called by the prover to evaluate the transitions corresponding to an evaluation frame. + /// In the case of the prover, the main evaluation table of the frame takes values in + /// `Self::Field`, since they are the evaluations of the main trace at the LDE domain. + fn compute_transition_prover( &self, - frame: &Frame, + frame: &Frame, periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, - ) -> Vec>; + ) -> Vec>; + + /// The method called by the verifier to evaluate the transitions at the out of domain frame. + /// In the case of the verifier, both main and auxiliary tables of the evaluation frame take + /// values in `Self::FieldExtension`, since they are the evaluations of the trace polynomials + /// at the out of domain challenge. + /// In case `Self::Field` coincides with `Self::FieldExtension`, this method and + /// `compute_transition_prover` should return the same values. + fn compute_transition_verifier( + &self, + frame: &Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec>; fn boundary_constraints( &self, rap_challenges: &Self::RAPChallenges, - ) -> BoundaryConstraints; + ) -> BoundaryConstraints; - fn transition_exemptions(&self) -> Vec>> { + fn transition_exemptions(&self) -> Vec>> { let trace_length = self.trace_length(); let roots_of_unity_order = trace_length.trailing_zeros(); let roots_of_unity = get_powers_of_primitive_root_coset( @@ -105,8 +125,9 @@ pub trait AIR { fn transition_exemptions_verifier( &self, root: &FieldElement, - ) -> Vec>> { - let x = Polynomial::new_monomial(FieldElement::one(), 1); + ) -> Vec>> { + let x = + Polynomial::>::new_monomial(FieldElement::one(), 1); let max = self .context() @@ -118,7 +139,7 @@ pub trait AIR { .map(|index| { (1..=index).fold( Polynomial::new_monomial(FieldElement::one(), 0), - |acc, k| acc * (&x - root.pow(k)), + |acc, k| acc * (&x - root.pow(k).to_extension()), ) }) .collect() @@ -137,7 +158,9 @@ pub trait AIR { .take(self.trace_length()) .cloned() .collect(); - let poly = Polynomial::interpolate_fft(&values).unwrap(); + let poly = + Polynomial::>::interpolate_fft::(&values) + .unwrap(); result.push(poly); } result diff --git a/provers/stark/src/transcript.rs b/provers/stark/src/transcript.rs index b6952b70bb..84a3bc478a 100644 --- a/provers/stark/src/transcript.rs +++ b/provers/stark/src/transcript.rs @@ -2,31 +2,42 @@ use lambdaworks_math::{ field::{ element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::{IsFFTField, IsField}, + traits::{IsFFTField, IsField, IsSubFieldOf}, }, traits::{ByteConversion, Serializable}, unsigned_integer::element::U256, }; use sha3::{Digest, Keccak256}; +/// The functionality of a transcript to be used in the STARK Prove and Verify protocols. pub trait IsStarkTranscript { + /// Appends a field element to the transcript. fn append_field_element(&mut self, element: &FieldElement); + /// Appends a bytes to the transcript. fn append_bytes(&mut self, new_bytes: &[u8]); + /// Returns the inner state of the transcript that fully determines its outputs. fn state(&self) -> [u8; 32]; + /// Returns a random field element. fn sample_field_element(&mut self) -> FieldElement; + /// Returns a random index between 0 and `upper_bound`. fn sample_u64(&mut self, upper_bound: u64) -> u64; - fn sample_z_ood( + /// Returns a field element not contained in `lde_roots_of_unity_coset` or `trace_roots_of_unity`. + fn sample_z_ood>( &mut self, - lde_roots_of_unity_coset: &[FieldElement], - trace_roots_of_unity: &[FieldElement], + lde_roots_of_unity_coset: &[FieldElement], + trace_roots_of_unity: &[FieldElement], ) -> FieldElement where FieldElement: Serializable, { loop { let value: FieldElement = self.sample_field_element(); - if !lde_roots_of_unity_coset.iter().any(|x| x == &value) - && !trace_roots_of_unity.iter().any(|x| x == &value) + if !lde_roots_of_unity_coset + .iter() + .any(|x| x.clone().to_extension() == value) + && !trace_roots_of_unity + .iter() + .any(|x| x.clone().to_extension() == value) { return value; } @@ -34,6 +45,7 @@ pub trait IsStarkTranscript { } } +/// A transcript implementing `IsStarkTranscript` and compatible with Stone (https://github.com/starkware-libs/stone-prover). pub struct StoneProverTranscript { state: [u8; 32], seed_increment: U256, @@ -42,9 +54,14 @@ pub struct StoneProverTranscript { } impl StoneProverTranscript { + /// The maximum multiple of the modulus of `p` that fits in 256 bits, where + /// `p = 0x800000000000011000000000000000000000000000000000000000000000001` const MODULUS_MAX_MULTIPLE: U256 = U256::from_hex_unchecked( "f80000000000020f00000000000000000000000000000000000000000000001f", ); + + /// The value of `2^{-256} mod p`, where + /// `p = 0x800000000000011000000000000000000000000000000000000000000000001` const R_INV: U256 = U256::from_hex_unchecked( "0x40000000000001100000000000012100000000000000000000000000000000", ); @@ -138,7 +155,7 @@ impl IsStarkTranscript for StoneProverTranscript { while result >= Self::MODULUS_MAX_MULTIPLE { result = self.sample_big_int(); } - FieldElement::new(result) * FieldElement::new(Self::R_INV) + FieldElement::::new(result) * FieldElement::new(Self::R_INV) } fn sample_u64(&mut self, upper_bound: u64) -> u64 { @@ -150,6 +167,7 @@ impl IsStarkTranscript for StoneProverTranscript { } } +/// Returns a batch of size `size` of field elements sampled from the transcript `transcript`. pub fn batch_sample_challenges( size: usize, transcript: &mut impl IsStarkTranscript, diff --git a/provers/stark/src/verifier.rs b/provers/stark/src/verifier.rs index 3bf1f41a36..72fad35585 100644 --- a/provers/stark/src/verifier.rs +++ b/provers/stark/src/verifier.rs @@ -1,3 +1,4 @@ +use std::marker::PhantomData; #[cfg(feature = "instruments")] use std::time::Instant; @@ -6,19 +7,19 @@ use lambdaworks_crypto::merkle_tree::proof::Proof; #[cfg(not(feature = "test_fiat_shamir"))] use log::error; +use crate::{ + config::Commitment, frame::Frame, proof::stark::DeepPolynomialOpening, + transcript::IsStarkTranscript, +}; use lambdaworks_math::{ fft::cpu::bit_reversing::reverse_index, field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsFFTField, + element::FieldElement, + traits::{IsFFTField, IsField, IsSubFieldOf}, }, traits::Serializable, }; -use crate::{ - config::Commitment, proof::stark::DeepPolynomialOpening, transcript::IsStarkTranscript, -}; - use super::{ config::BatchedMerkleTreeBackend, domain::Domain, @@ -28,37 +29,49 @@ use super::{ traits::AIR, }; -pub struct Verifier {} - -impl IsStarkVerifier for Verifier { - type Field = Stark252PrimeField; +/// A default STARK verifier implementing `IsStarkVerifier`. +pub struct Verifier { + phantom: PhantomData, } +impl IsStarkVerifier for Verifier {} + +/// A container holding the complete list of challenges sent to the prover along with the seed used +/// to validate the proof-of-work nonce. pub struct Challenges where - F: IsFFTField, - A: AIR, + F: IsField, + A: AIR, { + /// The out-of-domain challenge. pub z: FieldElement, + /// The composition polynomial coefficients corresponding to the boundary constraints terms. pub boundary_coeffs: Vec>, + /// The composition polynomial coefficients corresponding to the transition constraints terms. pub transition_coeffs: Vec>, + /// The deep composition polynomial coefficients corresponding to the trace polynomial terms. pub trace_term_coeffs: Vec>>, + /// The deep composition polynomial coefficients corresponding to the composition polynomial parts terms. pub gammas: Vec>, + /// The list of FRI commit phase folding challenges. pub zetas: Vec>, + /// The list of FRI query phase index challenges. pub iotas: Vec, + /// The challenges used to build the auxiliary trace. pub rap_challenges: A::RAPChallenges, + /// The seed used to verify the proof-of-work nonce. pub grinding_seed: [u8; 32], } pub type DeepPolynomialEvaluations = (Vec>, Vec>); -pub trait IsStarkVerifier { - type Field: IsFFTField; - +/// The functionality of a STARK verifier providing methods to run the STARK Verify protocol +/// https://lambdaclass.github.io/lambdaworks/starks/protocol.html +pub trait IsStarkVerifier { fn sample_query_indexes( number_of_queries: usize, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, ) -> Vec { let domain_size = domain.lde_roots_of_unity_coset.len() as u64; (0..number_of_queries) @@ -66,27 +79,28 @@ pub trait IsStarkVerifier { .collect::>() } - fn step_1_replay_rounds_and_recover_challenges( + /// Returns the list of challenges sent to the prover. + fn step_1_replay_rounds_and_recover_challenges( air: &A, - proof: &StarkProof, - domain: &Domain, - transcript: &mut impl IsStarkTranscript, - ) -> Challenges + proof: &StarkProof, + domain: &Domain, + transcript: &mut impl IsStarkTranscript, + ) -> Challenges where - FieldElement: Serializable, - A: AIR, + FieldElement: Serializable, + FieldElement: Serializable, { // =================================== // ==========| Round 1 |========== // =================================== // <<<< Receive commitments:[tโฑผ] - transcript.append_bytes(&proof.lde_trace_merkle_roots[0]); + transcript.append_bytes(&proof.lde_trace_main_merkle_root); let rap_challenges = air.build_rap_challenges(transcript); - if let Some(root) = proof.lde_trace_merkle_roots.get(1) { - transcript.append_bytes(root); + if let Some(root) = proof.lde_trace_aux_merkle_root { + transcript.append_bytes(&root); } // =================================== @@ -120,9 +134,10 @@ pub trait IsStarkVerifier { ); // <<<< Receive values: tโฑผ(zgแต) - for i in 0..proof.trace_ood_evaluations.width { - for j in 0..proof.trace_ood_evaluations.height { - transcript.append_field_element(&proof.trace_ood_evaluations.get_row(j)[i]); + let trace_ood_evaluations_columns = proof.trace_ood_evaluations.columns(); + for col in trace_ood_evaluations_columns.iter() { + for elem in col.iter() { + transcript.append_field_element(elem); } } // <<<< Receive value: Hแตข(z^N) @@ -165,7 +180,7 @@ pub trait IsStarkVerifier { transcript.append_bytes(root); element }) - .collect::>>(); + .collect::>>(); // >>>> Send challenge ๐œโ‚™โ‚‹โ‚ zetas.push(transcript.sample_field_element()); @@ -201,15 +216,15 @@ pub trait IsStarkVerifier { } } - fn step_2_verify_claimed_composition_polynomial( + /// Checks whether the purported evaluations of the composition polynomial parts and the trace + /// polynomials at the out-of-domain challenge are consistent. + /// See https://lambdaclass.github.io/lambdaworks/starks/protocol.html#step-2-verify-claimed-composition-polynomial + fn step_2_verify_claimed_composition_polynomial( air: &A, - proof: &StarkProof, - domain: &Domain, - challenges: &Challenges, - ) -> bool - where - A: AIR, - { + proof: &StarkProof, + domain: &Domain, + challenges: &Challenges, + ) -> bool { let boundary_constraints = air.boundary_constraints(&challenges.rap_challenges); let trace_length = air.trace_length(); @@ -217,18 +232,23 @@ pub trait IsStarkVerifier { #[allow(clippy::type_complexity)] let (boundary_c_i_evaluations_num, mut boundary_c_i_evaluations_den): ( - Vec>, - Vec>, + Vec>, + Vec>, ) = (0..number_of_b_constraints) .map(|index| { let step = boundary_constraints.constraints[index].step; + let is_aux = boundary_constraints.constraints[index].is_aux; let point = &domain.trace_primitive_root.pow(step as u64); let trace_idx = boundary_constraints.constraints[index].col; - let trace_evaluation = &proof.trace_ood_evaluations.get_row(0)[trace_idx]; - let boundary_zerofier_challenges_z_den = &challenges.z - point; + let trace_evaluation = if is_aux { + &proof.trace_ood_evaluations.get_row_aux(0)[trace_idx] + } else { + &proof.trace_ood_evaluations.get_row_main(0)[trace_idx] + }; + let boundary_zerofier_challenges_z_den = -point + &challenges.z; let boundary_quotient_ood_evaluation_num = - trace_evaluation - &boundary_constraints.constraints[index].value; + -&boundary_constraints.constraints[index].value + trace_evaluation; ( boundary_quotient_ood_evaluation_num, @@ -241,27 +261,30 @@ pub trait IsStarkVerifier { FieldElement::inplace_batch_inverse(&mut boundary_c_i_evaluations_den).unwrap(); - let boundary_quotient_ood_evaluation: FieldElement = + let boundary_quotient_ood_evaluation: FieldElement = boundary_c_i_evaluations_num .iter() .zip(&boundary_c_i_evaluations_den) .zip(&challenges.boundary_coeffs) .map(|((num, den), beta)| num * den * beta) - .fold(FieldElement::::zero(), |acc, x| acc + x); + .fold(FieldElement::::zero(), |acc, x| acc + x); let periodic_values = air .get_periodic_column_polynomials() .iter() .map(|poly| poly.evaluate(&challenges.z)) - .collect::>>(); + .collect::>>(); - let transition_ood_frame_evaluations = air.compute_transition( - &(proof.trace_ood_evaluations).into_frame(A::STEP_SIZE), + let transition_ood_frame_evaluations = air.compute_transition_verifier( + &Frame::read_from_ood_table( + &proof.trace_ood_evaluations, + &air.context().transition_offsets, + ), &periodic_values, &challenges.rap_challenges, ); - let denominator = (&challenges.z.pow(trace_length) - FieldElement::::one()) + let denominator = (-FieldElement::::one() + &challenges.z.pow(trace_length)) .inv() .unwrap(); @@ -271,7 +294,7 @@ pub trait IsStarkVerifier { ) .iter() .map(|poly| poly.evaluate(&challenges.z)) - .collect::>>(); + .collect::>>(); let unity = &FieldElement::one(); let transition_c_i_evaluations_sum = transition_ood_frame_evaluations @@ -283,7 +306,7 @@ pub trait IsStarkVerifier { .checked_sub(1) .map(|i| &exemption[i]) .unwrap_or(unity); - acc + &denominator * eval * beta * except + acc + eval * &denominator * beta * except }); let composition_poly_ood_evaluation = @@ -300,14 +323,17 @@ pub trait IsStarkVerifier { composition_poly_claimed_ood_evaluation == composition_poly_ood_evaluation } - fn step_3_verify_fri( - proof: &StarkProof, - domain: &Domain, - challenges: &Challenges, + /// Reconstructs the Deep composition polynomial evaluations at the challenge indices values using the provided + /// openings of the trace polynomials and the composition polynomial parts. It then uses these to verify that the + /// FRI decommitments are valid and correspond to the Deep composition polynomial. + fn step_3_verify_fri( + proof: &StarkProof, + domain: &Domain, + challenges: &Challenges, ) -> bool where - FieldElement: Serializable, - A: AIR, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let (deep_poly_evaluations, deep_poly_evaluations_sym) = Self::reconstruct_deep_composition_poly_evaluations_for_all_queries( @@ -319,7 +345,7 @@ pub trait IsStarkVerifier { .iotas .iter() .map(|iota| Self::query_challenge_to_evaluation_point(*iota, domain)) - .collect::>>(); + .collect::>>(); FieldElement::inplace_batch_inverse(&mut evaluation_point_inverse).unwrap(); proof .query_list @@ -328,7 +354,6 @@ pub trait IsStarkVerifier { .zip(evaluation_point_inverse) .enumerate() .fold(true, |mut result, (i, ((proof_s, iota_s), eval))| { - // this is done in constant time result &= Self::verify_query_and_sym_openings( proof, &challenges.zetas, @@ -342,151 +367,157 @@ pub trait IsStarkVerifier { }) } + /// Returns the field element element of the domain `domain` corresponding to the given FRI query index challenge `iota`. fn query_challenge_to_evaluation_point( iota: usize, - domain: &Domain, - ) -> FieldElement { + domain: &Domain, + ) -> FieldElement { domain.lde_roots_of_unity_coset [reverse_index(iota * 2, domain.lde_roots_of_unity_coset.len() as u64)] .clone() } + /// Returns the symmetric field element element of the domain `domain` corresponding to the given FRI query index challenge `iota`. fn query_challenge_to_evaluation_point_sym( iota: usize, - domain: &Domain, - ) -> FieldElement { + domain: &Domain, + ) -> FieldElement { domain.lde_roots_of_unity_coset [reverse_index(iota * 2 + 1, domain.lde_roots_of_unity_coset.len() as u64)] .clone() } - fn verify_opening( + /// Verifies the validity of the opening proof. + fn verify_opening( proof: &Proof, root: &Commitment, index: usize, - value: &[FieldElement], + value: &[FieldElement], ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, + E: IsField, + A::Field: IsSubFieldOf, { - proof.verify::>(root, index, &value.to_owned()) + proof.verify::>(root, index, &value.to_owned()) } /// Verify opening Open(tโฑผ(D_LDE), ๐œ) and Open(tโฑผ(D_LDE), -๐œ) for all trace polynomials tโฑผ, /// where ๐œ and -๐œ are the elements corresponding to the index challenge `iota`. fn verify_trace_openings( - num_main_columns: usize, - proof: &StarkProof, - deep_poly_openings: &DeepPolynomialOpening, - deep_poly_openings_sym: &DeepPolynomialOpening, + proof: &StarkProof, + deep_poly_openings: &DeepPolynomialOpening, iota: usize, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - let lde_trace_evaluations = vec![ - deep_poly_openings.lde_trace_evaluations[..num_main_columns].to_vec(), - deep_poly_openings.lde_trace_evaluations[num_main_columns..].to_vec(), - ]; - let index = iota * 2; - let openings_are_valid = proof - .lde_trace_merkle_roots - .iter() - .zip(&deep_poly_openings.lde_trace_merkle_proofs) - .zip(lde_trace_evaluations) - .fold(true, |acc, ((merkle_root, merkle_proof), evaluation)| { - acc & Self::verify_opening(merkle_proof, merkle_root, index, &evaluation) - }); + let index_sym = iota * 2 + 1; + let mut result = true; - let lde_trace_evaluations_sym = vec![ - deep_poly_openings_sym.lde_trace_evaluations[..num_main_columns].to_vec(), - deep_poly_openings_sym.lde_trace_evaluations[num_main_columns..].to_vec(), - ]; + result &= Self::verify_opening::( + &deep_poly_openings.main_trace_polys.proof, + &proof.lde_trace_main_merkle_root, + index, + &deep_poly_openings.main_trace_polys.evaluations, + ); + result &= Self::verify_opening::( + &deep_poly_openings.main_trace_polys.proof_sym, + &proof.lde_trace_main_merkle_root, + index_sym, + &deep_poly_openings.main_trace_polys.evaluations_sym, + ); - let index_sym = iota * 2 + 1; - let openings_sym_are_valid = proof - .lde_trace_merkle_roots - .iter() - .zip(&deep_poly_openings_sym.lde_trace_merkle_proofs) - .zip(lde_trace_evaluations_sym) - .fold(true, |acc, ((merkle_root, merkle_proof), evaluation)| { - acc & Self::verify_opening(merkle_proof, merkle_root, index_sym, &evaluation) - }); - openings_are_valid & openings_sym_are_valid + match ( + proof.lde_trace_aux_merkle_root, + &deep_poly_openings.aux_trace_polys, + ) { + (None, Some(_)) => result = false, + (Some(_), None) => result = false, + (Some(aux_root), Some(aux_trace_polys_opening)) => { + result &= Self::verify_opening::( + &aux_trace_polys_opening.proof, + &aux_root, + index, + &aux_trace_polys_opening.evaluations, + ); + result &= Self::verify_opening::( + &aux_trace_polys_opening.proof_sym, + &aux_root, + index_sym, + &aux_trace_polys_opening.evaluations_sym, + ); + } + _ => {} + } + + result } /// Verify opening Open(Hแตข(D_LDE), ๐œ) and Open(Hแตข(D_LDE), -๐œ) for all parts Hแตขof the composition /// polynomial, where ๐œ and -๐œ are the elements corresponding to the index challenge `iota`. fn verify_composition_poly_opening( - deep_poly_openings: &DeepPolynomialOpening, - deep_poly_openings_sym: &DeepPolynomialOpening, + deep_poly_openings: &DeepPolynomialOpening, composition_poly_merkle_root: &Commitment, iota: &usize, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - let mut value = deep_poly_openings - .lde_composition_poly_parts_evaluation - .clone(); - value.extend_from_slice(&deep_poly_openings_sym.lde_composition_poly_parts_evaluation); + let mut value = deep_poly_openings.composition_poly.evaluations.clone(); + value.extend_from_slice(&deep_poly_openings.composition_poly.evaluations_sym); deep_poly_openings - .lde_composition_poly_proof - .verify::>( + .composition_poly + .proof + .verify::>( composition_poly_merkle_root, *iota, &value, ) } - fn step_4_verify_trace_and_composition_openings>( - air: &A, - proof: &StarkProof, - challenges: &Challenges, + /// Verifies the validity of the purported values of the trace polynomials and the composition polynomial + /// parts at the domain elements and their symmetric counterparts corresponding to all the FRI query + /// index challenges. + fn step_4_verify_trace_and_composition_openings( + proof: &StarkProof, + challenges: &Challenges, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { - challenges - .iotas - .iter() - .zip(&proof.deep_poly_openings) - .zip(&proof.deep_poly_openings_sym) - .fold( - true, - |mut result, ((iota_n, deep_poly_opening), deep_poly_openings_sym)| { - result &= Self::verify_composition_poly_opening( - deep_poly_opening, - deep_poly_openings_sym, - &proof.composition_poly_root, - iota_n, - ); + challenges.iotas.iter().zip(&proof.deep_poly_openings).fold( + true, + |mut result, (iota_n, deep_poly_opening)| { + result &= Self::verify_composition_poly_opening( + deep_poly_opening, + &proof.composition_poly_root, + iota_n, + ); - let num_main_columns = - air.context().trace_columns - air.number_auxiliary_rap_columns(); - result &= Self::verify_trace_openings( - num_main_columns, - proof, - deep_poly_opening, - deep_poly_openings_sym, - *iota_n, - ); - result - }, - ) + result &= Self::verify_trace_openings(proof, deep_poly_opening, *iota_n); + result + }, + ) } + /// Verifies the openings of a fold polynomial of an inner layer of FRI. fn verify_fri_layer_openings( merkle_root: &Commitment, auth_path_sym: &Proof, - evaluation: &FieldElement, - evaluation_sym: &FieldElement, + evaluation: &FieldElement, + evaluation_sym: &FieldElement, iota: usize, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let evaluations = if iota % 2 == 1 { vec![evaluation_sym.clone(), evaluation.clone()] @@ -494,7 +525,7 @@ pub trait IsStarkVerifier { vec![evaluation.clone(), evaluation_sym.clone()] }; - auth_path_sym.verify::>( + auth_path_sym.verify::>( merkle_root, iota >> 1, &evaluations, @@ -510,19 +541,20 @@ pub trait IsStarkVerifier { /// `deep_composition_evaluation`: precomputed value of pโ‚€(๐œ), where pโ‚€ is the deep composition polynomial. /// `deep_composition_evaluation_sym`: precomputed value of pโ‚€(-๐œ), where pโ‚€ is the deep composition polynomial. fn verify_query_and_sym_openings( - proof: &StarkProof, - zetas: &[FieldElement], + proof: &StarkProof, + zetas: &[FieldElement], iota: usize, - fri_decommitment: &FriDecommitment, - evaluation_point_inv: FieldElement, - deep_composition_evaluation: &FieldElement, - deep_composition_evaluation_sym: &FieldElement, + fri_decommitment: &FriDecommitment, + evaluation_point_inv: FieldElement, + deep_composition_evaluation: &FieldElement, + deep_composition_evaluation_sym: &FieldElement, ) -> bool where - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { let fri_layers_merkle_roots = &proof.fri_layers_merkle_roots; - let evaluation_point_vec: Vec> = + let evaluation_point_vec: Vec> = core::iter::successors(Some(evaluation_point_inv.square()), |evaluation_point| { Some(evaluation_point.square()) }) @@ -534,7 +566,7 @@ pub trait IsStarkVerifier { // Reconstruct pโ‚(๐œยฒ) let mut v = - (p0_eval + p0_eval_sym) + &zetas[0] * (p0_eval - p0_eval_sym) * evaluation_point_inv; + (p0_eval + p0_eval_sym) + evaluation_point_inv * &zetas[0] * (p0_eval - p0_eval_sym); let mut index = iota; // For each FRI layer, starting from the layer 1: use the proof to verify the validity of values pแตข(โˆ’๐œ^(2โฑ)) (given by the prover) and @@ -565,7 +597,7 @@ pub trait IsStarkVerifier { ); // Update `v` with next value pแตขโ‚Šโ‚(๐œ^(2โฑโบยน)). - v = (&v + evaluation_sym) + &zetas[i + 1] * (&v - evaluation_sym) * evaluation_point_inv; + v = (&v + evaluation_sym) + evaluation_point_inv * &zetas[i + 1] * (&v - evaluation_sym); // Update index for next iteration. The index of the squares in the next layer // is obtained by halving the current index. This is due to the bit-reverse @@ -582,19 +614,27 @@ pub trait IsStarkVerifier { ) } - fn reconstruct_deep_composition_poly_evaluations_for_all_queries( - challenges: &Challenges, - domain: &Domain, - proof: &StarkProof, - ) -> DeepPolynomialEvaluations - where - A: AIR, - { + fn reconstruct_deep_composition_poly_evaluations_for_all_queries( + challenges: &Challenges, + domain: &Domain, + proof: &StarkProof, + ) -> DeepPolynomialEvaluations { let mut deep_poly_evaluations = Vec::new(); let mut deep_poly_evaluations_sym = Vec::new(); for (i, iota) in challenges.iotas.iter().enumerate() { let primitive_root = - &Self::Field::get_primitive_root_of_unity(domain.root_order as u64).unwrap(); + &A::Field::get_primitive_root_of_unity(domain.root_order as u64).unwrap(); + + let mut evaluations: Vec> = proof.deep_poly_openings[i] + .main_trace_polys + .evaluations + .clone() + .into_iter() + .map(|x| x.to_extension()) + .collect(); + if let Some(aux_trace_polys) = &proof.deep_poly_openings[i].aux_trace_polys { + evaluations.extend_from_slice(&aux_trace_polys.evaluations); + } let evaluation_point = Self::query_challenge_to_evaluation_point(*iota, domain); deep_poly_evaluations.push(Self::reconstruct_deep_composition_poly_evaluation( @@ -602,48 +642,59 @@ pub trait IsStarkVerifier { &evaluation_point, primitive_root, challenges, - &proof.deep_poly_openings[i].lde_trace_evaluations, - &proof.deep_poly_openings[i].lde_composition_poly_parts_evaluation, + &evaluations, + &proof.deep_poly_openings[i].composition_poly.evaluations, )); + let mut evaluations_sym: Vec> = proof + .deep_poly_openings[i] + .main_trace_polys + .evaluations_sym + .clone() + .into_iter() + .map(|x| x.to_extension()) + .collect(); + if let Some(aux_trace_polys) = &proof.deep_poly_openings[i].aux_trace_polys { + evaluations_sym.extend_from_slice(&aux_trace_polys.evaluations_sym); + } + let evaluation_point = Self::query_challenge_to_evaluation_point_sym(*iota, domain); deep_poly_evaluations_sym.push(Self::reconstruct_deep_composition_poly_evaluation( proof, &evaluation_point, primitive_root, challenges, - &proof.deep_poly_openings_sym[i].lde_trace_evaluations, - &proof.deep_poly_openings_sym[i].lde_composition_poly_parts_evaluation, + &evaluations_sym, + &proof.deep_poly_openings[i].composition_poly.evaluations_sym, )); } (deep_poly_evaluations, deep_poly_evaluations_sym) } - fn reconstruct_deep_composition_poly_evaluation>( - proof: &StarkProof, - evaluation_point: &FieldElement, - primitive_root: &FieldElement, - challenges: &Challenges, - lde_trace_evaluations: &[FieldElement], - lde_composition_poly_parts_evaluation: &[FieldElement], - ) -> FieldElement { - let mut denoms_trace = (0..proof.trace_ood_evaluations.height) - .map(|row_idx| evaluation_point - &challenges.z * primitive_root.pow(row_idx as u64)) - .collect::>>(); + fn reconstruct_deep_composition_poly_evaluation( + proof: &StarkProof, + evaluation_point: &FieldElement, + primitive_root: &FieldElement, + challenges: &Challenges, + lde_trace_evaluations: &[FieldElement], + lde_composition_poly_parts_evaluation: &[FieldElement], + ) -> FieldElement { + let mut denoms_trace = (0..proof.trace_ood_evaluations.n_rows()) + .map(|row_idx| evaluation_point - primitive_root.pow(row_idx as u64) * &challenges.z) + .collect::>>(); FieldElement::inplace_batch_inverse(&mut denoms_trace).unwrap(); - let trace_term = (0..proof.trace_ood_evaluations.width) + let trace_term = (0..proof.trace_ood_evaluations.n_cols()) .zip(&challenges.trace_term_coeffs) .fold(FieldElement::zero(), |trace_terms, (col_idx, coeff_row)| { - let trace_i = (0..proof.trace_ood_evaluations.height).zip(coeff_row).fold( - FieldElement::zero(), - |trace_t, (row_idx, coeff)| { + let trace_i = (0..proof.trace_ood_evaluations.n_rows()) + .zip(coeff_row) + .fold(FieldElement::zero(), |trace_t, (row_idx, coeff)| { let poly_evaluation = (lde_trace_evaluations[col_idx].clone() - proof.trace_ood_evaluations.get_row(row_idx)[col_idx].clone()) * &denoms_trace[row_idx]; trace_t + &poly_evaluation * coeff - }, - ); + }); trace_terms + trace_i }); @@ -662,15 +713,17 @@ pub trait IsStarkVerifier { trace_term + h_terms } - fn verify( - proof: &StarkProof, + /// Verifies a STARK proof with public inputs `pub_inputs`. + /// Warning: the transcript must be safely initializated before passing it to this method. + fn verify( + proof: &StarkProof, pub_input: &A::PublicInputs, proof_options: &ProofOptions, - mut transcript: impl IsStarkTranscript, + mut transcript: impl IsStarkTranscript, ) -> bool where - A: AIR, - FieldElement: Serializable, + FieldElement: Serializable + Sync + Send, + FieldElement: Serializable + Sync + Send, { // Verify there are enough queries if proof.query_list.len() < proof_options.fri_number_of_queries { @@ -746,7 +799,7 @@ pub trait IsStarkVerifier { let timer4 = Instant::now(); #[allow(clippy::let_and_return)] - if !Self::step_4_verify_trace_and_composition_openings(&air, proof, &challenges) { + if !Self::step_4_verify_trace_and_composition_openings(proof, &challenges) { error!("DEEP Composition Polynomial verification failed"); return false; } diff --git a/winterfell_adapter/Cargo.toml b/winterfell_adapter/Cargo.toml index 18d40a14a4..0dcf68df38 100644 --- a/winterfell_adapter/Cargo.toml +++ b/winterfell_adapter/Cargo.toml @@ -5,8 +5,26 @@ edition.workspace = true license.workspace = true [dependencies] -lambdaworks-math = { path = "../math" } -stark-platinum-prover = { path = "../provers/stark" } -winterfell = { git = "https://github.com/facebook/winterfell" } -winter-utils = { git = "https://github.com/facebook/winterfell" } +lambdaworks-math = { path = "../math", features=["winter_compatibility"] } +stark-platinum-prover = { path = "../provers/stark" , features=["winter_compatibility"]} rand = "0.8.5" +winter-air = { package = "winter-air", version = "0.6.4", default-features = false } +winter-prover = { package = "winter-prover", version = "0.6.4", default-features = false } +winter-math = { package = "winter-math", version = "0.6.4", default-features = false } +winter-utils = { package = "winter-utils", version = "0.6.4", default-features = false } +miden-air = { package = "miden-air", version = "0.7", default-features = false } +miden-core = { package = "miden-core" , version = "0.7", default-features = false } +miden-assembly = { package = "miden-assembly", version = "0.7", default-features = false } +miden-processor = { package = "miden-processor", version = "0.7", default-features = false } +sha3 = "0.10" + + +[dev-dependencies] +criterion = { version = "0.4", default-features = false } +miden-prover = { package = "miden-prover", version = "0.7", default-features = false } + + +[[bench]] +name = "proving" +harness = false + diff --git a/winterfell_adapter/README.md b/winterfell_adapter/README.md index 83177559a3..03e6e11f6c 100644 --- a/winterfell_adapter/README.md +++ b/winterfell_adapter/README.md @@ -51,3 +51,16 @@ let proof = Prover::prove::>>( ``` Here `TraceTable` is the Winterfell type that represents your trace table. To check more examples you can see the `examples` folder inside this crate. + +# Benchmarks +To run the fibonacci Miden benchmark run: + +```rust +cargo bench +``` + +To run it with parallelization run: + +```rust +cargo bench --features stark-platinum-prover/parallel,winter-prover/concurrent +``` diff --git a/winterfell_adapter/benches/proving.rs b/winterfell_adapter/benches/proving.rs new file mode 100644 index 0000000000..b82bb284c8 --- /dev/null +++ b/winterfell_adapter/benches/proving.rs @@ -0,0 +1,117 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use miden_air::{HashFunction, ProcessorAir, ProvingOptions, PublicInputs}; +use miden_assembly::Assembler; +use miden_core::{Program, StackInputs}; +use miden_processor::DefaultHost; +use miden_processor::{self as processor}; +use miden_prover::prove; +use stark_platinum_prover::proof::options::ProofOptions; +use stark_platinum_prover::prover::{IsStarkProver, Prover}; +use winter_air::FieldExtension; +use winter_prover::Trace; +use winterfell_adapter::adapter::public_inputs::AirAdapterPublicInputs; +use winterfell_adapter::adapter::QuadFeltTranscript; +use winterfell_adapter::examples::miden_vm::{ExecutionTraceMetadata, MidenVMQuadFeltAir}; + +struct BenchInstance { + program: Program, + stack_inputs: StackInputs, + lambda_proof_options: ProofOptions, +} + +fn create_bench_instance(fibonacci_number: usize) -> BenchInstance { + let program = format!( + "begin + repeat.{} + swap dup.1 add + end + end", + fibonacci_number - 1 + ); + let program = Assembler::default().compile(program).unwrap(); + let stack_inputs = StackInputs::try_from_values([0, 1]).unwrap(); + let mut lambda_proof_options = ProofOptions::default_test_options(); + lambda_proof_options.blowup_factor = 8; + + BenchInstance { + program, + stack_inputs, + lambda_proof_options, + } +} + +pub fn bench_prove_miden_fibonacci(c: &mut Criterion) { + let instance = create_bench_instance(100); + + c.bench_function("winterfell_prover", |b| { + b.iter(|| { + let proving_options = ProvingOptions::new( + instance.lambda_proof_options.fri_number_of_queries, + instance.lambda_proof_options.blowup_factor as usize, + instance.lambda_proof_options.grinding_factor as u32, + FieldExtension::Quadratic, + 2, + 0, + HashFunction::Blake3_192, + ); + + let (_outputs, _proof) = black_box( + prove( + &instance.program, + instance.stack_inputs.clone(), + DefaultHost::default(), + proving_options, + ) + .unwrap(), + ); + }) + }); + + c.bench_function("lambda_prover", |b| { + b.iter(|| { + // This is here because the only pub method in miden + // is a prove function that executes AND proves. + // This makes the benchmark a more fair + // in the case that the program execution takes + // too long. + let winter_trace = processor::execute( + &instance.program, + instance.stack_inputs.clone(), + DefaultHost::default(), + *ProvingOptions::default().execution_options(), + ) + .unwrap(); + + let program_info = winter_trace.program_info().clone(); + let stack_outputs = winter_trace.stack_outputs().clone(); + let pub_inputs = AirAdapterPublicInputs::::new( + PublicInputs::new( + program_info, + instance.stack_inputs.clone(), + stack_outputs.clone(), + ), + vec![2; 182], + vec![0, 1], + winter_trace.get_info(), + winter_trace.clone().into(), + ); + + let trace = MidenVMQuadFeltAir::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + + let _proof = black_box( + Prover::::prove( + &trace, + &pub_inputs, + &instance.lambda_proof_options, + QuadFeltTranscript::new(&[]), + ) + .unwrap(), + ); + }) + }); +} + +criterion_group!(benches, bench_prove_miden_fibonacci); +criterion_main!(benches); diff --git a/winterfell_adapter/src/adapter/air.rs b/winterfell_adapter/src/adapter/air.rs index 9ca8014fec..256deedf8f 100644 --- a/winterfell_adapter/src/adapter/air.rs +++ b/winterfell_adapter/src/adapter/air.rs @@ -1,69 +1,99 @@ -use crate::field_element::element::AdapterFieldElement; -use crate::utils::{matrix_adapter2field, matrix_field2adapter, vec_field2adapter}; -use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, +use crate::utils::{ + matrix_lambda2winter, matrix_winter2lambda, vec_lambda2winter, vec_winter2lambda, }; +use lambdaworks_math::field::element::FieldElement; +use lambdaworks_math::field::traits::{IsFFTField, IsField, IsSubFieldOf}; +use lambdaworks_math::traits::ByteConversion; +use miden_core::Felt; use stark_platinum_prover::{ constraints::boundary::{BoundaryConstraint, BoundaryConstraints}, traits::AIR, }; use std::marker::PhantomData; -use winterfell::{ - Air, AuxTraceRandElements, EvaluationFrame, FieldExtension, ProofOptions, Trace, TraceTable, -}; +use winter_air::{Air, AuxTraceRandElements, EvaluationFrame, FieldExtension, ProofOptions}; +use winter_math::{FieldElement as IsWinterfellFieldElement, StarkField}; +use winter_prover::{ColMatrix, Trace, TraceTable}; use super::public_inputs::AirAdapterPublicInputs; -pub trait FromColumns { - fn from_cols(columns: Vec>) -> Self; +pub trait FromColumns { + fn from_cols(columns: Vec>, metadata: &M) -> Self; } -impl FromColumns for TraceTable { - fn from_cols(columns: Vec>) -> Self { +impl FromColumns for TraceTable { + fn from_cols(columns: Vec>, _: &()) -> Self { TraceTable::init(columns) } } #[derive(Clone)] -pub struct AirAdapter +pub struct AirAdapter where - A: Air, + FE: IsWinterfellFieldElement + + StarkField + + ByteConversion + + Unpin + + IsFFTField + + IsSubFieldOf, + E: IsField, + A: Air, A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, + T: Trace + Clone + FromColumns, + M: Clone, { winterfell_air: A, - public_inputs: AirAdapterPublicInputs, + public_inputs: AirAdapterPublicInputs, air_context: stark_platinum_prover::context::AirContext, - phantom: PhantomData, + trace: PhantomData, + extension: PhantomData, } -impl AirAdapter +impl AirAdapter where - A: Air + Clone, + FE: IsWinterfellFieldElement + + StarkField + + ByteConversion + + Unpin + + IsFFTField + + IsField + + IsSubFieldOf, + E: IsField + IsWinterfellFieldElement, + A: Air + Clone, A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, + T: Trace + Clone + FromColumns, + M: Clone, { pub fn convert_winterfell_trace_table( - trace: TraceTable, - ) -> stark_platinum_prover::trace::TraceTable { + trace: ColMatrix, + ) -> stark_platinum_prover::trace::TraceTable { let mut columns = Vec::new(); - for i in 0..trace.width() { + for i in 0..trace.num_cols() { columns.push(trace.get_column(i).to_owned()); } - stark_platinum_prover::trace::TraceTable::from_columns(matrix_adapter2field(&columns), 1) + stark_platinum_prover::trace::TraceTable::from_columns(matrix_winter2lambda(&columns), 1) } } -impl AIR for AirAdapter +impl AIR for AirAdapter where - A: Air + Clone, + FE: IsWinterfellFieldElement + + StarkField + + ByteConversion + + Unpin + + IsFFTField + + IsField + + IsSubFieldOf, + E: IsField + IsWinterfellFieldElement, + A: Air + Clone, A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, + T: Trace + Clone + FromColumns, + M: Clone, { - type Field = Stark252PrimeField; - type RAPChallenges = Vec; - type PublicInputs = AirAdapterPublicInputs; + type Field = FE; + type FieldExtension = E; + type RAPChallenges = Vec; + type PublicInputs = AirAdapterPublicInputs; const STEP_SIZE: usize = 1; fn new( @@ -99,7 +129,8 @@ where winterfell_air, public_inputs: pub_inputs.clone(), air_context: lambda_context, - phantom: PhantomData, + trace: PhantomData, + extension: PhantomData, } } @@ -107,27 +138,30 @@ where &self, main_trace: &stark_platinum_prover::trace::TraceTable, rap_challenges: &Self::RAPChallenges, - ) -> stark_platinum_prover::trace::TraceTable { + ) -> stark_platinum_prover::trace::TraceTable { // We support at most a one-stage RAP. This covers most use cases. - if let Some(winter_trace) = T::from_cols(matrix_field2adapter(&main_trace.columns())) - .build_aux_segment(&[], rap_challenges) + if let Some(winter_trace) = T::from_cols( + matrix_lambda2winter(&main_trace.columns()), + &self.pub_inputs().metadata, + ) + .build_aux_segment(&[], rap_challenges) { let mut columns = Vec::new(); for i in 0..winter_trace.num_cols() { columns.push(winter_trace.get_column(i).to_owned()); } - stark_platinum_prover::trace::TraceTable::from_columns( - matrix_adapter2field(&columns), + stark_platinum_prover::trace::TraceTable::::from_columns( + matrix_winter2lambda(&columns), 1, ) } else { - stark_platinum_prover::trace::TraceTable::empty() + stark_platinum_prover::trace::TraceTable::::empty() } } fn build_rap_challenges( &self, - transcript: &mut impl stark_platinum_prover::transcript::IsStarkTranscript, + transcript: &mut impl stark_platinum_prover::transcript::IsStarkTranscript, ) -> Self::RAPChallenges { let trace_layout = self.winterfell_air.trace_layout(); let num_segments = trace_layout.num_aux_segments(); @@ -137,7 +171,7 @@ where for _ in 0..trace_layout.get_aux_segment_rand_elements(0) { result.push(transcript.sample_field_element()); } - vec_field2adapter(&result) + vec_lambda2winter(&result) } else if num_segments == 0 { Vec::new() } else { @@ -150,38 +184,46 @@ where } fn composition_poly_degree_bound(&self) -> usize { - self.public_inputs.composition_poly_degree_bound + self.winterfell_air + .context() + .num_constraint_composition_columns() + * self.trace_length() } - fn compute_transition( + fn compute_transition_prover( &self, - frame: &stark_platinum_prover::frame::Frame, - _periodic_values: &[FieldElement], + frame: &stark_platinum_prover::frame::Frame, + periodic_values: &[FieldElement], rap_challenges: &Self::RAPChallenges, - ) -> Vec> { - let num_aux_columns = self.number_auxiliary_rap_columns(); - let num_main_columns = self.context().trace_columns - num_aux_columns; - + ) -> Vec> { let first_step = frame.get_evaluation_step(0); let second_step = frame.get_evaluation_step(1); let main_frame = EvaluationFrame::from_rows( - vec_field2adapter(&first_step.get_row(0)[..num_main_columns]), - vec_field2adapter(&second_step.get_row(0)[..num_main_columns]), + vec_lambda2winter(first_step.get_row_main(0)), + vec_lambda2winter(second_step.get_row_main(0)), ); - let mut main_result = vec![ + let periodic_values = vec_lambda2winter(periodic_values); + + let main_result = vec![ FieldElement::zero(); self.winterfell_air .context() .num_main_transition_constraints() ]; - self.winterfell_air - .evaluate_transition::( - &main_frame, - &[], - &mut vec_field2adapter(&main_result), - ); // Periodic values not supported + + let mut main_result_winter = vec_lambda2winter(&main_result); + self.winterfell_air.evaluate_transition::( + &main_frame, + &periodic_values, + &mut main_result_winter, + ); + + let mut result: Vec<_> = vec_winter2lambda(&main_result_winter) + .into_iter() + .map(|element| element.to_extension()) + .collect(); if self.winterfell_air.trace_layout().num_aux_segments() == 1 { let mut rand_elements = AuxTraceRandElements::new(); @@ -191,40 +233,41 @@ where let second_step = frame.get_evaluation_step(1); let aux_frame = EvaluationFrame::from_rows( - vec_field2adapter(&first_step.get_row(0)[num_main_columns..]), - vec_field2adapter(&second_step.get_row(0)[num_main_columns..]), + vec_lambda2winter(first_step.get_row_aux(0)), + vec_lambda2winter(second_step.get_row_aux(0)), ); - let aux_result = vec![ + let mut aux_result = vec![ FieldElement::zero(); self.winterfell_air .context() .num_aux_transition_constraints() ]; + let mut winter_aux_result = vec_lambda2winter(&aux_result); self.winterfell_air.evaluate_aux_transition( &main_frame, &aux_frame, - &[], + &periodic_values, &rand_elements, - &mut vec_field2adapter(&aux_result), + &mut winter_aux_result, ); - - main_result.extend_from_slice(&aux_result); + aux_result = vec_winter2lambda(&winter_aux_result); + result.extend_from_slice(&aux_result); } - main_result + result } fn boundary_constraints( &self, rap_challenges: &Self::RAPChallenges, - ) -> stark_platinum_prover::constraints::boundary::BoundaryConstraints { + ) -> stark_platinum_prover::constraints::boundary::BoundaryConstraints { let mut result = Vec::new(); for assertion in self.winterfell_air.get_assertions() { assert!(assertion.is_single()); - result.push(BoundaryConstraint::new( + result.push(BoundaryConstraint::new_main( assertion.column(), assertion.first_step(), - assertion.values()[0].0, + FieldElement::::const_from_raw(assertion.values()[0]).to_extension(), )); } @@ -233,10 +276,10 @@ where for assertion in self.winterfell_air.get_aux_assertions(&rand_elements) { assert!(assertion.is_single()); - result.push(BoundaryConstraint::new( + result.push(BoundaryConstraint::new_aux( assertion.column(), assertion.first_step(), - assertion.values()[0].0, + FieldElement::::const_from_raw(assertion.values()[0]), )); } @@ -254,81 +297,72 @@ where fn pub_inputs(&self) -> &Self::PublicInputs { &self.public_inputs } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::examples::fibonacci_2_terms::{self, FibAir2Terms}; - use crate::examples::fibonacci_rap::{self, FibonacciRAP, RapTraceTable}; - use stark_platinum_prover::{ - proof::options::ProofOptions, - prover::{IsStarkProver, Prover}, - transcript::StoneProverTranscript, - verifier::{IsStarkVerifier, Verifier}, - }; - use winterfell::{TraceInfo, TraceLayout}; - - #[test] - fn prove_and_verify_a_winterfell_fibonacci_2_terms_air() { - let lambda_proof_options = ProofOptions::default_test_options(); - let trace = AirAdapter::>::convert_winterfell_trace_table( - fibonacci_2_terms::build_trace(16), - ); - let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: AdapterFieldElement(trace.columns()[1][7]), - transition_exemptions: vec![1, 1], - transition_offsets: vec![0, 1], - composition_poly_degree_bound: 8, - trace_info: TraceInfo::new(2, 8), - }; - let proof = Prover::prove::>>( - &trace, - &pub_inputs, - &lambda_proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::verify::>>( - &proof, - &pub_inputs, - &lambda_proof_options, - StoneProverTranscript::new(&[]), - )); + fn get_periodic_column_values(&self) -> Vec>> { + matrix_winter2lambda(&self.winterfell_air.get_periodic_column_values()) } - #[test] - fn prove_and_verify_a_winterfell_fibonacci_rap_air() { - let lambda_proof_options = ProofOptions::default_test_options(); - let trace = AirAdapter::>::convert_winterfell_trace_table( - fibonacci_rap::build_trace(16), + fn compute_transition_verifier( + &self, + frame: &stark_platinum_prover::frame::Frame, + periodic_values: &[FieldElement], + rap_challenges: &Self::RAPChallenges, + ) -> Vec> { + let first_step = frame.get_evaluation_step(0); + let second_step = frame.get_evaluation_step(1); + + let main_frame = EvaluationFrame::from_rows( + vec_lambda2winter(first_step.get_row_main(0)), + vec_lambda2winter(second_step.get_row_main(0)), ); - let trace_layout = TraceLayout::new(3, [1], [1]); - let trace_info = TraceInfo::new_multi_segment(trace_layout, 16, vec![]); - let fibonacci_result = trace.columns()[1][15]; - let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: AdapterFieldElement(fibonacci_result), - transition_exemptions: vec![1, 1, 1], - transition_offsets: vec![0, 1], - composition_poly_degree_bound: 32, - trace_info, - }; - let proof = Prover::prove::>>( - &trace, - &pub_inputs, - &lambda_proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!( - Verifier::verify::>>( - &proof, - &pub_inputs, - &lambda_proof_options, - StoneProverTranscript::new(&[]), - ) + let periodic_values = vec_lambda2winter(periodic_values); + + let main_result = vec![ + FieldElement::zero(); + self.winterfell_air + .context() + .num_main_transition_constraints() + ]; + + let mut main_result_winter = vec_lambda2winter(&main_result); + self.winterfell_air.evaluate_transition::( + &main_frame, + &periodic_values, + &mut main_result_winter, ); + + let mut result: Vec> = vec_winter2lambda(&main_result_winter); + + if self.winterfell_air.trace_layout().num_aux_segments() == 1 { + let mut rand_elements = AuxTraceRandElements::new(); + rand_elements.add_segment_elements(rap_challenges.clone()); + + let first_step = frame.get_evaluation_step(0); + let second_step = frame.get_evaluation_step(1); + + let aux_frame = EvaluationFrame::from_rows( + vec_lambda2winter(first_step.get_row_aux(0)), + vec_lambda2winter(second_step.get_row_aux(0)), + ); + + let mut aux_result = vec![ + FieldElement::zero(); + self.winterfell_air + .context() + .num_aux_transition_constraints() + ]; + let mut winter_aux_result = vec_lambda2winter(&aux_result); + self.winterfell_air.evaluate_aux_transition( + &main_frame, + &aux_frame, + &periodic_values, + &rand_elements, + &mut winter_aux_result, + ); + aux_result = vec_winter2lambda(&winter_aux_result); + result.extend_from_slice(&aux_result); + } + result } } diff --git a/winterfell_adapter/src/adapter/mod.rs b/winterfell_adapter/src/adapter/mod.rs index 3987d97069..365f0850df 100644 --- a/winterfell_adapter/src/adapter/mod.rs +++ b/winterfell_adapter/src/adapter/mod.rs @@ -1,2 +1,87 @@ +use lambdaworks_math::{field::fields::winterfell::QuadFelt, traits::ByteConversion}; +use miden_core::Felt; +use sha3::{Digest, Keccak256}; +use stark_platinum_prover::{fri::FieldElement, transcript::IsStarkTranscript}; +use winter_math::StarkField; + pub mod air; pub mod public_inputs; + +pub struct FeltTranscript { + hasher: Keccak256, +} + +impl FeltTranscript { + pub fn new(data: &[u8]) -> Self { + let mut res = Self { + hasher: Keccak256::new(), + }; + res.append_bytes(data); + res + } +} + +impl IsStarkTranscript for FeltTranscript { + fn append_field_element(&mut self, element: &FieldElement) { + self.append_bytes(&element.value().to_bytes_be()); + } + + fn append_bytes(&mut self, new_bytes: &[u8]) { + self.hasher.update(&mut new_bytes.to_owned()); + } + + fn state(&self) -> [u8; 32] { + self.hasher.clone().finalize().into() + } + + fn sample_field_element(&mut self) -> FieldElement { + let mut bytes = self.state()[..8].try_into().unwrap(); + let mut x = u64::from_be_bytes(bytes); + while x >= Felt::MODULUS { + self.append_bytes(&bytes); + bytes = self.state()[..8].try_into().unwrap(); + x = u64::from_be_bytes(bytes); + } + FieldElement::const_from_raw(Felt::new(x)) + } + + fn sample_u64(&mut self, upper_bound: u64) -> u64 { + u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound + } +} + +pub struct QuadFeltTranscript { + felt_transcript: FeltTranscript, +} + +impl QuadFeltTranscript { + pub fn new(data: &[u8]) -> Self { + Self { + felt_transcript: FeltTranscript::new(data), + } + } +} + +impl IsStarkTranscript for QuadFeltTranscript { + fn append_field_element(&mut self, element: &FieldElement) { + self.append_bytes(&element.value().to_bytes_be()); + } + + fn append_bytes(&mut self, new_bytes: &[u8]) { + self.felt_transcript.append_bytes(new_bytes); + } + + fn state(&self) -> [u8; 32] { + self.felt_transcript.state() + } + + fn sample_field_element(&mut self) -> FieldElement { + let x = self.felt_transcript.sample_field_element(); + let y = self.felt_transcript.sample_field_element(); + FieldElement::const_from_raw(QuadFelt::new(*x.value(), *y.value())) + } + + fn sample_u64(&mut self, upper_bound: u64) -> u64 { + u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound + } +} diff --git a/winterfell_adapter/src/adapter/public_inputs.rs b/winterfell_adapter/src/adapter/public_inputs.rs index fc463ebb53..fc5e49ba9f 100644 --- a/winterfell_adapter/src/adapter/public_inputs.rs +++ b/winterfell_adapter/src/adapter/public_inputs.rs @@ -1,15 +1,38 @@ -use crate::field_element::element::AdapterFieldElement; -use winterfell::{Air, TraceInfo}; +use winter_air::{Air, TraceInfo}; #[derive(Clone)] -pub struct AirAdapterPublicInputs +pub struct AirAdapterPublicInputs where - A: Air, + A: Air, A::PublicInputs: Clone, + M: Clone, { pub(crate) winterfell_public_inputs: A::PublicInputs, pub(crate) transition_exemptions: Vec, pub(crate) transition_offsets: Vec, pub(crate) trace_info: TraceInfo, - pub(crate) composition_poly_degree_bound: usize, + pub(crate) metadata: M, +} + +impl AirAdapterPublicInputs +where + A: Air, + A::PublicInputs: Clone, + M: Clone, +{ + pub fn new( + winterfell_public_inputs: A::PublicInputs, + transition_exemptions: Vec, + transition_offsets: Vec, + trace_info: TraceInfo, + metadata: M, + ) -> Self { + Self { + winterfell_public_inputs, + transition_exemptions, + transition_offsets, + trace_info, + metadata, + } + } } diff --git a/winterfell_adapter/src/examples/cubic.rs b/winterfell_adapter/src/examples/cubic.rs new file mode 100644 index 0000000000..b250abea1d --- /dev/null +++ b/winterfell_adapter/src/examples/cubic.rs @@ -0,0 +1,121 @@ +use miden_core::Felt; +use winter_air::{ + Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, + TransitionConstraintDegree, +}; +use winter_math::FieldElement as IsWinterfellFieldElement; +use winter_prover::TraceTable; + +/// A fibonacci winterfell AIR example. Two terms are computed +/// at each step. This was taken from the original winterfell +/// repository and adapted to work with lambdaworks. +#[derive(Clone)] +pub struct Cubic { + context: AirContext, + result: Felt, +} + +impl Air for Cubic { + type BaseField = Felt; + type PublicInputs = Felt; + + fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { + let degrees = vec![TransitionConstraintDegree::new(3)]; + Cubic { + context: AirContext::new(trace_info, degrees, 2, options), + result: pub_inputs, + } + } + + fn context(&self) -> &AirContext { + &self.context + } + + fn evaluate_transition>( + &self, + frame: &EvaluationFrame, + _periodic_values: &[E], + result: &mut [E], + ) { + let current = frame.current(); + let next = frame.next(); + + // s_i = (s_{i-1})ยณ + result[0] = next[0] - (current[0] * current[0] * current[0]); + } + + fn get_assertions(&self) -> Vec> { + // A valid Fibonacci sequence should start with two ones and terminate with + // the expected result + let last_step = self.trace_length() - 1; + vec![ + Assertion::single(0, 0, Self::BaseField::from(2u16)), + Assertion::single(0, last_step, self.result), + ] + } +} + +pub fn build_trace(sequence_length: usize) -> TraceTable { + assert!( + sequence_length.is_power_of_two(), + "sequence length must be a power of 2" + ); + + let mut accum = Felt::from(2u16); + let mut column = vec![accum]; + while column.len() < sequence_length { + accum = accum * accum * accum; + column.push(accum); + } + TraceTable::init(vec![column]) +} + +#[cfg(test)] +mod tests { + use miden_core::Felt; + use stark_platinum_prover::{ + proof::options::ProofOptions, + prover::{IsStarkProver, Prover}, + verifier::{IsStarkVerifier, Verifier}, + }; + use winter_air::TraceInfo; + use winter_prover::{Trace, TraceTable}; + + use crate::{ + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, FeltTranscript}, + examples::cubic::{self, Cubic}, + }; + + #[test] + fn prove_and_verify_a_winterfell_cubic_air() { + let lambda_proof_options = ProofOptions::default_test_options(); + let winter_trace = cubic::build_trace(16); + let trace = + AirAdapter::, Felt, Felt, ()>::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + let pub_inputs = AirAdapterPublicInputs { + winterfell_public_inputs: *trace.columns()[0][15].value(), + transition_exemptions: vec![1], + transition_offsets: vec![0, 1], + trace_info: TraceInfo::new(1, 16), + metadata: (), + }; + + let proof = Prover::, Felt, Felt, _>>::prove( + &trace, + &pub_inputs, + &lambda_proof_options, + FeltTranscript::new(&[]), + ) + .unwrap(); + assert!( + Verifier::, Felt, Felt, _>>::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + FeltTranscript::new(&[]), + ) + ); + } +} diff --git a/winterfell_adapter/src/examples/fibonacci_2_terms.rs b/winterfell_adapter/src/examples/fibonacci_2_terms.rs index c86296ee22..227f8ab2ae 100644 --- a/winterfell_adapter/src/examples/fibonacci_2_terms.rs +++ b/winterfell_adapter/src/examples/fibonacci_2_terms.rs @@ -1,24 +1,23 @@ -use lambdaworks_math::field::element::FieldElement; -use winterfell::math::FieldElement as IsWinterfellFieldElement; -use winterfell::{ - Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, TraceTable, +use miden_core::Felt; +use winter_air::{ + Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, TransitionConstraintDegree, }; - -use crate::field_element::element::AdapterFieldElement; +use winter_math::FieldElement as IsWinterfellFieldElement; +use winter_prover::TraceTable; /// A fibonacci winterfell AIR example. Two terms are computed /// at each step. This was taken from the original winterfell /// repository and adapted to work with lambdaworks. #[derive(Clone)] pub struct FibAir2Terms { - context: AirContext, - result: AdapterFieldElement, + context: AirContext, + result: Felt, } impl Air for FibAir2Terms { - type BaseField = AdapterFieldElement; - type PublicInputs = AdapterFieldElement; + type BaseField = Felt; + type PublicInputs = Felt; fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { let degrees = vec![ @@ -63,7 +62,7 @@ impl Air for FibAir2Terms { } } -pub fn build_trace(sequence_length: usize) -> TraceTable { +pub fn build_trace(sequence_length: usize) -> TraceTable { assert!( sequence_length.is_power_of_two(), "sequence length must be a power of 2" @@ -72,8 +71,8 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { let mut trace = TraceTable::new(2, sequence_length / 2); trace.fill( |state| { - state[0] = AdapterFieldElement(FieldElement::one()); - state[1] = AdapterFieldElement(FieldElement::one()); + state[0] = Felt::ONE; + state[1] = Felt::ONE; }, |_, state| { state[0] += state[1]; @@ -83,3 +82,54 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { trace } + +#[cfg(test)] +mod tests { + use miden_core::Felt; + use stark_platinum_prover::{ + proof::options::ProofOptions, + prover::{IsStarkProver, Prover}, + verifier::{IsStarkVerifier, Verifier}, + }; + use winter_air::TraceInfo; + use winter_prover::{Trace, TraceTable}; + + use crate::{ + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, FeltTranscript}, + examples::fibonacci_2_terms::{self, FibAir2Terms}, + }; + + #[test] + fn prove_and_verify_a_winterfell_fibonacci_2_terms_air() { + let lambda_proof_options = ProofOptions::default_test_options(); + let winter_trace = fibonacci_2_terms::build_trace(16); + let trace = + AirAdapter::, Felt, Felt, ()>::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + let pub_inputs = AirAdapterPublicInputs { + winterfell_public_inputs: *trace.columns()[1][7].value(), + transition_exemptions: vec![1, 1], + transition_offsets: vec![0, 1], + trace_info: TraceInfo::new(2, 8), + metadata: (), + }; + + let proof = Prover::, Felt, Felt, _>>::prove( + &trace, + &pub_inputs, + &lambda_proof_options, + FeltTranscript::new(&[]), + ) + .unwrap(); + + assert!(Verifier::< + AirAdapter, Felt, Felt, _>, + >::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + FeltTranscript::new(&[]), + )); + } +} diff --git a/winterfell_adapter/src/examples/fibonacci_rap.rs b/winterfell_adapter/src/examples/fibonacci_rap.rs index 3b9155e6d2..7315c3d099 100644 --- a/winterfell_adapter/src/examples/fibonacci_rap.rs +++ b/winterfell_adapter/src/examples/fibonacci_rap.rs @@ -1,16 +1,14 @@ use crate::adapter::air::FromColumns; -use crate::field_element::element::AdapterFieldElement; -use crate::utils::vec_field2adapter; -use lambdaworks_math::field::element::FieldElement; +use miden_core::Felt; use rand::seq::SliceRandom; use rand::thread_rng; -use winter_utils::{collections::Vec, uninit_vector}; -use winterfell::math::FieldElement as IsWinterfellFieldElement; -use winterfell::{math::StarkField, matrix::ColMatrix, Trace, TraceLayout}; -use winterfell::{ - Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, TraceTable, - TransitionConstraintDegree, +use winter_air::{ + Air, AirContext, Assertion, AuxTraceRandElements, EvaluationFrame, ProofOptions, TraceInfo, + TraceLayout, TransitionConstraintDegree, }; +use winter_math::{ExtensionOf, FieldElement as IsWinterfellFieldElement, StarkField}; +use winter_prover::{ColMatrix, Trace, TraceTable}; +use winter_utils::{collections::Vec, uninit_vector}; #[derive(Clone)] pub struct RapTraceTable { @@ -132,21 +130,21 @@ impl Trace for RapTraceTable { } } -impl FromColumns for RapTraceTable { - fn from_cols(columns: Vec>) -> Self { +impl FromColumns for RapTraceTable { + fn from_cols(columns: Vec>, _: &()) -> Self { RapTraceTable::init(columns) } } #[derive(Clone)] pub struct FibonacciRAP { - context: AirContext, - result: AdapterFieldElement, + context: AirContext, + result: Felt, } impl Air for FibonacciRAP { - type BaseField = AdapterFieldElement; - type PublicInputs = AdapterFieldElement; + type BaseField = Felt; + type PublicInputs = Felt; fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { let degrees = vec![ @@ -183,11 +181,11 @@ impl Air for FibonacciRAP { main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], - aux_rand_elements: &winterfell::AuxTraceRandElements, + aux_rand_elements: &AuxTraceRandElements, result: &mut [E], ) where F: IsWinterfellFieldElement, - E: IsWinterfellFieldElement + winterfell::math::ExtensionOf, + E: IsWinterfellFieldElement + ExtensionOf, { let gamma = aux_rand_elements.get_segment_elements(0)[0]; let curr_aux = aux_frame.current(); @@ -211,20 +209,20 @@ impl Air for FibonacciRAP { fn get_aux_assertions>( &self, - _aux_rand_elements: &winterfell::AuxTraceRandElements, + _aux_rand_elements: &AuxTraceRandElements, ) -> Vec> { let last_step = self.trace_length() - 1; - vec![Assertion::single(3, last_step, Self::BaseField::ONE.into())] + vec![Assertion::single(0, last_step, Self::BaseField::ONE.into())] } } -pub fn build_trace(sequence_length: usize) -> TraceTable { +pub fn build_trace(sequence_length: usize) -> TraceTable { assert!( sequence_length.is_power_of_two(), "sequence length must be a power of 2" ); - let mut fibonacci = vec![FieldElement::one(), FieldElement::one()]; + let mut fibonacci = vec![Felt::ONE, Felt::ONE]; for i in 2..(sequence_length + 1) { fibonacci.push(fibonacci[i - 2] + fibonacci[i - 1]) } @@ -234,8 +232,62 @@ pub fn build_trace(sequence_length: usize) -> TraceTable { permuted.shuffle(&mut rng); TraceTable::init(vec![ - vec_field2adapter(&fibonacci[..fibonacci.len() - 1]), - vec_field2adapter(&fibonacci[1..]), - vec_field2adapter(&permuted), + fibonacci[..fibonacci.len() - 1].to_vec(), + fibonacci[1..].to_vec(), + permuted, ]) } + +#[cfg(test)] +mod tests { + use lambdaworks_math::field::fields::winterfell::QuadFelt; + use miden_core::Felt; + use stark_platinum_prover::{ + proof::options::ProofOptions, + prover::{IsStarkProver, Prover}, + verifier::{IsStarkVerifier, Verifier}, + }; + use winter_air::{TraceInfo, TraceLayout}; + use winter_prover::Trace; + + use crate::{ + adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, QuadFeltTranscript}, + examples::fibonacci_rap::{self, FibonacciRAP, RapTraceTable}, + }; + + #[test] + fn prove_and_verify_a_winterfell_fibonacci_rap_air() { + let lambda_proof_options = ProofOptions::default_test_options(); + let winter_trace = fibonacci_rap::build_trace(16); + let trace = + AirAdapter::, Felt, QuadFelt, ()>::convert_winterfell_trace_table( + winter_trace.main_segment().clone(), + ); + let trace_layout = TraceLayout::new(3, [1], [1]); + let trace_info = TraceInfo::new_multi_segment(trace_layout, 16, vec![]); + let fibonacci_result = trace.columns()[1][15]; + let pub_inputs = AirAdapterPublicInputs:: { + winterfell_public_inputs: *fibonacci_result.value(), + transition_exemptions: vec![1, 1, 1], + transition_offsets: vec![0, 1], + trace_info, + metadata: (), + }; + + let proof = Prover::, Felt, QuadFelt, _>>::prove( + &trace, + &pub_inputs, + &lambda_proof_options, + QuadFeltTranscript::new(&[]), + ) + .unwrap(); + assert!(Verifier::< + AirAdapter, Felt, QuadFelt, _>, + >::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + QuadFeltTranscript::new(&[]), + )); + } +} diff --git a/winterfell_adapter/src/examples/miden_vm.rs b/winterfell_adapter/src/examples/miden_vm.rs new file mode 100644 index 0000000000..89208f7876 --- /dev/null +++ b/winterfell_adapter/src/examples/miden_vm.rs @@ -0,0 +1,188 @@ +use lambdaworks_math::field::fields::winterfell::QuadFelt; +use miden_air::ProcessorAir; +use miden_core::{Felt, ProgramInfo, StackOutputs}; +use miden_processor::{AuxTraceHints, ExecutionTrace, TraceLenSummary}; +use winter_air::TraceLayout; +use winter_prover::ColMatrix; + +use crate::adapter::air::{AirAdapter, FromColumns}; + +pub type MidenVMQuadFeltAir = + AirAdapter; + +#[derive(Clone)] +pub struct ExecutionTraceMetadata { + meta: Vec, + layout: TraceLayout, + aux_trace_hints: AuxTraceHints, + program_info: ProgramInfo, + stack_outputs: StackOutputs, + trace_len_summary: TraceLenSummary, +} + +impl From for ExecutionTraceMetadata { + fn from(value: ExecutionTrace) -> Self { + Self { + meta: value.meta, + layout: value.layout, + aux_trace_hints: value.aux_trace_hints, + program_info: value.program_info, + stack_outputs: value.stack_outputs, + trace_len_summary: value.trace_len_summary, + } + } +} + +impl FromColumns for ExecutionTrace { + fn from_cols(columns: Vec>, metadata: &ExecutionTraceMetadata) -> Self { + ExecutionTrace { + meta: metadata.meta.clone(), + layout: metadata.layout.clone(), + main_trace: ColMatrix::new(columns), + aux_trace_hints: metadata.aux_trace_hints.clone(), + program_info: metadata.program_info.clone(), + stack_outputs: metadata.stack_outputs.clone(), + trace_len_summary: metadata.trace_len_summary, + } + } +} + +#[cfg(test)] +mod tests { + use crate::adapter::public_inputs::AirAdapterPublicInputs; + use crate::adapter::QuadFeltTranscript; + use crate::examples::miden_vm::MidenVMQuadFeltAir; + use miden_air::{ProvingOptions, PublicInputs}; + use miden_assembly::Assembler; + use miden_core::{Felt, StackInputs}; + use miden_processor::DefaultHost; + use miden_processor::{self as processor}; + use stark_platinum_prover::prover::Prover; + use stark_platinum_prover::verifier::Verifier; + use stark_platinum_prover::{ + proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, + }; + use winter_math::{FieldElement, StarkField}; + use winter_prover::Trace; + + #[test] + fn prove_and_verify_miden_readme_example() { + let mut lambda_proof_options = ProofOptions::default_test_options(); + lambda_proof_options.blowup_factor = 32; + let assembler = Assembler::default(); + + let program = assembler.compile("begin push.3 push.5 add end").unwrap(); + + let winter_trace = processor::execute( + &program, + StackInputs::default(), + DefaultHost::default(), + *ProvingOptions::default().execution_options(), + ) + .unwrap(); + let program_info = winter_trace.program_info().clone(); + let stack_outputs = winter_trace.stack_outputs().clone(); + + let pub_inputs = PublicInputs::new(program_info, StackInputs::default(), stack_outputs); + + let pub_inputs = AirAdapterPublicInputs { + winterfell_public_inputs: pub_inputs, + transition_exemptions: vec![2; 182], + transition_offsets: vec![0, 1], + trace_info: winter_trace.get_info(), + metadata: winter_trace.clone().into(), + }; + + let trace = + MidenVMQuadFeltAir::convert_winterfell_trace_table(winter_trace.main_segment().clone()); + + let proof = Prover::::prove( + &trace, + &pub_inputs, + &lambda_proof_options, + QuadFeltTranscript::new(&[]), + ) + .unwrap(); + + assert!(Verifier::::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + QuadFeltTranscript::new(&[]), + )); + } + + fn compute_fibonacci(n: usize) -> Felt { + let mut t0 = Felt::ZERO; + let mut t1 = Felt::ONE; + + for _ in 0..n { + t1 = t0 + t1; + core::mem::swap(&mut t0, &mut t1); + } + t0 + } + + #[test] + fn prove_and_verify_miden_fibonacci() { + let fibonacci_number = 16; + let program = format!( + "begin + repeat.{} + swap dup.1 add + end + end", + fibonacci_number - 1 + ); + let program = Assembler::default().compile(program).unwrap(); + let expected_result = vec![compute_fibonacci(fibonacci_number).as_int()]; + let stack_inputs = StackInputs::try_from_values([0, 1]).unwrap(); + + let mut lambda_proof_options = ProofOptions::default_test_options(); + lambda_proof_options.blowup_factor = 8; + + let winter_trace = processor::execute( + &program, + stack_inputs.clone(), + DefaultHost::default(), + *ProvingOptions::default().execution_options(), + ) + .unwrap(); + let program_info = winter_trace.program_info().clone(); + let stack_outputs = winter_trace.stack_outputs().clone(); + + let pub_inputs = PublicInputs::new(program_info, stack_inputs, stack_outputs.clone()); + + assert_eq!( + expected_result, + stack_outputs.clone().stack_truncated(1), + "Program result was computed incorrectly" + ); + + let pub_inputs = AirAdapterPublicInputs { + winterfell_public_inputs: pub_inputs, + transition_exemptions: vec![2; 182], + transition_offsets: vec![0, 1], + trace_info: winter_trace.get_info(), + metadata: winter_trace.clone().into(), + }; + + let trace = + MidenVMQuadFeltAir::convert_winterfell_trace_table(winter_trace.main_segment().clone()); + + let proof = Prover::::prove( + &trace, + &pub_inputs, + &lambda_proof_options, + QuadFeltTranscript::new(&[]), + ) + .unwrap(); + + assert!(Verifier::::verify( + &proof, + &pub_inputs, + &lambda_proof_options, + QuadFeltTranscript::new(&[]), + )); + } +} diff --git a/winterfell_adapter/src/examples/mod.rs b/winterfell_adapter/src/examples/mod.rs index 9a74354be6..117c0609af 100644 --- a/winterfell_adapter/src/examples/mod.rs +++ b/winterfell_adapter/src/examples/mod.rs @@ -1,2 +1,4 @@ +pub mod cubic; pub mod fibonacci_2_terms; pub mod fibonacci_rap; +pub mod miden_vm; diff --git a/winterfell_adapter/src/field_element/element.rs b/winterfell_adapter/src/field_element/element.rs index dda01351e5..8137587d45 100644 --- a/winterfell_adapter/src/field_element/element.rs +++ b/winterfell_adapter/src/field_element/element.rs @@ -1,3 +1,4 @@ +use crate::field_element::positive_integer::AdapterPositiveInteger; use core::fmt; use core::{ mem, @@ -12,14 +13,8 @@ use lambdaworks_math::{ traits::ByteConversion, }; use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub}; -use winter_utils::{AsBytes, DeserializationError, Randomizable}; -use winterfell::math::ExtensibleField; -use winterfell::{ - math::{FieldElement as IsWinterfellFieldElement, StarkField}, - Deserializable, Serializable, -}; - -use crate::field_element::positive_integer::AdapterPositiveInteger; +use winter_math::{ExtensibleField, FieldElement as IsWinterfellFieldElement, StarkField}; +use winter_utils::{AsBytes, Deserializable, DeserializationError, Randomizable, Serializable}; #[derive(Debug, Copy, Clone, Default)] pub struct AdapterFieldElement(pub FieldElement); @@ -68,7 +63,7 @@ impl IsWinterfellFieldElement for AdapterFieldElement { unsafe { slice::from_raw_parts(p as *const u8, len) } } - unsafe fn bytes_as_elements(bytes: &[u8]) -> Result<&[Self], winterfell::DeserializationError> { + unsafe fn bytes_as_elements(bytes: &[u8]) -> Result<&[Self], DeserializationError> { if bytes.len() % Self::ELEMENT_BYTES != 0 { return Err(DeserializationError::InvalidValue(format!( "number of bytes ({}) does not divide into whole number of field elements", diff --git a/winterfell_adapter/src/utils.rs b/winterfell_adapter/src/utils.rs index 40e082c831..12591cd7cb 100644 --- a/winterfell_adapter/src/utils.rs +++ b/winterfell_adapter/src/utils.rs @@ -1,23 +1,25 @@ -use crate::field_element::element::AdapterFieldElement; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; +use lambdaworks_math::field::traits::IsField; use stark_platinum_prover::fri::FieldElement; -pub fn vec_field2adapter(input: &[FieldElement]) -> Vec { - input.iter().map(|&e| AdapterFieldElement(e)).collect() +pub fn vec_lambda2winter + Copy>(input: &[FieldElement]) -> Vec { + input.iter().map(|&e| *e.value()).collect() } -pub fn vec_adapter2field(input: &[AdapterFieldElement]) -> Vec> { - input.iter().map(|&e| e.0).collect() +pub fn vec_winter2lambda + Copy>(input: &[FE]) -> Vec> { + input + .iter() + .map(|&e| FieldElement::::const_from_raw(e)) + .collect() } -pub fn matrix_field2adapter( - input: &[Vec>], -) -> Vec> { - input.iter().map(|v| vec_field2adapter(v)).collect() +pub fn matrix_lambda2winter + Copy>( + input: &[Vec>], +) -> Vec> { + input.iter().map(|v| vec_lambda2winter(v)).collect() } -pub fn matrix_adapter2field( - input: &[Vec], -) -> Vec>> { - input.iter().map(|v| vec_adapter2field(v)).collect() +pub fn matrix_winter2lambda + Copy>( + input: &[Vec], +) -> Vec>> { + input.iter().map(|v| vec_winter2lambda(v)).collect() }