From 9448b6f84b7e831a854d322fa6b6ea9b00848feb Mon Sep 17 00:00:00 2001 From: nganhkhoa Date: Tue, 18 Jun 2024 20:57:54 +0700 Subject: [PATCH] what did i do? --- .gitignore | 1 + Cargo.lock | 580 ++++++++++++++++++ Cargo.toml | 16 + build.rs | 5 + examples/a.bsnark | 7 + polynomial-rs/.github/dependabot.yml | 15 + polynomial-rs/.github/workflows/rust-ci.yml | 118 ++++ polynomial-rs/.gitignore | 2 + polynomial-rs/Cargo.toml | 26 + polynomial-rs/LICENSE | 22 + polynomial-rs/README.md | 30 + polynomial-rs/src/lib.rs | 616 ++++++++++++++++++++ src/ast.rs | 41 ++ src/bsnark.lalrpop | 37 ++ src/field.rs | 165 ++++++ src/main.rs | 226 +++++++ 16 files changed, 1907 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 examples/a.bsnark create mode 100644 polynomial-rs/.github/dependabot.yml create mode 100644 polynomial-rs/.github/workflows/rust-ci.yml create mode 100644 polynomial-rs/.gitignore create mode 100644 polynomial-rs/Cargo.toml create mode 100644 polynomial-rs/LICENSE create mode 100644 polynomial-rs/README.md create mode 100644 polynomial-rs/src/lib.rs create mode 100644 src/ast.rs create mode 100644 src/bsnark.lalrpop create mode 100644 src/field.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b76e3d3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,580 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "lalrpop" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30455341b0e18f276fa64540aff54deafb54c589de6aca68659c63dd2d5d823" +dependencies = [ + "ascii-canvas", + "atty", + "bit-set", + "diff", + "ena", + "itertools", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "lalrpop-util" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf796c978e9b4d983414f4caedc9273aa33ee214c5b887bd55fde84c85d2dc4" +dependencies = [ + "regex", +] + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "modinverse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f62f577f148cc1a9466e7065a22e59466a7d537cceba5e77e57181d0f706633" +dependencies = [ + "num-integer", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "petgraph" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" + +[[package]] +name = "polynomial" +version = "0.2.5" +dependencies = [ + "modinverse", + "num-traits", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "small-simple-snark" +version = "0.1.0" +dependencies = [ + "lalrpop", + "lalrpop-util", + "modinverse", + "polynomial", + "regex", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "string_cache" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "syn" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8fea473 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "small-simple-snark" +version = "0.1.0" +edition = "2021" +author = "nganhkhoa" + +build = "build.rs" + +[dependencies] +lalrpop-util = { version = "0.19.8", features = ["lexer"] } +modinverse = "0.1.1" +polynomial = { path="polynomial-rs" } +regex = "1.7.0" + +[build-dependencies] +lalrpop = "0.19.8" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..23c7d3f --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +extern crate lalrpop; + +fn main() { + lalrpop::process_root().unwrap(); +} diff --git a/examples/a.bsnark b/examples/a.bsnark new file mode 100644 index 0000000..b1c4654 --- /dev/null +++ b/examples/a.bsnark @@ -0,0 +1,7 @@ +a, b, c + + +d = a * a +e = b * b +f = c * c +f = d + e diff --git a/polynomial-rs/.github/dependabot.yml b/polynomial-rs/.github/dependabot.yml new file mode 100644 index 0000000..4363b3b --- /dev/null +++ b/polynomial-rs/.github/dependabot.yml @@ -0,0 +1,15 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" + - package-ecosystem: "cargo" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" diff --git a/polynomial-rs/.github/workflows/rust-ci.yml b/polynomial-rs/.github/workflows/rust-ci.yml new file mode 100644 index 0000000..546c2e5 --- /dev/null +++ b/polynomial-rs/.github/workflows/rust-ci.yml @@ -0,0 +1,118 @@ +name: Rust CI + +on: push + +env: + CARGO_TERM_COLOR: always + +jobs: + rustfmt_check: + name: Rustfmt Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: rustfmt + override: true + - name: rustfmt-check + run: cargo fmt --all --check + + clippy_check: + name: Clippy Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check workflow permissions + id: check_permissions + uses: scherermichael-oss/action-has-permission@1.0.6 + with: + required-permission: write + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Run clippy action to produce annotations + uses: actions-rs/clippy-check@v1 + if: ${{ steps.check_permissions.outputs.has-permission }} + with: + # GitHub displays the clippy job and its results as separate entries + name: Clippy (stable) Results + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-features --all-targets -- -D warnings + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: clippy + override: true + - name: Run clippy manually without annotations + if: ${{ !steps.check_permissions.outputs.has-permission }} + run: cargo clippy --all-features --all-targets -- -D warnings + + security_audit: + name: Security Audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + test: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - 1.56.1 + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + - name: cargo test + run: cargo test --workspace --all-targets --all-features + + build: + name: Build + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - stable + - 1.56.1 + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + - name: cargo build + run: cargo build --workspace --all-targets --all-features + + coverage: + name: Code coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + components: llvm-tools-preview + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + - name: Generate code coverage + run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: lcov.info + fail_ci_if_error: true + diff --git a/polynomial-rs/.gitignore b/polynomial-rs/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/polynomial-rs/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/polynomial-rs/Cargo.toml b/polynomial-rs/Cargo.toml new file mode 100644 index 0000000..83e71d8 --- /dev/null +++ b/polynomial-rs/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "polynomial" +version = "0.2.5" +edition = "2021" +rust-version = "1.56.1" +authors = ["gifnksm "] +license = "MIT" +readme = "README.md" +repository = "https://github.com/gifnksm/polynomial-rs" +description = "A library for manipulating polynomials" + +[badges] +maintenance = { status = "passively-maintained" } + +[dependencies] +num-traits = "0.2" +serde = { version = "1.0", features = ["derive"], optional = true } +modinverse = "0.1.1" + +[features] +serde = ["dep:serde"] + +[package.metadata.release] +pre-release-replacements = [ + { file = "README.md", search = "polynomial = \"[0-9\\.]+\"", replace = "{{crate_name}} = \"{{version}}\"" } +] diff --git a/polynomial-rs/LICENSE b/polynomial-rs/LICENSE new file mode 100644 index 0000000..1a0de11 --- /dev/null +++ b/polynomial-rs/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 NAKASHIMA, Makoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/polynomial-rs/README.md b/polynomial-rs/README.md new file mode 100644 index 0000000..0987d8a --- /dev/null +++ b/polynomial-rs/README.md @@ -0,0 +1,30 @@ +# polynomial-rs + +[![maintenance status: passively-maintained](https://img.shields.io/badge/maintenance-passively--maintained-yellowgreen.svg)](https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section) +[![license](https://img.shields.io/crates/l/polynomial.svg)](LICENSE) +[![crates.io](https://img.shields.io/crates/v/polynomial.svg)](https://crates.io/crates/polynomial) +[![docs.rs](https://img.shields.io/docsrs/polynomial/latest)](https://docs.rs/polynomial/latest/) +[![rust 1.56.1+ badge](https://img.shields.io/badge/rust-1.56.1+-93450a.svg)](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field) +[![Rust CI](https://github.com/gifnksm/polynomial-rs/actions/workflows/rust-ci.yml/badge.svg)](https://github.com/gifnksm/polynomial-rs/actions/workflows/rust-ci.yml) +[![codecov](https://codecov.io/gh/gifnksm/polynomial-rs/branch/master/graph/badge.svg?token=0RxeiNjQNM)](https://codecov.io/gh/gifnksm/polynomial-rs) + +A library for manipulating polynomials. + +[Documentation](https://docs.rs/polynomial/latest/polynomial/) + +## How to use? + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +polynomial = "0.2.5" +``` + +## Minimum supported Rust version (MSRV) + +The minimum supported Rust version is **Rust 1.56.1**. +At least the last 3 versions of stable Rust are supported at any given time. + +While a crate is pre-release status (0.x.x) it may have its MSRV bumped in a patch release. +Once a crate has reached 1.x, any MSRV bump will be accompanied with a new minor version. diff --git a/polynomial-rs/src/lib.rs b/polynomial-rs/src/lib.rs new file mode 100644 index 0000000..9cad0d1 --- /dev/null +++ b/polynomial-rs/src/lib.rs @@ -0,0 +1,616 @@ +//! A library for manipulating polynomials. + +#![warn(bad_style)] +#![warn(missing_docs)] +#![warn(trivial_casts)] +#![warn(trivial_numeric_casts)] +#![warn(unused)] +#![warn(unused_extern_crates)] +#![warn(unused_import_braces)] +#![warn(unused_qualifications)] +#![warn(unused_results)] + +use num_traits::{FromPrimitive, One, Zero}; +use std::ops::{Add, Div, Mul, Neg, Sub, Rem}; +use std::{cmp, fmt}; + +/// A polynomial. +#[derive(Eq, PartialEq, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Polynomial { + data: Vec, +} + +impl Polynomial { + /// Creates a new `Polynomial` from a `Vec` of coefficients. + /// + /// # Examples + /// + /// ``` + /// use polynomial::Polynomial; + /// let poly = Polynomial::new(vec![1, 2, 3]); + /// assert_eq!("1+2*x+3*x^2", poly.pretty("x")); + /// ``` + #[inline] + pub fn new(mut data: Vec) -> Self { + while let Some(true) = data.last().map(|x| x.is_zero()) { + let _ = data.pop(); + } + Self { data } + } +} + +impl Polynomial +where + T: One + + Zero + + Clone + + Copy + + Neg + + Div + + Mul + + Sub + + Rem + + Ord + + fmt::Display, +{ + fn egcd(a: T, b: T) -> (T, T, T) { + if a == T::zero() { + (b, T::zero(), T::one()) + } + else { + let (g, x, y) = Self::egcd(b % a, a); + (g, y - (b / a) * x, x) + } + } + + fn modinverse(a: T, m: T) -> Option { + let (g, x, _) = Self::egcd(a, m); + if g != T::one() { + None + } + else { + Some((x % m + m) % m) + } + } + + /// Creates the [Lagrange polynomial] that fits a number of points. + /// + /// [Lagrange polynomial]: https://en.wikipedia.org/wiki/Lagrange_polynomial + /// + /// Returns `None` if any two x-coordinates are the same. + /// + /// # Examples + /// + /// ``` + /// use polynomial::Polynomial; + /// let poly = Polynomial::lagrange(&[1, 2, 3], &[10, 40, 90]).unwrap(); + /// println!("{}", poly.pretty("x")); + /// assert_eq!("10*x^2", poly.pretty("x")); + /// ``` + #[inline] + pub fn lagrange(xs: &[T], ys: &[T], field: Option<&T>) -> Option { + let mut res = Polynomial::new(vec![Zero::zero()]); + for ((i, x), y) in (0..).zip(xs.iter()).zip(ys.iter()) { + let mut p: Polynomial = Polynomial::new(vec![T::one()]); + let mut denom = T::one(); + let mut negative = false; + for (j, x2) in (0..).zip(xs.iter()) { + if i != j { + p = p * &Polynomial::new(vec![-x2.clone(), T::one()]); + let diff = if x < x2 { + x2.clone() - x.clone() + } else { + x.clone() - x2.clone() + }; + if diff.is_zero() { + return None; + } + + negative = !(negative ^ (x < x2)); + denom = denom * diff; + } + } + let scalar = match field { + Some(f) => { + let denom_inv = Self::modinverse(denom, f.clone()).unwrap(); + if negative { + - y.clone() * denom_inv + } else { + y.clone() * denom_inv + } + }, + None => { + if negative { + - y.clone() / denom + } else { + y.clone() / denom + } + } + }; + println!("{}/{} = {}", y.to_string(), denom.to_string(), scalar); + res = res + p * &Polynomial::::new(vec![scalar]); + } + Some(res) + } +} + +impl Polynomial +where + T: One + + Zero + + Clone + + Copy + + Neg + + Div + + Mul + + Sub + + Rem + + Ord + + FromPrimitive + + fmt::Display, +{ + /// [Chebyshev approximation] fits a function to a polynomial over a range of values. + /// + /// [Chebyshev approximation]: https://en.wikipedia.org/wiki/Approximation_theory#Chebyshev_approximation + /// + /// This attempts to minimize the maximum error. + /// + /// Retrurns `None` if `n < 1` or `xmin >= xmax`. + /// + /// # Examples + /// + /// ``` + /// use polynomial::Polynomial; + /// use std::f64::consts::PI; + /// let p = Polynomial::chebyshev(&f64::sin, 7, -PI/4., PI/4.).unwrap(); + /// assert!((p.eval(0.) - (0.0_f64).sin()).abs() < 0.0001); + /// assert!((p.eval(0.1) - (0.1_f64).sin()).abs() < 0.0001); + /// assert!((p.eval(-0.1) - (-0.1_f64).sin()).abs() < 0.0001); + /// ``` + #[inline] + pub fn chebyshev T>(f: &F, n: usize, xmin: f64, xmax: f64) -> Option { + if n < 1 || xmin >= xmax { + return None; + } + + let mut xs = Vec::new(); + for i in 0..n { + use std::f64::consts::PI; + let x = T::from_f64( + (xmax + xmin) * 0.5 + + (xmin - xmax) * 0.5 * ((2 * i + 1) as f64 * PI / (2 * n) as f64).cos(), + ) + .unwrap(); + xs.push(x); + } + + let ys: Vec = xs.iter().map(|x| f(x.clone())).collect(); + Polynomial::lagrange(&xs[0..], &ys[0..], None) + } +} + +impl + Clone> Polynomial { + /// Evaluates the polynomial at a point. + /// + /// # Examples + /// + /// ``` + /// use polynomial::Polynomial; + /// let poly = Polynomial::new(vec![1, 2, 3]); + /// assert_eq!(1, poly.eval(0)); + /// assert_eq!(6, poly.eval(1)); + /// assert_eq!(17, poly.eval(2)); + /// ``` + #[inline] + pub fn eval(&self, x: T) -> T { + let mut result: T = Zero::zero(); + for n in self.data.iter().rev() { + result = n.clone() + result * x.clone(); + } + result + } +} + +impl Polynomial { + /// Gets the slice of internal data. + #[inline] + pub fn data(&self) -> &[T] { + &self.data + } +} + +impl Polynomial +where + T: Zero + One + Eq + Neg + Ord + fmt::Display + Clone, +{ + /// Pretty prints the polynomial. + pub fn pretty(&self, x: &str) -> String { + if self.is_zero() { + return "0".to_string(); + } + + let one = One::one(); + let mut s = Vec::new(); + for (i, n) in self.data.iter().enumerate() { + // output n*x^i / -n*x^i + if n.is_zero() { + continue; + } + + let term = if i.is_zero() { + n.to_string() + } else if i == 1 { + if (*n) == one { + x.to_string() + } else if (*n) == -one.clone() { + format!("-{}", x) + } else { + format!("{}*{}", n, x) + } + } else if (*n) == one { + format!("{}^{}", x, i) + } else if (*n) == -one.clone() { + format!("-{}^{}", x, i) + } else { + format!("{}*{}^{}", n, x, i) + }; + + if !s.is_empty() && (*n) > Zero::zero() { + s.push("+".to_string()); + } + s.push(term); + } + + s.concat() + } +} + +impl Neg for Polynomial +where + T: Neg + Zero + Clone, + ::Output: Zero, +{ + type Output = Polynomial<::Output>; + + #[inline] + fn neg(self) -> Polynomial<::Output> { + -&self + } +} + +impl<'a, T> Neg for &'a Polynomial +where + T: Neg + Zero + Clone, + ::Output: Zero, +{ + type Output = Polynomial<::Output>; + + #[inline] + fn neg(self) -> Polynomial<::Output> { + Polynomial::new(self.data.iter().map(|x| -x.clone()).collect()) + } +} + +macro_rules! forward_val_val_binop { + (impl $imp:ident, $method:ident) => { + impl $imp> for Polynomial + where + Lhs: Zero + $imp + Clone, + Rhs: Zero + Clone, + >::Output: Zero, + { + type Output = Polynomial<>::Output>; + + #[inline] + fn $method(self, other: Polynomial) -> Polynomial<>::Output> { + (&self).$method(&other) + } + } + }; +} + +macro_rules! forward_ref_val_binop { + (impl $imp:ident, $method:ident) => { + impl<'a, Lhs, Rhs> $imp> for &'a Polynomial + where + Lhs: Zero + $imp + Clone, + Rhs: Zero + Clone, + >::Output: Zero, + { + type Output = Polynomial<>::Output>; + + #[inline] + fn $method(self, other: Polynomial) -> Polynomial<>::Output> { + self.$method(&other) + } + } + }; +} + +macro_rules! forward_val_ref_binop { + (impl $imp:ident, $method:ident) => { + impl<'a, Lhs, Rhs> $imp<&'a Polynomial> for Polynomial + where + Lhs: Zero + $imp + Clone, + Rhs: Zero + Clone, + >::Output: Zero, + { + type Output = Polynomial<>::Output>; + + #[inline] + fn $method(self, other: &Polynomial) -> Polynomial<>::Output> { + (&self).$method(other) + } + } + }; +} + +macro_rules! forward_all_binop { + (impl $imp:ident, $method:ident) => { + forward_val_val_binop!(impl $imp, $method); + forward_ref_val_binop!(impl $imp, $method); + forward_val_ref_binop!(impl $imp, $method); + }; +} + +forward_all_binop!(impl Add, add); + +impl<'a, 'b, Lhs, Rhs> Add<&'b Polynomial> for &'a Polynomial +where + Lhs: Zero + Add + Clone, + Rhs: Zero + Clone, + >::Output: Zero, +{ + type Output = Polynomial<>::Output>; + + fn add(self, other: &Polynomial) -> Polynomial<>::Output> { + let max_len = cmp::max(self.data.len(), other.data.len()); + let min_len = cmp::min(self.data.len(), other.data.len()); + + let mut sum = Vec::with_capacity(max_len); + for i in 0..min_len { + sum.push(self.data[i].clone() + other.data[i].clone()); + } + + if self.data.len() <= other.data.len() { + for i in min_len..max_len { + sum.push(num_traits::zero::() + other.data[i].clone()); + } + } else { + for i in min_len..max_len { + sum.push(self.data[i].clone() + num_traits::zero::()); + } + } + + Polynomial::new(sum) + } +} + +forward_all_binop!(impl Sub, sub); + +impl<'a, 'b, Lhs, Rhs> Sub<&'b Polynomial> for &'a Polynomial +where + Lhs: Zero + Sub + Clone, + Rhs: Zero + Clone, + >::Output: Zero, +{ + type Output = Polynomial<>::Output>; + + fn sub(self, other: &Polynomial) -> Polynomial<>::Output> { + let min_len = cmp::min(self.data.len(), other.data.len()); + let max_len = cmp::max(self.data.len(), other.data.len()); + + let mut sub = Vec::with_capacity(max_len); + for i in 0..min_len { + sub.push(self.data[i].clone() - other.data[i].clone()); + } + if self.data.len() <= other.data.len() { + for i in min_len..max_len { + sub.push(num_traits::zero::() - other.data[i].clone()) + } + } else { + for i in min_len..max_len { + sub.push(self.data[i].clone() - num_traits::zero::()) + } + } + Polynomial::new(sub) + } +} + +forward_all_binop!(impl Mul, mul); + +impl<'a, 'b, Lhs, Rhs> Mul<&'b Polynomial> for &'a Polynomial +where + Lhs: Zero + Mul + Clone, + Rhs: Zero + Clone, + >::Output: Zero, +{ + type Output = Polynomial<>::Output>; + + fn mul(self, other: &Polynomial) -> Polynomial<>::Output> { + if self.is_zero() || other.is_zero() { + return Polynomial::new(vec![]); + } + + let slen = self.data.len(); + let olen = other.data.len(); + let prod = (0..slen + olen - 1) + .map(|i| { + let mut p = num_traits::zero::<>::Output>(); + let kstart = cmp::max(olen, i + 1) - olen; + let kend = cmp::min(slen, i + 1); + for k in kstart..kend { + p = p + self.data[k].clone() * other.data[i - k].clone(); + } + p + }) + .collect(); + Polynomial::new(prod) + } +} + +impl Zero for Polynomial { + #[inline] + fn zero() -> Self { + Self { data: vec![] } + } + #[inline] + fn is_zero(&self) -> bool { + self.data.is_empty() + } +} + +impl One for Polynomial { + #[inline] + fn one() -> Self { + Self { + data: vec![One::one()], + } + } +} + +#[cfg(test)] +mod tests { + use super::Polynomial; + + #[test] + fn new() { + fn check(dst: Vec, src: Vec) { + assert_eq!(dst, Polynomial::new(src).data); + } + check(vec![1, 2, 3], vec![1, 2, 3]); + check(vec![1, 2, 3], vec![1, 2, 3, 0, 0]); + check(vec![], vec![0, 0, 0]); + } + + #[test] + fn neg_add_sub() { + fn check(a: &[i32], b: &[i32], c: &[i32]) { + fn check_eq(a: &Polynomial, b: &Polynomial) { + assert_eq!(*a, *b); + assert_eq!(-a, -b); + } + fn check_add(sum: &Polynomial, a: &Polynomial, b: &Polynomial) { + check_eq(sum, &(a + b)); + check_eq(sum, &(b + a)); + } + fn check_sub(sum: &Polynomial, a: &Polynomial, b: &Polynomial) { + check_eq(a, &(sum - b)); + check_eq(b, &(sum - a)); + } + + let a = &Polynomial::new(a.to_vec()); + let b = &Polynomial::new(b.to_vec()); + let c = &Polynomial::new(c.to_vec()); + check_add(c, a, b); + check_add(&(-c), &(-a), &(-b)); + check_sub(c, a, b); + check_sub(&(-c), &(-a), &(-b)); + } + check(&[], &[], &[]); + check(&[], &[1], &[1]); + check(&[1], &[1], &[2]); + check(&[1, 0, 1], &[1], &[2, 0, 1]); + check(&[1, 0, -1], &[-1, 0, 1], &[]); + } + + #[test] + fn mul() { + fn check(a: &[i32], b: &[i32], c: &[i32]) { + let a = Polynomial::new(a.to_vec()); + let b = Polynomial::new(b.to_vec()); + let c = Polynomial::new(c.to_vec()); + assert_eq!(c, &a * &b); + assert_eq!(c, &b * &a); + } + check(&[], &[], &[]); + check(&[0, 0], &[], &[]); + check(&[0, 0], &[1], &[]); + check(&[1, 0], &[1], &[1]); + check(&[1, 0, 1], &[1], &[1, 0, 1]); + check(&[1, 1], &[1, 1], &[1, 2, 1]); + check(&[1, 1], &[1, 0, 1], &[1, 1, 1, 1]); + check(&[0, 0, 1], &[0, 0, 1], &[0, 0, 0, 0, 1]); + } + + #[test] + fn eval() { + fn check i32>(pol: &[i32], f: F) { + for n in -10..10 { + assert_eq!(f(n), Polynomial::new(pol.to_vec()).eval(n)); + } + } + check(&[], |_x| 0); + check(&[1], |_x| 1); + check(&[1, 1], |x| x + 1); + check(&[0, 1], |x| x); + check(&[10, -10, 10], |x| 10 * x * x - 10 * x + 10); + } + + #[test] + fn pretty() { + fn check(slice: &[i32], s: &str) { + assert_eq!(s.to_string(), Polynomial::new(slice.to_vec()).pretty("x")); + } + check(&[0], "0"); + check(&[1], "1"); + check(&[1, 1], "1+x"); + check(&[1, 1, 1], "1+x+x^2"); + check(&[2, 2, 2], "2+2*x+2*x^2"); + check(&[0, 0, 0, 1], "x^3"); + check(&[0, 0, 0, -1], "-x^3"); + check(&[-1, 0, 0, -1], "-1-x^3"); + check(&[-1, 1, 0, -1], "-1+x-x^3"); + check(&[-1, 1, -1, -1], "-1+x-x^2-x^3"); + } + + #[test] + fn lagrange() { + // Evaluate the lagrange polynomial at the x coordinates. + // The error should be close to zero. + fn check(xs: &[f64], ys: &[f64]) { + let p = Polynomial::lagrange(xs, ys).unwrap(); + for (x, y) in xs.iter().zip(ys) { + assert!((p.eval(*x) - y).abs() < 1e-9); + } + } + + // Squares + check(&[1., 2., 3.], &[10., 40., 90.]); + // Cubes + check(&[-1., 0., 1., 2.], &[-1000., 0., 1000., 8000.]); + // Non linear x. + check(&[1., 9., 10., 11.], &[1., 2., 3., 4.]); + // Test double x failure case. + assert_eq!( + Polynomial::lagrange(&[1., 9., 9., 11.], &[1., 2., 3., 4.]), + None + ); + } + + #[test] + fn chebyshev() { + // Construct a Chebyshev approximation for a function + // and evaulate it at 100 points. + fn check f64>(f: &F, n: usize, xmin: f64, xmax: f64) { + let p = Polynomial::chebyshev(f, n, xmin, xmax).unwrap(); + for i in 0..=100 { + let x = xmin + (i as f64) * ((xmax - xmin) / 100.0); + let diff = (f(x) - p.eval(x)).abs(); + assert!(diff < 0.0001); + } + } + + // Approximate some common functions. + use std::f64::consts::PI; + check(&f64::sin, 7, -PI / 2., PI / 2.); + check(&f64::cos, 7, 0., PI / 4.); + + // Test n >= 1 condition + assert!(Polynomial::chebyshev(&f64::exp, 0, 0., 1.) == None); + + // Test xmax > xmin condition + assert!(Polynomial::chebyshev(&f64::ln, 1, 1., 0.) == None); + } +} diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..ee6bb4f --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,41 @@ +#[derive(Debug)] +pub struct Constraints<'input> { + pub iden: Operand<'input>, + pub expr: Expr<'input>, +} + +impl<'input> Constraints<'input> { + pub fn new(iden: Operand<'input>, expr: Expr<'input>) -> Self { + Self { + iden, expr + } + } +} + +#[derive(Debug)] +pub struct Expr<'input> { + pub left: Operand<'input>, + pub right: Operand<'input>, + pub op: Opcode, +} + +impl<'input> Expr<'input> { + pub fn new(left: Operand<'input>, op: Opcode, right: Operand<'input>) -> Self { + Self { + left, right, op + } + } +} + + +#[derive(Debug)] +pub enum Operand<'input> { + Identifier(&'input str), + Num(&'input str), +} + +#[derive(Debug)] +pub enum Opcode { + Add, + Mul, +} diff --git a/src/bsnark.lalrpop b/src/bsnark.lalrpop new file mode 100644 index 0000000..1359212 --- /dev/null +++ b/src/bsnark.lalrpop @@ -0,0 +1,37 @@ +use crate::ast::{Expr, Constraints, Opcode, Operand}; + +grammar; + +pub Declaration: Vec> = { + => { + let mut s: Vec> = s.into_iter().map(|x| x.0).collect(); + s.push(i); + s + }, +}; + +pub Constraints: Box> = { + "=" => Box::new(Constraints::new(i, e)) +}; + +pub Expr: Expr<'input> = { + Operand ExprOp Operand => Expr::new(<>), +}; + +Operand: Operand<'input> = { + Iden, + Num, +}; + +Iden: Operand<'input> = { + r"[a-zA-Z][0-9a-zA-Z]*" => Operand::Identifier(<>), +}; + +Num: Operand<'input> = { + r"[0-9]+" => Operand::Num(<>), +}; + +ExprOp: Opcode = { + "+" => Opcode::Add, + "*" => Opcode::Mul, +}; diff --git a/src/field.rs b/src/field.rs new file mode 100644 index 0000000..54e5260 --- /dev/null +++ b/src/field.rs @@ -0,0 +1,165 @@ +use std::fmt; + +use modinverse::modinverse; + +pub struct G1 { + p: u64, + x: u64, + y: u64, +} + +impl fmt::Display for G1 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {}) in F_{}", self.x, self.y, self.p) + } +} + +impl fmt::Debug for G1 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({}, {}) in F_{}", self.x, self.y, self.p) + } +} + + + +impl G1 { + pub fn new(p: u64, x: u64, y: u64) -> Self { + G1 { + p, x, y + } + } + + pub fn add(self: &mut Self, other: &G1) { + let yp = &self.y; + let yq = &other.y; + let xp = &self.x; + let xq = &other.x; + + if *xq == 0 && *yq == 0 { + return; + } + + if *xp == 0 && *yp == 0 { + self.x = *xq; + self.y = *yq; + return; + } + + if yp == yq && xp == xq { + self.double(); + return; + } + + let p = &self.p; + + let xq_xp = xq + (p - xp); + let yq_yp = yq + (p - yp); + + if xq_xp == *p { + self.x = 0; + self.y = 0; + return; + } + + let xq_xp_inv = modinverse(xq_xp as i64, *p as i64).unwrap(); + let lambda = (yq_yp * xq_xp_inv as u64) % p; + + let xr = (lambda * lambda + (p - xp) + (p - xq)) % p; + let yr = (lambda * (xp + (p - xr)) + (p - yp)) % p; + + self.x = xr; + self.y = yr; + } + + pub fn double(self: &mut Self) { + let x = &self.x; + let y = &self.y; + let p = &self.p; + + let y2_inv = modinverse((2 * y) as i64, *p as i64).unwrap(); + + let m = (3 * x * x * y2_inv as u64) % p; + let m2 = (m * m) % p; + let x_new = (m2 + (p - ((2 * x) % p))) % p; + let y_new = (m * (3 * x + (p - m2)) + (p - y)) % p; + + self.x = x_new; + self.y = y_new; + } + + pub fn mul(self: &mut Self, times: u64) { + let mut g = self.clone(); + let mut count = times; + let mut res = G1::new(self.p, 0, 0); + while count != 0 { + if count & 1 == 1 { + res.add(&g); + } + g.double(); + count = count >> 1; + } + self.x = res.x; + self.y = res.y; + println!("{}", self); + } + + pub fn inv(self: Self) -> Self { + Self { + p: self.p, + x: self.x, + y: self.p - self.y, + } + } + + pub fn clone(self: &Self) -> Self { + Self { + p: self.p, + x: self.x, + y: self.y, + } + } +} + +#[test] +fn test_double() { + let mut g1 = G1::new(101, 1, 2); + println!("g1 = {}", g1); + g1.double(); + println!("2g1 = {}", g1); +} + +#[test] +fn test_0add() { + let g1 = G1::new(101, 1, 2); + let mut inf = G1::new(101, 0, 0); + inf.add(&g1); + println!("O+g1 = {}", inf); +} + +#[test] +fn test_add0() { + let mut g1 = G1::new(101, 1, 2); + let inf = G1::new(101, 0, 0); + g1.add(&inf); + println!("g1+O = {}", g1); +} + + +#[test] +fn test_mul() { + let mut g1 = G1::new(101, 1, 2); + println!("g1 = {}", g1); + + let x = 121; + g1.mul(x); + println!("{}g1 = {}g1 = {}", x, x % 17, g1); +} + +#[test] +fn test_add() { + let mut g1 = G1::new(101, 1, 2); + println!("g1 = {}", g1); + g1.mul(7); + println!("7g1 = {}", g1); +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..277b18a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,226 @@ +#[macro_use] +extern crate lalrpop_util; + +lalrpop_mod!(pub bsnark); +pub mod ast; +pub mod field; + +use ast::{Operand, Constraints}; + +use std::collections::HashMap; +use std::vec::Vec; +use std::{env, fs}; +use std::error::Error; + +use polynomial::Polynomial; + +#[derive(Debug)] +struct Context<'a> { + content: &'a str, + inputs: Vec>, + outputs: Vec>, + publics: Vec>, + constraints: Vec>, +} + +impl<'a> Context<'a> { + fn new(content: &'a str, inputs: Vec>, outputs: Vec>, publics: Vec>) -> Self { + Self { + content, + inputs, + outputs, + publics, + constraints: Vec::new(), + } + } + + fn add_constraint(&mut self, constraint: Constraints<'a>) { + self.constraints.push(constraint); + } + + fn make_trace(&mut self, input_values: Vec) { + // building gates + let mut q_left: Vec = Vec::new(); + let mut q_right: Vec = Vec::new(); + let mut q_output: Vec = Vec::new(); + let mut q_mul: Vec = Vec::new(); + let mut q_const: Vec = Vec::new(); + let mut a_values: Vec = Vec::new(); + let mut b_values: Vec = Vec::new(); + let mut c_values: Vec = Vec::new(); + + let mut values_map: HashMap = HashMap::new(); + for (i, input) in self.inputs.iter().enumerate() { + match input { + Operand::Identifier(id) => { + values_map.insert(id.to_string(), input_values[i]); + } + _ => {} + } + } + println!("{:?}", values_map); + + // let mut execution_trace = ExecutionTrace::new(inputs, input_values); + let polynomials = self.constraints + .iter() + .enumerate() + .for_each(|(idx, Constraints { expr, iden })| { + let left_value = match &expr.left { + Operand::Identifier(id) => { + values_map[&id.to_string()] + }, + Operand::Num(n) => { + n.parse::().unwrap() + } + }; + let right_value = match &expr.right { + Operand::Identifier(id) => { + values_map[&id.to_string()] + }, + Operand::Num(n) => { + n.parse::().unwrap() + } + }; + let assigned_var = match iden { + Operand::Identifier(id) => id.to_string(), + _ => "".into(), + }; + + let eval_value = match &expr.op { + ast::Opcode::Mul => { + q_mul.push(1); + q_left.push(0); + q_right.push(0); + q_output.push(-1); + q_const.push(0); + + left_value * right_value + }, + ast::Opcode::Add => { + q_mul.push(0); + q_left.push(1); + q_right.push(1); + q_output.push(-1); + q_const.push(0); + left_value + right_value + }, + }; + + a_values.push(left_value as i64); + b_values.push(right_value as i64); + c_values.push(eval_value as i64); + + match values_map.get(&assigned_var) { + Some(value) => { + assert!(*value == eval_value, + "constraint {}: expecting {} equals {}\n{:?} = {:?}", + idx, value, left_value * right_value, + iden, expr,); + }, + None => { + values_map.insert(assigned_var, eval_value); + } + } + + }); + + println!("values {:?}", values_map); + + println!("q_mul {:?}", q_mul); + println!("q_left {:?}", q_left); + println!("q_right {:?}", q_right); + println!("q_output {:?}", q_output); + println!("q_const {:?}", q_const); + println!("a_values {:?}", a_values); + println!("b_values {:?}", b_values); + println!("c_values {:?}", c_values); + + let nroot = vec![1, 4, 16, 13]; + let field = 17; + let a_poly = Polynomial::lagrange(&nroot, &a_values, Some(&field)).unwrap(); + println!("{}", a_poly.pretty("x")); + + } +} + +fn main() -> Result<(), Box>{ + let args: Vec = env::args().collect(); + let content = fs::read_to_string(&args[1]).unwrap(); + + let mut lines = content.split("\n"); + + // first 3 lines declares the inputs/outputs/publics + let inputs = lines.next().ok_or("1st line must provide a list of inputs")?; + let outputs = lines.next().ok_or("2nd line must provide a list of outputs")?; + let publics = lines.next().ok_or("3rd line must provide a list of publics")?; + + let inputs = if inputs.is_empty() { + Vec::new() + } else { + bsnark::DeclarationParser::new().parse(inputs).unwrap() + }; + + let outputs = if outputs.is_empty() { + Vec::new() + } else { + bsnark::DeclarationParser::new().parse(outputs).unwrap() + }; + + let publics = if publics.is_empty() { + Vec::new() + } else { + bsnark::DeclarationParser::new().parse(publics).unwrap() + }; + + let mut context = Context::new(&content, inputs, outputs, publics); + + lines + .fold(&mut context, |ctx, line| { + if line.is_empty() { + return ctx; + } + let constraint = bsnark::ConstraintsParser::new().parse(line).unwrap(); + println!("{:?}", constraint); + ctx.add_constraint(*constraint); + ctx + }); + + println!("{:?}", context); + + + // setup + // + // curve y^2 = x^3 + 3 (mod 101) + // G1 (1, 2) + // G2 (36, 31u) (complex number, u^2 = -2) + // toxic waste s = 2 (randomly generated) + // + // SRS = [G1, sG1, s^2 G1, s^3 G1, ...., s^(n+2) G1, G2, sG2] + // where n is the number of gates + // + // gates: QL a + QR b + QO c + QM a * b + QC = 0 + // therefore, we have n = 4 (QLeft, QRight, QOutput, QMultiply) (QConstant is not counted) + + let s = 2u64; + let mut srs: Vec = Vec::new(); + for i in 0..=6 { + let mut g1 = field::G1::new(101, 1, 2); + g1.mul(s.pow(i)); + srs.push(g1) + } + + println!("{:?}", srs); + + context.make_trace(vec![3, 4, 5]); + + let a = vec![14, 6, 3, 3, 4, 7]; + let a = a.iter().enumerate().fold(field::G1::new(101, 0, 0), |acc, (i, coeff)| { + let mut point = srs[i].clone(); + point.mul(*coeff); + point.add(&acc); + point + }); + println!("[a(s)]_1 = {}", a); + + Ok(()) +}