mirror of
https://github.com/pseXperiments/ff-Goldilocks.git
synced 2026-01-08 23:17:57 -05:00
init commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
993
Cargo.lock
generated
Normal file
993
Cargo.lock
generated
Normal file
@@ -0,0 +1,993 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anes"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "ark-std"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"num-traits",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"ciborium-ll",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-io"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-ll"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"is-terminal",
|
||||
"itertools",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "goldilocks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ark-std",
|
||||
"criterion",
|
||||
"ff",
|
||||
"halo2curves",
|
||||
"rand_core",
|
||||
"rand_xorshift",
|
||||
"serde",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[package]]
|
||||
name = "halo2curves"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6b1142bd1059aacde1b477e0c80c142910f1ceae67fc619311d6a17428007ab"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"ff",
|
||||
"group",
|
||||
"lazy_static",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"pasta_curves",
|
||||
"paste",
|
||||
"rand",
|
||||
"rand_core",
|
||||
"static_assertions",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[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.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "pasta_curves"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"ff",
|
||||
"group",
|
||||
"lazy_static",
|
||||
"rand",
|
||||
"static_assertions",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
"plotters-svg",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters-backend"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
|
||||
|
||||
[[package]]
|
||||
name = "plotters-svg"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
|
||||
dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[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-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[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.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.0",
|
||||
"windows_aarch64_msvc 0.52.0",
|
||||
"windows_i686_gnu 0.52.0",
|
||||
"windows_i686_msvc 0.52.0",
|
||||
"windows_x86_64_gnu 0.52.0",
|
||||
"windows_x86_64_gnullvm 0.52.0",
|
||||
"windows_x86_64_msvc 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
24
Cargo.toml
Normal file
24
Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "goldilocks"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ff = "0.13.0"
|
||||
rand_core = "0.6.4"
|
||||
subtle = "2.5"
|
||||
serde = "1.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
rand_xorshift = "0.3"
|
||||
ark-std = { version = "0.4", features = ["print-trace"] }
|
||||
halo2curves = "0.1.0"
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
|
||||
[[bench]]
|
||||
name = "bench"
|
||||
harness = false
|
||||
8
README.md
Normal file
8
README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Implementation of Goldilocks and its extension fields
|
||||
------
|
||||
|
||||
This repo implements
|
||||
- Goldilocks Field mod `2^64 - 2^32 + 1`
|
||||
- Goldilocks quadratic extension over `x^2 + 1`
|
||||
- Goldilocks cubic extension over `x^3 - x - 1`
|
||||
Traits are compatible with `ff 0.13.0`.
|
||||
51
benches/bench.rs
Normal file
51
benches/bench.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use ff::Field;
|
||||
use goldilocks::{Goldilocks, GoldilocksExt2, GoldilocksExt3, SmallField};
|
||||
use halo2curves::bn256::Fr;
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
const SIZE: usize = 1000;
|
||||
|
||||
criterion_main!(bench);
|
||||
|
||||
criterion_group!(bench, bench_fields);
|
||||
|
||||
fn bench_fields(c: &mut Criterion) {
|
||||
bench_field::<Goldilocks>(c, Goldilocks::NAME);
|
||||
bench_field::<GoldilocksExt2>(c, GoldilocksExt2::NAME);
|
||||
bench_field::<GoldilocksExt3>(c, GoldilocksExt3::NAME);
|
||||
bench_field::<Fr>(c, "Bn256 scalar")
|
||||
}
|
||||
|
||||
fn bench_field<F: Field>(c: &mut Criterion, field_name: &str) {
|
||||
let mut bench_group = c.benchmark_group(format!("{}", field_name));
|
||||
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
let a = (0..SIZE).map(|_| F::random(&mut rng)).collect::<Vec<_>>();
|
||||
let b = (0..SIZE).map(|_| F::random(&mut rng)).collect::<Vec<_>>();
|
||||
|
||||
let bench_str = format!("{} additions", SIZE);
|
||||
bench_group.bench_function(bench_str, |bencher| {
|
||||
bencher.iter(|| {
|
||||
a.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(&ai, &bi)| ai + bi)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
});
|
||||
|
||||
let bench_str = format!("{} multiplications", SIZE);
|
||||
bench_group.bench_function(bench_str, |bencher| {
|
||||
bencher.iter(|| {
|
||||
a.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(&ai, &bi)| ai * bi)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
});
|
||||
bench_group.finish();
|
||||
}
|
||||
20
src/field.rs
Normal file
20
src/field.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
//! This module defines our customized field trait.
|
||||
|
||||
use ff::PrimeField;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{fp2::GoldilocksExt2, Goldilocks, GoldilocksExt3};
|
||||
|
||||
pub trait SmallField: PrimeField + Serialize {
|
||||
const NAME: &'static str;
|
||||
}
|
||||
|
||||
impl SmallField for Goldilocks {
|
||||
const NAME: &'static str = "Goldilocks";
|
||||
}
|
||||
impl SmallField for GoldilocksExt2 {
|
||||
const NAME: &'static str = "GoldilocksExt2";
|
||||
}
|
||||
impl SmallField for GoldilocksExt3 {
|
||||
const NAME: &'static str = "GoldilocksExt3";
|
||||
}
|
||||
456
src/fp.rs
Normal file
456
src/fp.rs
Normal file
@@ -0,0 +1,456 @@
|
||||
use crate::util::{add_no_canonicalize_trashing_input, branch_hint, split, sqrt_tonelli_shanks};
|
||||
use crate::util::{assume, try_inverse_u64};
|
||||
use core::iter::{Product, Sum};
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use ff::{Field, PrimeField};
|
||||
use rand_core::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// Goldilocks field with modulus 2^64 - 2^32 + 1.
|
||||
/// A Goldilocks field may store a non-canonical form of the element
|
||||
/// where the value can be between 0 and 2^64.
|
||||
/// For unique representation of its form, use `to_canonical_u64`
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Serialize, Deserialize)]
|
||||
pub struct Goldilocks(pub(crate) u64);
|
||||
|
||||
impl PartialEq for Goldilocks {
|
||||
fn eq(&self, other: &Goldilocks) -> bool {
|
||||
self.to_canonical_u64() == other.to_canonical_u64()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Goldilocks {
|
||||
fn fmt(&self, w: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(w, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// 2^64 - 2^32 + 1
|
||||
pub const MODULUS: u64 = 0xffffffff00000001;
|
||||
/// 2^32 - 1
|
||||
pub const EPSILON: u64 = 0xffffffff;
|
||||
|
||||
impl Field for Goldilocks {
|
||||
/// The zero element of the field, the additive identity.
|
||||
const ZERO: Self = Self(0);
|
||||
|
||||
/// The one element of the field, the multiplicative identity.
|
||||
const ONE: Self = Self(1);
|
||||
|
||||
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||
/// Note: this sampler is not constant time!
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
let mut res = rng.next_u64();
|
||||
while res >= MODULUS {
|
||||
res = rng.next_u64();
|
||||
}
|
||||
Self(res)
|
||||
}
|
||||
|
||||
/// Squares this element.
|
||||
#[must_use]
|
||||
fn square(&self) -> Self {
|
||||
*self * *self
|
||||
}
|
||||
|
||||
/// Cubes this element.
|
||||
#[must_use]
|
||||
fn cube(&self) -> Self {
|
||||
self.square() * self
|
||||
}
|
||||
|
||||
/// Doubles this element.
|
||||
#[must_use]
|
||||
fn double(&self) -> Self {
|
||||
*self + *self
|
||||
}
|
||||
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
match try_inverse_u64(&self.0) {
|
||||
Some(p) => CtOption::new(Self(p), Choice::from(1)),
|
||||
None => CtOption::new(Self(0), Choice::from(0)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the square root of the field element, if it is
|
||||
/// quadratic residue.
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
// TODO: better algorithm taking advantage of (t-1)/2 has a nice structure
|
||||
sqrt_tonelli_shanks(self, 0x7fffffff)
|
||||
}
|
||||
|
||||
/// Computes:
|
||||
///
|
||||
/// - $(\textsf{true}, \sqrt{\textsf{num}/\textsf{div}})$, if $\textsf{num}$ and
|
||||
/// $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is a square in the
|
||||
/// field;
|
||||
/// - $(\textsf{true}, 0)$, if $\textsf{num}$ is zero;
|
||||
/// - $(\textsf{false}, 0)$, if $\textsf{num}$ is nonzero and $\textsf{div}$ is zero;
|
||||
/// - $(\textsf{false}, \sqrt{G_S \cdot \textsf{num}/\textsf{div}})$, if
|
||||
/// $\textsf{num}$ and $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is
|
||||
/// a nonsquare in the field;
|
||||
///
|
||||
/// where $G_S$ is a non-square.
|
||||
///
|
||||
/// # Warnings
|
||||
///
|
||||
/// - The choice of root from `sqrt` is unspecified.
|
||||
/// - The value of $G_S$ is unspecified, and cannot be assumed to have any specific
|
||||
/// value in a generic context.
|
||||
fn sqrt_ratio(_: &Self, _: &Self) -> (Choice, Self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<u64> for Goldilocks {
|
||||
fn as_ref(&self) -> &u64 {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for Goldilocks {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
let ptr = self as *mut Self as *mut u8;
|
||||
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
|
||||
// with alignment greater than that of u8
|
||||
unsafe { std::slice::from_raw_parts_mut(ptr, 8) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Goldilocks {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
let ptr = self as *const Self as *const u8;
|
||||
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
|
||||
// with alignment greater than that of u8
|
||||
unsafe { std::slice::from_raw_parts(ptr, 8) }
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents an element of a prime field.
|
||||
impl PrimeField for Goldilocks {
|
||||
/// The prime field can be converted back and forth into this binary
|
||||
/// representation.
|
||||
type Repr = Self;
|
||||
|
||||
/// Modulus of the field written as a string for debugging purposes.
|
||||
///
|
||||
/// The encoding of the modulus is implementation-specific. Generic users of the
|
||||
/// `PrimeField` trait should treat this string as opaque.
|
||||
const MODULUS: &'static str = "0xffffffff00000001";
|
||||
|
||||
/// How many bits are needed to represent an element of this field.
|
||||
const NUM_BITS: u32 = 64;
|
||||
|
||||
/// How many bits of information can be reliably stored in the field element.
|
||||
///
|
||||
/// This is usually `Self::NUM_BITS - 1`.
|
||||
const CAPACITY: u32 = 63;
|
||||
|
||||
/// An integer `s` satisfying the equation `2^s * t = modulus - 1` with `t` odd.
|
||||
///
|
||||
/// This is the number of leading zero bits in the little-endian bit representation of
|
||||
/// `modulus - 1`.
|
||||
const S: u32 = 32;
|
||||
|
||||
/// Inverse of $2$ in the field.
|
||||
const TWO_INV: Self = Self(0x7fffffff80000001);
|
||||
|
||||
/// A fixed multiplicative generator of `modulus - 1` order. This element must also be
|
||||
/// a quadratic nonresidue.
|
||||
///
|
||||
/// It can be calculated using [SageMath] as `GF(modulus).primitive_element()`.
|
||||
///
|
||||
/// Implementations of this trait MUST ensure that this is the generator used to
|
||||
/// derive `Self::ROOT_OF_UNITY`.
|
||||
///
|
||||
/// [SageMath]: https://www.sagemath.org/
|
||||
const MULTIPLICATIVE_GENERATOR: Self = Self(7);
|
||||
|
||||
/// The `2^s` root of unity.
|
||||
///
|
||||
/// It can be calculated by exponentiating `Self::MULTIPLICATIVE_GENERATOR` by `t`,
|
||||
/// where `t = (modulus - 1) >> Self::S`.
|
||||
const ROOT_OF_UNITY: Self = Self(0x185629dcda58878c);
|
||||
|
||||
/// Inverse of [`Self::ROOT_OF_UNITY`].
|
||||
const ROOT_OF_UNITY_INV: Self = Self(0x76b6b635b6fc8719);
|
||||
|
||||
/// Generator of the `t-order` multiplicative subgroup.
|
||||
///
|
||||
/// It can be calculated by exponentiating [`Self::MULTIPLICATIVE_GENERATOR`] by `2^s`,
|
||||
/// where `s` is [`Self::S`].
|
||||
const DELTA: Self = Self(0xaa5b2509f86bb4d4);
|
||||
|
||||
/// Attempts to convert a byte representation of a field element into an element of
|
||||
/// this prime field, failing if the input is not canonical (is not smaller than the
|
||||
/// field's modulus).
|
||||
///
|
||||
/// The byte representation is interpreted with the same endianness as elements
|
||||
/// returned by [`PrimeField::to_repr`].
|
||||
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
||||
CtOption::new(repr, Choice::from(1))
|
||||
}
|
||||
|
||||
/// Attempts to convert a byte representation of a field element into an element of
|
||||
/// this prime field, failing if the input is not canonical (is not smaller than the
|
||||
/// field's modulus).
|
||||
///
|
||||
/// The byte representation is interpreted with the same endianness as elements
|
||||
/// returned by [`PrimeField::to_repr`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This method provides **no** constant-time guarantees. Implementors of the
|
||||
/// `PrimeField` trait **may** optimise this method using non-constant-time logic.
|
||||
fn from_repr_vartime(repr: Self::Repr) -> Option<Self> {
|
||||
Self::from_repr(repr).into()
|
||||
}
|
||||
|
||||
/// Converts an element of the prime field into the standard byte representation for
|
||||
/// this field.
|
||||
///
|
||||
/// The endianness of the byte representation is implementation-specific. Generic
|
||||
/// encodings of field elements should be treated as opaque.
|
||||
fn to_repr(&self) -> Self::Repr {
|
||||
*self
|
||||
}
|
||||
|
||||
/// Returns true iff this element is odd.
|
||||
fn is_odd(&self) -> Choice {
|
||||
Choice::from((self.0 & 1) as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Goldilocks {
|
||||
fn from(input: u64) -> Self {
|
||||
Self(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Goldilocks> for u64 {
|
||||
fn from(input: Goldilocks) -> Self {
|
||||
input.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for Goldilocks {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Self(u64::conditional_select(&a.0, &b.0, choice))
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for Goldilocks {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.to_canonical_u64().ct_eq(&other.to_canonical_u64())
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Goldilocks {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
if self.0 == 0 {
|
||||
self
|
||||
} else {
|
||||
Self(MODULUS - self.to_canonical_u64())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Goldilocks {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
let (sum, over) = self.0.overflowing_add(rhs.0);
|
||||
let (mut sum, over) = sum.overflowing_add((over as u64) * EPSILON);
|
||||
if over {
|
||||
// NB: self.0 > Self::ORDER && rhs.0 > Self::ORDER is necessary but not sufficient for
|
||||
// double-overflow.
|
||||
// This assume does two things:
|
||||
// 1. If compiler knows that either self.0 or rhs.0 <= ORDER, then it can skip this
|
||||
// check.
|
||||
// 2. Hints to the compiler how rare this double-overflow is (thus handled better with
|
||||
// a branch).
|
||||
assume(self.0 > MODULUS && rhs.0 > MODULUS);
|
||||
branch_hint();
|
||||
sum += EPSILON; // Cannot overflow.
|
||||
}
|
||||
Self(sum)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<&'a Goldilocks> for Goldilocks {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'a Goldilocks) -> Self::Output {
|
||||
self + *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for Goldilocks {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddAssign<&'a Goldilocks> for Goldilocks {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: &'a Goldilocks) {
|
||||
*self = *self + *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Goldilocks {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||
fn sub(self, rhs: Self) -> Self {
|
||||
let (diff, under) = self.0.overflowing_sub(rhs.0);
|
||||
let (mut diff, under) = diff.overflowing_sub((under as u64) * EPSILON);
|
||||
if under {
|
||||
// NB: self.0 < EPSILON - 1 && rhs.0 > Self::ORDER is necessary but not sufficient for
|
||||
// double-underflow.
|
||||
// This assume does two things:
|
||||
// 1. If compiler knows that either self.0 >= EPSILON - 1 or rhs.0 <= ORDER, then it
|
||||
// can skip this check.
|
||||
// 2. Hints to the compiler how rare this double-underflow is (thus handled better
|
||||
// with a branch).
|
||||
assume(self.0 < EPSILON - 1 && rhs.0 > MODULUS);
|
||||
branch_hint();
|
||||
diff -= EPSILON; // Cannot underflow.
|
||||
}
|
||||
Self(diff)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<&'a Goldilocks> for Goldilocks {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'a Goldilocks) -> Self::Output {
|
||||
self - *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Goldilocks {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SubAssign<&'a Goldilocks> for Goldilocks {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: &'a Goldilocks) {
|
||||
*self = *self - *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::core::borrow::Borrow<Goldilocks>> Sum<T> for Goldilocks {
|
||||
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
|
||||
iter.fold(Self::ZERO, |acc, item| acc + item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Goldilocks {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
reduce128((self.0 as u128) * (rhs.0 as u128))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<&'a Goldilocks> for Goldilocks {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'a Goldilocks) -> Self::Output {
|
||||
self * *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for Goldilocks {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = *self * rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MulAssign<&'a Goldilocks> for Goldilocks {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'a Goldilocks) {
|
||||
*self = *self * *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::core::borrow::Borrow<Goldilocks>> Product<T> for Goldilocks {
|
||||
fn product<I: Iterator<Item = T>>(iter: I) -> Self {
|
||||
iter.fold(Self::ONE, |acc, item| acc * item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
/// Reduces to a 64-bit value. The result might not be in canonical form; it could be in between the
|
||||
/// field order and `2^64`.
|
||||
#[inline]
|
||||
fn reduce128(x: u128) -> Goldilocks {
|
||||
let (x_lo, x_hi) = split(x); // This is a no-op
|
||||
let x_hi_hi = x_hi >> 32;
|
||||
let x_hi_lo = x_hi & EPSILON;
|
||||
|
||||
let (mut t0, borrow) = x_lo.overflowing_sub(x_hi_hi);
|
||||
if borrow {
|
||||
branch_hint(); // A borrow is exceedingly rare. It is faster to branch.
|
||||
t0 -= EPSILON; // Cannot underflow.
|
||||
}
|
||||
let t1 = x_hi_lo * EPSILON;
|
||||
let t2 = unsafe { add_no_canonicalize_trashing_input(t0, t1) };
|
||||
Goldilocks(t2)
|
||||
}
|
||||
|
||||
impl Goldilocks {
|
||||
#[inline]
|
||||
pub fn to_canonical_u64(self) -> u64 {
|
||||
let mut c = self.0;
|
||||
// We only need one condition subtraction, since 2 * ORDER would not fit in a u64.
|
||||
if c >= MODULUS {
|
||||
c -= MODULUS;
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
pub const fn size() -> usize {
|
||||
8
|
||||
}
|
||||
|
||||
pub fn legendre(&self) -> LegendreSymbol {
|
||||
// s = self^((modulus - 1) // 2)
|
||||
// 9223372034707292160
|
||||
let s = 0x7fffffff80000000;
|
||||
let s = self.pow_vartime([s]);
|
||||
if s == Self::ZERO {
|
||||
LegendreSymbol::Zero
|
||||
} else if s == Self::ONE {
|
||||
LegendreSymbol::QuadraticResidue
|
||||
} else {
|
||||
LegendreSymbol::QuadraticNonResidue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum LegendreSymbol {
|
||||
Zero = 0,
|
||||
QuadraticResidue = 1,
|
||||
QuadraticNonResidue = -1,
|
||||
}
|
||||
366
src/fp2.rs
Normal file
366
src/fp2.rs
Normal file
@@ -0,0 +1,366 @@
|
||||
//! This module implements Goldilocks quadratic extension field mod x^2 + 1
|
||||
|
||||
use crate::Goldilocks;
|
||||
use core::iter::{Product, Sum};
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use ff::{Field, PrimeField};
|
||||
use rand_core::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// Degree 3 Goldilocks extension field mod x^2 + 1
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct GoldilocksExt2(pub [Goldilocks; 2]);
|
||||
|
||||
/// For a = (a1, a2) and b = (b1, b2)
|
||||
/// The multiplication is define as
|
||||
/// c := a * b = a(x) * b(x) % (x^2 + 1)
|
||||
/// = x*a2*b1 + x*a1*b2
|
||||
/// + a1*b1 - a2*b2
|
||||
|
||||
/// This requires 9 multiplications and 6 1 additions
|
||||
fn mul_internal(a: &GoldilocksExt2, b: &GoldilocksExt2) -> GoldilocksExt2 {
|
||||
// todo: optimizations?
|
||||
let a1b1 = a.0[0] * b.0[0];
|
||||
let a1b2 = a.0[0] * b.0[1];
|
||||
let a2b1 = a.0[1] * b.0[0];
|
||||
let a2b2 = a.0[1] * b.0[1];
|
||||
|
||||
let c1 = a1b1 - a2b2;
|
||||
let c2 = a2b1 + a1b2;
|
||||
GoldilocksExt2([c1, c2])
|
||||
}
|
||||
|
||||
impl GoldilocksExt2 {
|
||||
fn to_canonical_u64_array(&self) -> [u64; 2] {
|
||||
[self.0[0].to_canonical_u64(), self.0[1].to_canonical_u64()]
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for GoldilocksExt2 {
|
||||
/// The zero element of the field, the additive identity.
|
||||
const ZERO: Self = Self([Goldilocks::ZERO; 2]);
|
||||
|
||||
/// The one element of the field, the multiplicative identity.
|
||||
const ONE: Self = Self([Goldilocks::ONE, Goldilocks::ZERO]);
|
||||
|
||||
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||
/// Note: this sampler is not constant time!
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
let a1 = Goldilocks::random(&mut rng);
|
||||
let a2 = Goldilocks::random(&mut rng);
|
||||
|
||||
Self([a1, a2])
|
||||
}
|
||||
|
||||
/// Squares this element.
|
||||
#[must_use]
|
||||
fn square(&self) -> Self {
|
||||
*self * *self
|
||||
}
|
||||
|
||||
/// Cubes this element.
|
||||
#[must_use]
|
||||
fn cube(&self) -> Self {
|
||||
self.square() * self
|
||||
}
|
||||
|
||||
/// Doubles this element.
|
||||
#[must_use]
|
||||
fn double(&self) -> Self {
|
||||
*self + *self
|
||||
}
|
||||
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Returns the square root of the field element, if it is
|
||||
/// quadratic residue.
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Computes:
|
||||
///
|
||||
/// - $(\textsf{true}, \sqrt{\textsf{num}/\textsf{div}})$, if $\textsf{num}$ and
|
||||
/// $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is a square in the
|
||||
/// field;
|
||||
/// - $(\textsf{true}, 0)$, if $\textsf{num}$ is zero;
|
||||
/// - $(\textsf{false}, 0)$, if $\textsf{num}$ is nonzero and $\textsf{div}$ is zero;
|
||||
/// - $(\textsf{false}, \sqrt{G_S \cdot \textsf{num}/\textsf{div}})$, if
|
||||
/// $\textsf{num}$ and $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is
|
||||
/// a nonsquare in the field;
|
||||
///
|
||||
/// where $G_S$ is a non-square.
|
||||
///
|
||||
/// # Warnings
|
||||
///
|
||||
/// - The choice of root from `sqrt` is unspecified.
|
||||
/// - The value of $G_S$ is unspecified, and cannot be assumed to have any specific
|
||||
/// value in a generic context.
|
||||
fn sqrt_ratio(_: &Self, _: &Self) -> (Choice, Self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for GoldilocksExt2 {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Self([
|
||||
Goldilocks::conditional_select(&a.0[0], &b.0[0], choice),
|
||||
Goldilocks::conditional_select(&a.0[1], &b.0[1], choice),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for GoldilocksExt2 {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.to_canonical_u64_array()
|
||||
.ct_eq(&other.to_canonical_u64_array())
|
||||
}
|
||||
}
|
||||
impl Neg for GoldilocksExt2 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
Self([-self.0[0], -self.0[1]])
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for GoldilocksExt2 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<&'a GoldilocksExt2> for GoldilocksExt2 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'a GoldilocksExt2) -> Self::Output {
|
||||
self + *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for GoldilocksExt2 {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddAssign<&'a GoldilocksExt2> for GoldilocksExt2 {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: &'a GoldilocksExt2) {
|
||||
*self = *self + *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for GoldilocksExt2 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: Self) -> Self {
|
||||
Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<&'a GoldilocksExt2> for GoldilocksExt2 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'a GoldilocksExt2) -> Self::Output {
|
||||
self - *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for GoldilocksExt2 {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SubAssign<&'a GoldilocksExt2> for GoldilocksExt2 {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: &'a GoldilocksExt2) {
|
||||
*self = *self - *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::core::borrow::Borrow<GoldilocksExt2>> Sum<T> for GoldilocksExt2 {
|
||||
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
|
||||
iter.fold(Self::ZERO, |acc, item| acc + item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for GoldilocksExt2 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
mul_internal(&self, &rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<&'a GoldilocksExt2> for GoldilocksExt2 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'a GoldilocksExt2) -> Self::Output {
|
||||
self * *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for GoldilocksExt2 {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = *self * rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MulAssign<&'a GoldilocksExt2> for GoldilocksExt2 {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'a GoldilocksExt2) {
|
||||
*self = *self * *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::core::borrow::Borrow<GoldilocksExt2>> Product<T> for GoldilocksExt2 {
|
||||
fn product<I: Iterator<Item = T>>(iter: I) -> Self {
|
||||
iter.fold(Self::ONE, |acc, item| acc * item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for GoldilocksExt2 {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
let ptr = self as *mut Self as *mut u8;
|
||||
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
|
||||
// with alignment greater than that of u8
|
||||
unsafe { std::slice::from_raw_parts_mut(ptr, 16) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for GoldilocksExt2 {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
let ptr = self as *const Self as *const u8;
|
||||
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
|
||||
// with alignment greater than that of u8
|
||||
unsafe { std::slice::from_raw_parts(ptr, 16) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Goldilocks> for GoldilocksExt2 {
|
||||
fn from(a: Goldilocks) -> Self {
|
||||
Self([a, Goldilocks::ZERO])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for GoldilocksExt2 {
|
||||
fn from(a: u64) -> Self {
|
||||
Goldilocks::from(a).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents an element of a prime field.
|
||||
impl PrimeField for GoldilocksExt2 {
|
||||
/// The prime field can be converted back and forth into this binary
|
||||
/// representation.
|
||||
type Repr = Self;
|
||||
|
||||
/// Modulus of the field written as a string for debugging purposes.
|
||||
///
|
||||
/// The encoding of the modulus is implementation-specific. Generic users of the
|
||||
/// `PrimeField` trait should treat this string as opaque.
|
||||
const MODULUS: &'static str = "0xffffffff00000001";
|
||||
|
||||
/// How many bits are needed to represent an element of this field.
|
||||
const NUM_BITS: u32 = 64;
|
||||
|
||||
/// How many bits of information can be reliably stored in the field element.
|
||||
///
|
||||
/// This is usually `Self::NUM_BITS - 1`.
|
||||
const CAPACITY: u32 = 63;
|
||||
|
||||
/// An integer `s` satisfying the equation `2^s * t = modulus - 1` with `t` odd.
|
||||
///
|
||||
/// This is the number of leading zero bits in the little-endian bit representation of
|
||||
/// `modulus - 1`.
|
||||
const S: u32 = 32;
|
||||
|
||||
/// Inverse of $2$ in the field.
|
||||
const TWO_INV: Self = GoldilocksExt2([Goldilocks::TWO_INV, Goldilocks::ZERO]);
|
||||
|
||||
/// A fixed multiplicative generator of `modulus - 1` order. This element must also be
|
||||
/// a quadratic nonresidue.
|
||||
///
|
||||
/// It can be calculated using [SageMath] as `GF(modulus).primitive_element()`.
|
||||
///
|
||||
/// Implementations of this trait MUST ensure that this is the generator used to
|
||||
/// derive `Self::ROOT_OF_UNITY`.
|
||||
///
|
||||
/// [SageMath]: https://www.sagemath.org/
|
||||
const MULTIPLICATIVE_GENERATOR: Self =
|
||||
GoldilocksExt2([Goldilocks::MULTIPLICATIVE_GENERATOR, Goldilocks::ZERO]);
|
||||
|
||||
/// The `2^s` root of unity.
|
||||
///
|
||||
/// It can be calculated by exponentiating `Self::MULTIPLICATIVE_GENERATOR` by `t`,
|
||||
/// where `t = (modulus - 1) >> Self::S`.
|
||||
const ROOT_OF_UNITY: Self = GoldilocksExt2([Goldilocks::ROOT_OF_UNITY, Goldilocks::ZERO]);
|
||||
|
||||
/// Inverse of [`Self::ROOT_OF_UNITY`].
|
||||
const ROOT_OF_UNITY_INV: Self =
|
||||
GoldilocksExt2([Goldilocks::ROOT_OF_UNITY_INV, Goldilocks::ZERO]);
|
||||
|
||||
/// Generator of the `t-order` multiplicative subgroup.
|
||||
///
|
||||
/// It can be calculated by exponentiating [`Self::MULTIPLICATIVE_GENERATOR`] by `2^s`,
|
||||
/// where `s` is [`Self::S`].
|
||||
const DELTA: Self = GoldilocksExt2([Goldilocks::DELTA, Goldilocks::ZERO]);
|
||||
|
||||
/// Attempts to convert a byte representation of a field element into an element of
|
||||
/// this prime field, failing if the input is not canonical (is not smaller than the
|
||||
/// field's modulus).
|
||||
///
|
||||
/// The byte representation is interpreted with the same endianness as elements
|
||||
/// returned by [`PrimeField::to_repr`].
|
||||
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
||||
CtOption::new(repr, Choice::from(1))
|
||||
}
|
||||
|
||||
/// Attempts to convert a byte representation of a field element into an element of
|
||||
/// this prime field, failing if the input is not canonical (is not smaller than the
|
||||
/// field's modulus).
|
||||
///
|
||||
/// The byte representation is interpreted with the same endianness as elements
|
||||
/// returned by [`PrimeField::to_repr`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This method provides **no** constant-time guarantees. Implementors of the
|
||||
/// `PrimeField` trait **may** optimise this method using non-constant-time logic.
|
||||
fn from_repr_vartime(repr: Self::Repr) -> Option<Self> {
|
||||
Self::from_repr(repr).into()
|
||||
}
|
||||
|
||||
/// Converts an element of the prime field into the standard byte representation for
|
||||
/// this field.
|
||||
///
|
||||
/// The endianness of the byte representation is implementation-specific. Generic
|
||||
/// encodings of field elements should be treated as opaque.
|
||||
fn to_repr(&self) -> Self::Repr {
|
||||
*self
|
||||
}
|
||||
|
||||
/// Returns true iff this element is odd.
|
||||
fn is_odd(&self) -> Choice {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
396
src/fp3.rs
Normal file
396
src/fp3.rs
Normal file
@@ -0,0 +1,396 @@
|
||||
//! This module implements Goldilocks cubic extension field mod x^3-x-1
|
||||
|
||||
use crate::Goldilocks;
|
||||
use core::iter::{Product, Sum};
|
||||
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use ff::{Field, PrimeField};
|
||||
use rand_core::RngCore;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
/// Degree 3 Goldilocks extension field mod x^3-x-1
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct GoldilocksExt3(pub [Goldilocks; 3]);
|
||||
|
||||
/// For a = (a1, a2, a3) and b = (b1, b2, b3)
|
||||
/// The multiplication is define as
|
||||
/// c := a * b = a(x) * b(x) % (x^3-x-1)
|
||||
/// = x^2*a3*b1 + x^2*a2*b2 + x^2*a1*b3 + x^2*a3*b3
|
||||
/// + x*a2*b1 + x*a1*b2 + x*a3*b2 + x*a2*b3 + x*a3*b3
|
||||
/// + a1*b1 + a3*b2 + a2*b3
|
||||
/// This requires 9 multiplications and 6 1 additions
|
||||
fn mul_internal(a: &GoldilocksExt3, b: &GoldilocksExt3) -> GoldilocksExt3 {
|
||||
// todo: optimizations?
|
||||
let a1b1 = a.0[0] * b.0[0];
|
||||
let a1b2 = a.0[0] * b.0[1];
|
||||
let a1b3 = a.0[0] * b.0[2];
|
||||
let a2b1 = a.0[1] * b.0[0];
|
||||
let a2b2 = a.0[1] * b.0[1];
|
||||
let a2b3 = a.0[1] * b.0[2];
|
||||
let a3b1 = a.0[2] * b.0[0];
|
||||
let a3b2 = a.0[2] * b.0[1];
|
||||
let a3b3 = a.0[2] * b.0[2];
|
||||
|
||||
let c1 = a1b1 + a3b2 + a2b3;
|
||||
let c2 = a2b1 + a1b2 + a2b3 + a3b2 + a3b3;
|
||||
let c3 = a3b1 + a2b2 + a1b3 + a3b3;
|
||||
GoldilocksExt3([c1, c2, c3])
|
||||
}
|
||||
|
||||
impl GoldilocksExt3 {
|
||||
fn to_canonical_u64_array(&self) -> [u64; 3] {
|
||||
[
|
||||
self.0[0].to_canonical_u64(),
|
||||
self.0[1].to_canonical_u64(),
|
||||
self.0[2].to_canonical_u64(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl Field for GoldilocksExt3 {
|
||||
/// The zero element of the field, the additive identity.
|
||||
const ZERO: Self = Self([Goldilocks::ZERO; 3]);
|
||||
|
||||
/// The one element of the field, the multiplicative identity.
|
||||
const ONE: Self = Self([Goldilocks::ONE, Goldilocks::ZERO, Goldilocks::ZERO]);
|
||||
|
||||
/// Returns an element chosen uniformly at random using a user-provided RNG.
|
||||
/// Note: this sampler is not constant time!
|
||||
fn random(mut rng: impl RngCore) -> Self {
|
||||
let a1 = Goldilocks::random(&mut rng);
|
||||
let a2 = Goldilocks::random(&mut rng);
|
||||
let a3 = Goldilocks::random(&mut rng);
|
||||
|
||||
Self([a1, a2, a3])
|
||||
}
|
||||
|
||||
/// Squares this element.
|
||||
#[must_use]
|
||||
fn square(&self) -> Self {
|
||||
*self * *self
|
||||
}
|
||||
|
||||
/// Cubes this element.
|
||||
#[must_use]
|
||||
fn cube(&self) -> Self {
|
||||
self.square() * self
|
||||
}
|
||||
|
||||
/// Doubles this element.
|
||||
#[must_use]
|
||||
fn double(&self) -> Self {
|
||||
*self + *self
|
||||
}
|
||||
|
||||
/// Computes the multiplicative inverse of this element,
|
||||
/// failing if the element is zero.
|
||||
fn invert(&self) -> CtOption<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Returns the square root of the field element, if it is
|
||||
/// quadratic residue.
|
||||
fn sqrt(&self) -> CtOption<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Computes:
|
||||
///
|
||||
/// - $(\textsf{true}, \sqrt{\textsf{num}/\textsf{div}})$, if $\textsf{num}$ and
|
||||
/// $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is a square in the
|
||||
/// field;
|
||||
/// - $(\textsf{true}, 0)$, if $\textsf{num}$ is zero;
|
||||
/// - $(\textsf{false}, 0)$, if $\textsf{num}$ is nonzero and $\textsf{div}$ is zero;
|
||||
/// - $(\textsf{false}, \sqrt{G_S \cdot \textsf{num}/\textsf{div}})$, if
|
||||
/// $\textsf{num}$ and $\textsf{div}$ are nonzero and $\textsf{num}/\textsf{div}$ is
|
||||
/// a nonsquare in the field;
|
||||
///
|
||||
/// where $G_S$ is a non-square.
|
||||
///
|
||||
/// # Warnings
|
||||
///
|
||||
/// - The choice of root from `sqrt` is unspecified.
|
||||
/// - The value of $G_S$ is unspecified, and cannot be assumed to have any specific
|
||||
/// value in a generic context.
|
||||
fn sqrt_ratio(_: &Self, _: &Self) -> (Choice, Self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConditionallySelectable for GoldilocksExt3 {
|
||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||
Self([
|
||||
Goldilocks::conditional_select(&a.0[0], &b.0[0], choice),
|
||||
Goldilocks::conditional_select(&a.0[1], &b.0[1], choice),
|
||||
Goldilocks::conditional_select(&a.0[2], &b.0[2], choice),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantTimeEq for GoldilocksExt3 {
|
||||
fn ct_eq(&self, other: &Self) -> Choice {
|
||||
self.to_canonical_u64_array()
|
||||
.ct_eq(&other.to_canonical_u64_array())
|
||||
}
|
||||
}
|
||||
impl Neg for GoldilocksExt3 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
Self([-self.0[0], -self.0[1], -self.0[2]])
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for GoldilocksExt3 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self([
|
||||
self.0[0] + rhs.0[0],
|
||||
self.0[1] + rhs.0[1],
|
||||
self.0[2] + rhs.0[2],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Add<&'a GoldilocksExt3> for GoldilocksExt3 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &'a GoldilocksExt3) -> Self::Output {
|
||||
self + *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign for GoldilocksExt3 {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: Self) {
|
||||
*self = *self + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddAssign<&'a GoldilocksExt3> for GoldilocksExt3 {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: &'a GoldilocksExt3) {
|
||||
*self = *self + *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for GoldilocksExt3 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: Self) -> Self {
|
||||
Self([
|
||||
self.0[0] - rhs.0[0],
|
||||
self.0[1] - rhs.0[1],
|
||||
self.0[2] - rhs.0[2],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sub<&'a GoldilocksExt3> for GoldilocksExt3 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &'a GoldilocksExt3) -> Self::Output {
|
||||
self - *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for GoldilocksExt3 {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: Self) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SubAssign<&'a GoldilocksExt3> for GoldilocksExt3 {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: &'a GoldilocksExt3) {
|
||||
*self = *self - *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::core::borrow::Borrow<GoldilocksExt3>> Sum<T> for GoldilocksExt3 {
|
||||
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
|
||||
iter.fold(Self::ZERO, |acc, item| acc + item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for GoldilocksExt3 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
mul_internal(&self, &rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Mul<&'a GoldilocksExt3> for GoldilocksExt3 {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rhs: &'a GoldilocksExt3) -> Self::Output {
|
||||
self * *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for GoldilocksExt3 {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: Self) {
|
||||
*self = *self * rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MulAssign<&'a GoldilocksExt3> for GoldilocksExt3 {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, rhs: &'a GoldilocksExt3) {
|
||||
*self = *self * *rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ::core::borrow::Borrow<GoldilocksExt3>> Product<T> for GoldilocksExt3 {
|
||||
fn product<I: Iterator<Item = T>>(iter: I) -> Self {
|
||||
iter.fold(Self::ONE, |acc, item| acc * item.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[u8]> for GoldilocksExt3 {
|
||||
fn as_mut(&mut self) -> &mut [u8] {
|
||||
let ptr = self as *mut Self as *mut u8;
|
||||
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
|
||||
// with alignment greater than that of u8
|
||||
unsafe { std::slice::from_raw_parts_mut(ptr, 24) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for GoldilocksExt3 {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
let ptr = self as *const Self as *const u8;
|
||||
// SAFETY Self is repr(transparent) and u64 is 8 bytes wide,
|
||||
// with alignment greater than that of u8
|
||||
unsafe { std::slice::from_raw_parts(ptr, 24) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Goldilocks> for GoldilocksExt3 {
|
||||
fn from(a: Goldilocks) -> Self {
|
||||
Self([a, Goldilocks::ZERO, Goldilocks::ZERO])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for GoldilocksExt3 {
|
||||
fn from(a: u64) -> Self {
|
||||
Goldilocks::from(a).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// This represents an element of a prime field.
|
||||
impl PrimeField for GoldilocksExt3 {
|
||||
/// The prime field can be converted back and forth into this binary
|
||||
/// representation.
|
||||
type Repr = Self;
|
||||
|
||||
/// Modulus of the field written as a string for debugging purposes.
|
||||
///
|
||||
/// The encoding of the modulus is implementation-specific. Generic users of the
|
||||
/// `PrimeField` trait should treat this string as opaque.
|
||||
const MODULUS: &'static str = "0xffffffff00000001";
|
||||
|
||||
/// How many bits are needed to represent an element of this field.
|
||||
const NUM_BITS: u32 = 64;
|
||||
|
||||
/// How many bits of information can be reliably stored in the field element.
|
||||
///
|
||||
/// This is usually `Self::NUM_BITS - 1`.
|
||||
const CAPACITY: u32 = 63;
|
||||
|
||||
/// An integer `s` satisfying the equation `2^s * t = modulus - 1` with `t` odd.
|
||||
///
|
||||
/// This is the number of leading zero bits in the little-endian bit representation of
|
||||
/// `modulus - 1`.
|
||||
const S: u32 = 32;
|
||||
|
||||
/// Inverse of $2$ in the field.
|
||||
const TWO_INV: Self = GoldilocksExt3([Goldilocks::TWO_INV, Goldilocks::ZERO, Goldilocks::ZERO]);
|
||||
|
||||
/// A fixed multiplicative generator of `modulus - 1` order. This element must also be
|
||||
/// a quadratic nonresidue.
|
||||
///
|
||||
/// It can be calculated using [SageMath] as `GF(modulus).primitive_element()`.
|
||||
///
|
||||
/// Implementations of this trait MUST ensure that this is the generator used to
|
||||
/// derive `Self::ROOT_OF_UNITY`.
|
||||
///
|
||||
/// [SageMath]: https://www.sagemath.org/
|
||||
const MULTIPLICATIVE_GENERATOR: Self = GoldilocksExt3([
|
||||
Goldilocks::MULTIPLICATIVE_GENERATOR,
|
||||
Goldilocks::ZERO,
|
||||
Goldilocks::ZERO,
|
||||
]);
|
||||
|
||||
/// The `2^s` root of unity.
|
||||
///
|
||||
/// It can be calculated by exponentiating `Self::MULTIPLICATIVE_GENERATOR` by `t`,
|
||||
/// where `t = (modulus - 1) >> Self::S`.
|
||||
const ROOT_OF_UNITY: Self = GoldilocksExt3([
|
||||
Goldilocks::ROOT_OF_UNITY,
|
||||
Goldilocks::ZERO,
|
||||
Goldilocks::ZERO,
|
||||
]);
|
||||
|
||||
/// Inverse of [`Self::ROOT_OF_UNITY`].
|
||||
const ROOT_OF_UNITY_INV: Self = GoldilocksExt3([
|
||||
Goldilocks::ROOT_OF_UNITY_INV,
|
||||
Goldilocks::ZERO,
|
||||
Goldilocks::ZERO,
|
||||
]);
|
||||
|
||||
/// Generator of the `t-order` multiplicative subgroup.
|
||||
///
|
||||
/// It can be calculated by exponentiating [`Self::MULTIPLICATIVE_GENERATOR`] by `2^s`,
|
||||
/// where `s` is [`Self::S`].
|
||||
const DELTA: Self = GoldilocksExt3([Goldilocks::DELTA, Goldilocks::ZERO, Goldilocks::ZERO]);
|
||||
|
||||
/// Attempts to convert a byte representation of a field element into an element of
|
||||
/// this prime field, failing if the input is not canonical (is not smaller than the
|
||||
/// field's modulus).
|
||||
///
|
||||
/// The byte representation is interpreted with the same endianness as elements
|
||||
/// returned by [`PrimeField::to_repr`].
|
||||
fn from_repr(repr: Self::Repr) -> CtOption<Self> {
|
||||
CtOption::new(repr, Choice::from(1))
|
||||
}
|
||||
|
||||
/// Attempts to convert a byte representation of a field element into an element of
|
||||
/// this prime field, failing if the input is not canonical (is not smaller than the
|
||||
/// field's modulus).
|
||||
///
|
||||
/// The byte representation is interpreted with the same endianness as elements
|
||||
/// returned by [`PrimeField::to_repr`].
|
||||
///
|
||||
/// # Security
|
||||
///
|
||||
/// This method provides **no** constant-time guarantees. Implementors of the
|
||||
/// `PrimeField` trait **may** optimise this method using non-constant-time logic.
|
||||
fn from_repr_vartime(repr: Self::Repr) -> Option<Self> {
|
||||
Self::from_repr(repr).into()
|
||||
}
|
||||
|
||||
/// Converts an element of the prime field into the standard byte representation for
|
||||
/// this field.
|
||||
///
|
||||
/// The endianness of the byte representation is implementation-specific. Generic
|
||||
/// encodings of field elements should be treated as opaque.
|
||||
fn to_repr(&self) -> Self::Repr {
|
||||
*self
|
||||
}
|
||||
|
||||
/// Returns true iff this element is odd.
|
||||
fn is_odd(&self) -> Choice {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
16
src/lib.rs
Normal file
16
src/lib.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
//! This crate implements Goldilocks field with modulus 2^64 - 2^32 + 1
|
||||
//! Credit: the majority of the code is borrowed or inspired from Plonky2 with modifications.
|
||||
|
||||
pub use field::SmallField;
|
||||
pub use fp::Goldilocks;
|
||||
pub use fp2::GoldilocksExt2;
|
||||
pub use fp3::GoldilocksExt3;
|
||||
|
||||
mod field;
|
||||
mod fp;
|
||||
mod fp2;
|
||||
mod fp3;
|
||||
mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
241
src/tests.rs
Normal file
241
src/tests.rs
Normal file
@@ -0,0 +1,241 @@
|
||||
mod fp;
|
||||
mod fp2;
|
||||
mod fp3;
|
||||
|
||||
use ark_std::{end_timer, start_timer};
|
||||
use ff::Field;
|
||||
use ff::PrimeField;
|
||||
use rand_core::RngCore;
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
pub fn random_field_tests<F: Field>(type_name: String) {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
random_multiplication_tests::<F, _>(&mut rng, type_name.clone());
|
||||
random_addition_tests::<F, _>(&mut rng, type_name.clone());
|
||||
random_subtraction_tests::<F, _>(&mut rng, type_name.clone());
|
||||
random_negation_tests::<F, _>(&mut rng, type_name.clone());
|
||||
random_doubling_tests::<F, _>(&mut rng, type_name.clone());
|
||||
random_squaring_tests::<F, _>(&mut rng, type_name.clone());
|
||||
// random_inversion_tests::<F, _>(&mut rng, type_name.clone());
|
||||
random_expansion_tests::<F, _>(&mut rng, type_name);
|
||||
|
||||
assert_eq!(F::ZERO.is_zero().unwrap_u8(), 1);
|
||||
{
|
||||
let mut z = F::ZERO;
|
||||
z = z.neg();
|
||||
assert_eq!(z.is_zero().unwrap_u8(), 1);
|
||||
}
|
||||
|
||||
// assert!(bool::from(F::ZERO.invert().is_none()));
|
||||
|
||||
// Multiplication by zero
|
||||
{
|
||||
let mut a = F::random(&mut rng);
|
||||
a.mul_assign(&F::ZERO);
|
||||
assert_eq!(a.is_zero().unwrap_u8(), 1);
|
||||
}
|
||||
|
||||
// Addition by zero
|
||||
{
|
||||
let mut a = F::random(&mut rng);
|
||||
let copy = a;
|
||||
a.add_assign(&F::ZERO);
|
||||
assert_eq!(a, copy);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_multiplication_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
|
||||
let message = format!("multiplication {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
let a = F::random(&mut rng);
|
||||
let b = F::random(&mut rng);
|
||||
let c = F::random(&mut rng);
|
||||
|
||||
let mut t0 = a; // (a * b) * c
|
||||
t0.mul_assign(&b);
|
||||
t0.mul_assign(&c);
|
||||
|
||||
let mut t1 = a; // (a * c) * b
|
||||
t1.mul_assign(&c);
|
||||
t1.mul_assign(&b);
|
||||
|
||||
let mut t2 = b; // (b * c) * a
|
||||
t2.mul_assign(&c);
|
||||
t2.mul_assign(&a);
|
||||
|
||||
assert_eq!(t0, t1);
|
||||
assert_eq!(t1, t2);
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
|
||||
fn random_addition_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
|
||||
let message = format!("addition {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
let a = F::random(&mut rng);
|
||||
let b = F::random(&mut rng);
|
||||
let c = F::random(&mut rng);
|
||||
|
||||
let mut t0 = a; // (a + b) + c
|
||||
t0.add_assign(&b);
|
||||
t0.add_assign(&c);
|
||||
|
||||
let mut t1 = a; // (a + c) + b
|
||||
t1.add_assign(&c);
|
||||
t1.add_assign(&b);
|
||||
|
||||
let mut t2 = b; // (b + c) + a
|
||||
t2.add_assign(&c);
|
||||
t2.add_assign(&a);
|
||||
|
||||
assert_eq!(t0, t1);
|
||||
assert_eq!(t1, t2);
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
|
||||
fn random_subtraction_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
|
||||
let message = format!("subtraction {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
let a = F::random(&mut rng);
|
||||
let b = F::random(&mut rng);
|
||||
|
||||
let mut t0 = a; // (a - b)
|
||||
t0.sub_assign(&b);
|
||||
|
||||
let mut t1 = b; // (b - a)
|
||||
t1.sub_assign(&a);
|
||||
|
||||
let mut t2 = t0; // (a - b) + (b - a) = 0
|
||||
t2.add_assign(&t1);
|
||||
|
||||
assert_eq!(t2.is_zero().unwrap_u8(), 1);
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
|
||||
fn random_negation_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
|
||||
let message = format!("negation {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
let a = F::random(&mut rng);
|
||||
let mut b = a;
|
||||
b = b.neg();
|
||||
b.add_assign(&a);
|
||||
|
||||
assert_eq!(b.is_zero().unwrap_u8(), 1);
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
|
||||
fn random_doubling_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
|
||||
let message = format!("doubling {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
let mut a = F::random(&mut rng);
|
||||
let mut b = a;
|
||||
a.add_assign(&b);
|
||||
b = b.double();
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
|
||||
fn random_squaring_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
|
||||
let message = format!("squaring {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
let mut a = F::random(&mut rng);
|
||||
let mut b = a;
|
||||
a.mul_assign(&b);
|
||||
b = b.square();
|
||||
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn random_inversion_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
|
||||
assert!(bool::from(F::ZERO.invert().is_none()));
|
||||
|
||||
let message = format!("inversion {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
let mut a = F::random(&mut rng);
|
||||
let b = a.invert().unwrap(); // probabilistically nonzero
|
||||
a.mul_assign(&b);
|
||||
assert_eq!(a, F::ONE);
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
|
||||
fn random_expansion_tests<F: Field, R: RngCore>(mut rng: R, type_name: String) {
|
||||
let message = format!("expansion {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
// Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d)
|
||||
|
||||
let a = F::random(&mut rng);
|
||||
let b = F::random(&mut rng);
|
||||
let c = F::random(&mut rng);
|
||||
let d = F::random(&mut rng);
|
||||
|
||||
let mut t0 = a;
|
||||
t0.add_assign(&b);
|
||||
let mut t1 = c;
|
||||
t1.add_assign(&d);
|
||||
t0.mul_assign(&t1);
|
||||
|
||||
let mut t2 = a;
|
||||
t2.mul_assign(&c);
|
||||
let mut t3 = b;
|
||||
t3.mul_assign(&c);
|
||||
let mut t4 = a;
|
||||
t4.mul_assign(&d);
|
||||
let mut t5 = b;
|
||||
t5.mul_assign(&d);
|
||||
|
||||
t2.add_assign(&t3);
|
||||
t2.add_assign(&t4);
|
||||
t2.add_assign(&t5);
|
||||
|
||||
assert_eq!(t0, t2);
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
|
||||
pub fn random_prime_field_tests<F: PrimeField>(type_name: String) {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
random_serdes_tests::<F, _>(&mut rng, type_name);
|
||||
}
|
||||
|
||||
fn random_serdes_tests<F: PrimeField, R: RngCore>(mut rng: R, type_name: String) {
|
||||
let message = format!("expansion {}", type_name);
|
||||
let start = start_timer!(|| message);
|
||||
for _ in 0..1000000 {
|
||||
// convert a into and from repr
|
||||
|
||||
let a = F::random(&mut rng);
|
||||
let b = a.to_repr();
|
||||
let c = F::from_repr(b).unwrap();
|
||||
let d = c.to_repr();
|
||||
|
||||
assert_eq!(a, c);
|
||||
assert_eq!(b.as_ref(), d.as_ref());
|
||||
}
|
||||
end_timer!(start);
|
||||
}
|
||||
57
src/tests/fp.rs
Normal file
57
src/tests/fp.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use std::ops::Neg;
|
||||
|
||||
use ff::Field;
|
||||
use rand_core::SeedableRng;
|
||||
use rand_xorshift::XorShiftRng;
|
||||
|
||||
use super::random_field_tests;
|
||||
use super::random_prime_field_tests;
|
||||
use crate::fp::Goldilocks;
|
||||
use crate::fp::LegendreSymbol;
|
||||
|
||||
#[test]
|
||||
fn test_sqrt_fq() {
|
||||
let mut rng = XorShiftRng::from_seed([
|
||||
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
|
||||
0xe5,
|
||||
]);
|
||||
|
||||
let two_inv = Goldilocks(0x7fffffff80000001);
|
||||
let v = two_inv.square().sqrt().unwrap();
|
||||
assert!(v == two_inv || (-v) == two_inv);
|
||||
|
||||
for _ in 0..10000 {
|
||||
let a = Goldilocks::random(&mut rng);
|
||||
let mut b = a;
|
||||
b = b.square();
|
||||
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||
|
||||
let b = b.sqrt().unwrap();
|
||||
let mut negb = b;
|
||||
negb = negb.neg();
|
||||
assert!(a == b || a == negb);
|
||||
}
|
||||
|
||||
let mut c = Goldilocks::ONE;
|
||||
for _ in 0..10000 {
|
||||
let mut b = c;
|
||||
b = b.square();
|
||||
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
|
||||
|
||||
b = b.sqrt().unwrap();
|
||||
|
||||
if b != c {
|
||||
b = b.neg();
|
||||
}
|
||||
|
||||
assert_eq!(b, c);
|
||||
|
||||
c += &Goldilocks::ONE;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_field() {
|
||||
random_field_tests::<Goldilocks>("Goldilocks".to_string());
|
||||
random_prime_field_tests::<Goldilocks>("Goldilocks".to_string());
|
||||
}
|
||||
18
src/tests/fp2.rs
Normal file
18
src/tests/fp2.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use super::random_field_tests;
|
||||
use super::random_prime_field_tests;
|
||||
use crate::fp::Goldilocks;
|
||||
use crate::fp2::GoldilocksExt2;
|
||||
|
||||
#[test]
|
||||
fn test_field() {
|
||||
random_field_tests::<GoldilocksExt2>("GoldilocksExt3".to_string());
|
||||
random_prime_field_tests::<GoldilocksExt2>("GoldilocksExt3".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_answer_tests() {
|
||||
let a = GoldilocksExt2([Goldilocks::from(1), Goldilocks::from(2)]);
|
||||
let b = GoldilocksExt2([Goldilocks::from(3), Goldilocks::from(4)]);
|
||||
let c = GoldilocksExt2([-Goldilocks::from(5), Goldilocks::from(10)]);
|
||||
assert_eq!(a * b, c)
|
||||
}
|
||||
30
src/tests/fp3.rs
Normal file
30
src/tests/fp3.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use super::random_field_tests;
|
||||
use super::random_prime_field_tests;
|
||||
use crate::fp::Goldilocks;
|
||||
use crate::fp3::GoldilocksExt3;
|
||||
|
||||
#[test]
|
||||
fn test_field() {
|
||||
random_field_tests::<GoldilocksExt3>("GoldilocksExt3".to_string());
|
||||
random_prime_field_tests::<GoldilocksExt3>("GoldilocksExt3".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_answer_tests() {
|
||||
let a = GoldilocksExt3([
|
||||
Goldilocks::from(1),
|
||||
Goldilocks::from(2),
|
||||
Goldilocks::from(3),
|
||||
]);
|
||||
let b = GoldilocksExt3([
|
||||
Goldilocks::from(4),
|
||||
Goldilocks::from(5),
|
||||
Goldilocks::from(6),
|
||||
]);
|
||||
let c = GoldilocksExt3([
|
||||
Goldilocks::from(31),
|
||||
Goldilocks::from(58),
|
||||
Goldilocks::from(46),
|
||||
]);
|
||||
assert_eq!(a * b, c)
|
||||
}
|
||||
292
src/util.rs
Normal file
292
src/util.rs
Normal file
@@ -0,0 +1,292 @@
|
||||
use std::arch::asm;
|
||||
use std::hint::unreachable_unchecked;
|
||||
|
||||
use ff::{Field, PrimeField};
|
||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||
|
||||
use crate::fp::{Goldilocks, MODULUS};
|
||||
|
||||
#[inline(always)]
|
||||
pub fn assume(p: bool) {
|
||||
debug_assert!(p);
|
||||
if !p {
|
||||
unsafe {
|
||||
unreachable_unchecked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to force Rust to emit a branch. Example:
|
||||
/// if x > 2 {
|
||||
/// y = foo();
|
||||
/// branch_hint();
|
||||
/// } else {
|
||||
/// y = bar();
|
||||
/// }
|
||||
/// This function has no semantics. It is a hint only.
|
||||
#[inline(always)]
|
||||
pub fn branch_hint() {
|
||||
unsafe {
|
||||
asm!("", options(nomem, nostack, preserves_flags));
|
||||
}
|
||||
}
|
||||
|
||||
/// Fast addition modulo ORDER for x86-64.
|
||||
/// This function is marked unsafe for the following reasons:
|
||||
/// - It is only correct if x + y < 2**64 + ORDER = 0x1ffffffff00000001.
|
||||
/// - It is only faster in some circumstances. In particular, on x86 it overwrites both inputs in
|
||||
/// the registers, so its use is not recommended when either input will be used again.
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub(crate) unsafe fn add_no_canonicalize_trashing_input(x: u64, y: u64) -> u64 {
|
||||
let res_wrapped: u64;
|
||||
let adjustment: u64;
|
||||
asm!(
|
||||
"add {0}, {1}",
|
||||
// Trick. The carry flag is set iff the addition overflowed.
|
||||
// sbb x, y does x := x - y - CF. In our case, x and y are both {1:e}, so it simply does
|
||||
// {1:e} := 0xffffffff on overflow and {1:e} := 0 otherwise. {1:e} is the low 32 bits of
|
||||
// {1}; the high 32-bits are zeroed on write. In the end, we end up with 0xffffffff in {1}
|
||||
// on overflow; this happens be EPSILON.
|
||||
// Note that the CPU does not realize that the result of sbb x, x does not actually depend
|
||||
// on x. We must write the result to a register that we know to be ready. We have a
|
||||
// dependency on {1} anyway, so let's use it.
|
||||
"sbb {1:e}, {1:e}",
|
||||
inlateout(reg) x => res_wrapped,
|
||||
inlateout(reg) y => adjustment,
|
||||
options(pure, nomem, nostack),
|
||||
);
|
||||
assume(x != 0 || (res_wrapped == y && adjustment == 0));
|
||||
assume(y != 0 || (res_wrapped == x && adjustment == 0));
|
||||
// Add EPSILON == subtract ORDER.
|
||||
// Cannot overflow unless the assumption if x + y < 2**64 + ORDER is incorrect.
|
||||
res_wrapped + adjustment
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
pub(crate) unsafe fn add_no_canonicalize_trashing_input(x: u64, y: u64) -> u64 {
|
||||
let (res_wrapped, carry) = x.overflowing_add(y);
|
||||
// Below cannot overflow unless the assumption if x + y < 2**64 + ORDER is incorrect.
|
||||
res_wrapped + EPSILON * (carry as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn split(x: u128) -> (u64, u64) {
|
||||
(x as u64, (x >> 64) as u64)
|
||||
}
|
||||
|
||||
/// Try to invert an element in a prime field.
|
||||
///
|
||||
/// The algorithm below is the "plus-minus-inversion" method
|
||||
/// with an "almost Montgomery inverse" flair. See Handbook of
|
||||
/// Elliptic and Hyperelliptic Cryptography, Algorithms 11.6
|
||||
/// and 11.12.
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn try_inverse_u64(x: &u64) -> Option<u64> {
|
||||
let mut f = *x;
|
||||
let mut g = MODULUS;
|
||||
// NB: These two are very rarely such that their absolute
|
||||
// value exceeds (p-1)/2; we are paying the price of i128 for
|
||||
// the whole calculation, just for the times they do
|
||||
// though. Measurements suggest a further 10% time saving if c
|
||||
// and d could be replaced with i64's.
|
||||
let mut c = 1i128;
|
||||
let mut d = 0i128;
|
||||
|
||||
if f == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// f and g must always be odd.
|
||||
let mut k = f.trailing_zeros();
|
||||
f >>= k;
|
||||
if f == 1 {
|
||||
return Some(inverse_2exp(k as usize));
|
||||
}
|
||||
|
||||
// The first two iterations are unrolled. This is to handle
|
||||
// the case where f and g are both large and f+g can
|
||||
// overflow. log2(max{f,g}) goes down by at least one each
|
||||
// iteration though, so after two iterations we can be sure
|
||||
// that f+g won't overflow.
|
||||
|
||||
// Iteration 1:
|
||||
safe_iteration(&mut f, &mut g, &mut c, &mut d, &mut k);
|
||||
|
||||
if f == 1 {
|
||||
// c must be -1 or 1 here.
|
||||
if c == -1 {
|
||||
return Some(MODULUS - inverse_2exp(k as usize));
|
||||
}
|
||||
debug_assert!(c == 1, "bug in try_inverse_u64");
|
||||
return Some(inverse_2exp(k as usize));
|
||||
}
|
||||
|
||||
// Iteration 2:
|
||||
safe_iteration(&mut f, &mut g, &mut c, &mut d, &mut k);
|
||||
|
||||
// Remaining iterations:
|
||||
while f != 1 {
|
||||
unsafe {
|
||||
unsafe_iteration(&mut f, &mut g, &mut c, &mut d, &mut k);
|
||||
}
|
||||
}
|
||||
|
||||
// The following two loops adjust c so it's in the canonical range
|
||||
// [0, F::ORDER).
|
||||
|
||||
// The maximum number of iterations observed here is 2; should
|
||||
// prove this.
|
||||
while c < 0 {
|
||||
c += MODULUS as i128;
|
||||
}
|
||||
|
||||
// The maximum number of iterations observed here is 1; should
|
||||
// prove this.
|
||||
while c >= MODULUS as i128 {
|
||||
c -= MODULUS as i128;
|
||||
}
|
||||
|
||||
// Precomputing the binary inverses rather than using inverse_2exp
|
||||
// saves ~5ns on my machine.
|
||||
let res = Goldilocks(c as u64) * Goldilocks(inverse_2exp(k as usize));
|
||||
debug_assert!(
|
||||
Goldilocks(*x) * res == Goldilocks::ONE,
|
||||
"bug in try_inverse_u64"
|
||||
);
|
||||
Some(res.0)
|
||||
}
|
||||
|
||||
/// This is a 'safe' iteration for the modular inversion algorithm. It
|
||||
/// is safe in the sense that it will produce the right answer even
|
||||
/// when f + g >= 2^64.
|
||||
#[inline(always)]
|
||||
fn safe_iteration(f: &mut u64, g: &mut u64, c: &mut i128, d: &mut i128, k: &mut u32) {
|
||||
if f < g {
|
||||
std::mem::swap(f, g);
|
||||
std::mem::swap(c, d);
|
||||
}
|
||||
if *f & 3 == *g & 3 {
|
||||
// f - g = 0 (mod 4)
|
||||
*f -= *g;
|
||||
*c -= *d;
|
||||
|
||||
// kk >= 2 because f is now 0 (mod 4).
|
||||
let kk = f.trailing_zeros();
|
||||
*f >>= kk;
|
||||
*d <<= kk;
|
||||
*k += kk;
|
||||
} else {
|
||||
// f + g = 0 (mod 4)
|
||||
*f = (*f >> 2) + (*g >> 2) + 1u64;
|
||||
*c += *d;
|
||||
let kk = f.trailing_zeros();
|
||||
*f >>= kk;
|
||||
*d <<= kk + 2;
|
||||
*k += kk + 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an 'unsafe' iteration for the modular inversion
|
||||
/// algorithm. It is unsafe in the sense that it might produce the
|
||||
/// wrong answer if f + g >= 2^64.
|
||||
#[inline(always)]
|
||||
unsafe fn unsafe_iteration(f: &mut u64, g: &mut u64, c: &mut i128, d: &mut i128, k: &mut u32) {
|
||||
if *f < *g {
|
||||
std::mem::swap(f, g);
|
||||
std::mem::swap(c, d);
|
||||
}
|
||||
if *f & 3 == *g & 3 {
|
||||
// f - g = 0 (mod 4)
|
||||
*f -= *g;
|
||||
*c -= *d;
|
||||
} else {
|
||||
// f + g = 0 (mod 4)
|
||||
*f += *g;
|
||||
*c += *d;
|
||||
}
|
||||
|
||||
// kk >= 2 because f is now 0 (mod 4).
|
||||
let kk = f.trailing_zeros();
|
||||
*f >>= kk;
|
||||
*d <<= kk;
|
||||
*k += kk;
|
||||
}
|
||||
|
||||
/// Compute the inverse of 2^exp in this field.
|
||||
#[inline]
|
||||
fn inverse_2exp(exp: usize) -> u64 {
|
||||
// Let p = char(F). Since 2^exp is in the prime subfield, i.e. an
|
||||
// element of GF_p, its inverse must be as well. Thus we may add
|
||||
// multiples of p without changing the result. In particular,
|
||||
// 2^-exp = 2^-exp - p 2^-exp
|
||||
// = 2^-exp (1 - p)
|
||||
// = p - (p - 1) / 2^exp
|
||||
|
||||
// If this field's two adicity, t, is at least exp, then 2^exp divides
|
||||
// p - 1, so this division can be done with a simple bit shift. If
|
||||
// exp > t, we repeatedly multiply by 2^-t and reduce exp until it's in
|
||||
// the right range.
|
||||
|
||||
// NB: The only reason this is split into two cases is to save
|
||||
// the multiplication (and possible calculation of
|
||||
// inverse_2_pow_adicity) in the usual case that exp <=
|
||||
// TWO_ADICITY. Can remove the branch and simplify if that
|
||||
// saving isn't worth it.
|
||||
let res = if exp > 32 {
|
||||
// NB: This should be a compile-time constant
|
||||
// MODULUS - ((MODULUS - 1) >> 32)
|
||||
let inverse_2_pow_adicity = Goldilocks(0xfffffffe00000002);
|
||||
|
||||
let mut res = inverse_2_pow_adicity;
|
||||
let mut e = exp - 32;
|
||||
|
||||
while e > 32 {
|
||||
res *= inverse_2_pow_adicity;
|
||||
e -= 32;
|
||||
}
|
||||
res * Goldilocks(MODULUS - ((MODULUS - 1) >> e))
|
||||
} else {
|
||||
Goldilocks(MODULUS - ((MODULUS - 1) >> exp))
|
||||
};
|
||||
res.0
|
||||
}
|
||||
|
||||
pub(crate) fn sqrt_tonelli_shanks(f: &Goldilocks, tm1d2: u64) -> CtOption<Goldilocks> {
|
||||
// w = self^((t - 1) // 2)
|
||||
let w = f.pow_vartime([tm1d2]);
|
||||
|
||||
let mut v = Goldilocks::S;
|
||||
let mut x = w * f;
|
||||
let mut b = x * w;
|
||||
|
||||
// Initialize z as the 2^S root of unity.
|
||||
let mut z = Goldilocks::ROOT_OF_UNITY;
|
||||
|
||||
for max_v in (1..=Goldilocks::S).rev() {
|
||||
let mut k = 1;
|
||||
let mut tmp = b.square();
|
||||
let mut j_less_than_v: Choice = 1.into();
|
||||
|
||||
for j in 2..max_v {
|
||||
let tmp_is_one = tmp.ct_eq(&Goldilocks::ONE);
|
||||
let squared = Goldilocks::conditional_select(&tmp, &z, tmp_is_one).square();
|
||||
tmp = Goldilocks::conditional_select(&squared, &tmp, tmp_is_one);
|
||||
let new_z = Goldilocks::conditional_select(&z, &squared, tmp_is_one);
|
||||
j_less_than_v &= !j.ct_eq(&v);
|
||||
k = u32::conditional_select(&j, &k, tmp_is_one);
|
||||
z = Goldilocks::conditional_select(&z, &new_z, j_less_than_v);
|
||||
}
|
||||
|
||||
let result = x * z;
|
||||
x = Goldilocks::conditional_select(&result, &x, b.ct_eq(&Goldilocks::ONE));
|
||||
z = z.square();
|
||||
b *= z;
|
||||
v = k;
|
||||
}
|
||||
CtOption::new(
|
||||
x,
|
||||
(x * x).ct_eq(f), // Only return Some if it's the square root.
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user