commit 26e512394eff10dda8297f18b30d1310076b9fb4 Author: Cian Hughes Date: Wed Jul 19 16:38:52 2023 +0100 First dev commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b169044 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +/.vscode +/tmp +*.ipynb \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f2e7446 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,709 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[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.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f644d0dac522c8b05ddc39aaaccc5b136d5dc4ff216610c5641e3be5becf56c" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af410122b9778e024f9e0fb35682cc09cc3f85cad5e8d3ba8f47a9702df6e73d" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[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-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[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.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastmath" +version = "0.1.0" +dependencies = [ + "bincode", + "criterion", + "num-traits", + "once_cell", + "serde", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[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", +] + +[[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.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[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 = "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 = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[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.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +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", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..53b4b0f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "fastmath" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bincode = "1.3.3" +num-traits = "0.2.15" +once_cell = "1.18.0" +serde = {version = "1.0.171", features = ["derive"] } + +[build-dependencies] +bincode = "1.3.3" +num-traits = "0.2.15" +serde = {version = "1.0.171", features = ["derive"] } + +[lib] +name = "fastmath" +path = "src/lib.rs" +test = true +bench = true + +[[bench]] +name = "bench" +harness = false + +[dev-dependencies] +criterion = "0.5.1" diff --git a/benches/bench.rs b/benches/bench.rs new file mode 100644 index 0000000..838ab4d --- /dev/null +++ b/benches/bench.rs @@ -0,0 +1,111 @@ +#![allow(unused_imports)] +extern crate fastmath; + +use fastmath::*; +use criterion::{Criterion, BenchmarkGroup, measurement::WallTime}; +use criterion::{black_box, criterion_group, criterion_main}; + +fn pow2_benchmarks(group: &mut BenchmarkGroup, x_f64: &[f64], x_f32: &[f32]) { + group.bench_function("f64_fast", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x).fast_pow2()).collect::>()) + }); + group.bench_function("f64_builtin_fn", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x).powi(2)).collect::>()) + }); + group.bench_function("f64_builtin_mul", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x) * x).collect::>()) + }); + group.bench_function("f32_fast", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x).fast_pow2()).collect::>()) + }); + group.bench_function("f32_builtin_fn", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x).powi(2)).collect::>()) + }); + group.bench_function("f32_builtin_mul", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x) * x).collect::>()) + }); +} + +fn exp_benchmarks(group: &mut BenchmarkGroup, x_f64: &[f64], x_f32: &[f32]) { + group.bench_function("f64_fast", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x).fast_exp()).collect::>()) + }); + group.bench_function("f64_builtin", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x).exp()).collect::>()) + }); + group.bench_function("f32_fast", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x).fast_exp()).collect::>()) + }); + group.bench_function("f32_builtin", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x).exp()).collect::>()) + }); +} + +fn cos_benchmarks(group: &mut BenchmarkGroup, x_f64: &[f64], x_f32: &[f32]) { + group.bench_function("f64_fast", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x).fast_cos()).collect::>()) + }); + group.bench_function("f64_lookup", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x).lookup_cos()).collect::>()) + }); + group.bench_function("f64_builtin", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x).cos()).collect::>()) + }); + group.bench_function("f32_fast", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x).fast_cos()).collect::>()) + }); + group.bench_function("f32_lookup", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x).lookup_cos()).collect::>()) + }); + group.bench_function("f32_builtin", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x).cos()).collect::>()) + }); +} + +fn sigmoid_benchmarks(group: &mut BenchmarkGroup, x_f64: &[f64], x_f32: &[f32]) { + group.bench_function("f64_fast", |b| { + b.iter(|| x_f64.iter().map(|&x| black_box(x).fast_sigmoid()).collect::>()) + }); + group.bench_function("f64_builtin", |b| { + b.iter(|| x_f64.iter().map(|&x| sigmoid_builtin_f64(black_box(x))).collect::>()) + }); + group.bench_function("f32_fast", |b| { + b.iter(|| x_f32.iter().map(|&x| black_box(x).fast_sigmoid()).collect::>()) + }); + group.bench_function("f32_builtin", |b| { + b.iter(|| x_f32.iter().map(|&x| sigmoid_builtin_f32(black_box(x))).collect::>()) + }); +} + +fn criterion_benchmark(c: &mut Criterion) { + // Prepare x values for testing functions + let x_f64 = (-10000..10000) + .map(|a| (a as f64) / 1000.) + .collect::>(); + let x_f32 = (-10000..10000) + .map(|a| (a as f32) / 1000.) + .collect::>(); + // to ensure tests are fair, we need to instantiate the lookup tables + 1.0f64.lookup_cos(); + 1.0f32.lookup_cos(); + // Then, tests can begin + let mut group = c.benchmark_group("pow2"); + pow2_benchmarks(&mut group, &x_f64, &x_f32); + group.finish(); + + let mut group = c.benchmark_group("exp"); + exp_benchmarks(&mut group, &x_f64, &x_f32); + group.finish(); + + let mut group = c.benchmark_group("cos"); + cos_benchmarks(&mut group, &x_f64, &x_f32); + group.finish(); + + let mut group = c.benchmark_group("sigmoid"); + sigmoid_benchmarks(&mut group, &x_f64, &x_f32); + group.finish(); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); + diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..f76240b --- /dev/null +++ b/build.rs @@ -0,0 +1,49 @@ +// build.rs + +mod precalculate_lookup_tables { + use std::fs::{create_dir_all, File}; + use std::io::Write; + include!("src/lookup/lookup_table.rs"); + use bincode::serialize; + + const PRECISION: usize = 1000; + + fn precalculate_sin_tables() -> Result<(), Box> { + let data = serialize(&EndoSinLookupTable::::new(PRECISION))?; + let mut file = File::create("src/lookup/data/sin_f32.bin")?; + file.write_all(&data)?; + + let data = serialize(&EndoSinLookupTable::::new(PRECISION))?; + let mut file = File::create("src/lookup/data/sin_f64.bin")?; + file.write_all(&data)?; + + Ok(()) + } + + fn precalculate_cos_tables() -> Result<(), Box> { + let data = serialize(&EndoCosLookupTable::::new(PRECISION))?; + let mut file = File::create("src/lookup/data/cos_f32.bin")?; + file.write_all(&data)?; + + let data = serialize(&EndoCosLookupTable::::new(PRECISION))?; + let mut file = File::create("src/lookup/data/cos_f64.bin")?; + file.write_all(&data)?; + + Ok(()) + } + + pub fn generate() -> Result<(), Box> { + create_dir_all("src/lookup/data")?; + + precalculate_sin_tables()?; + precalculate_cos_tables()?; + + Ok(()) + } +} + +fn main() -> Result<(), Box> { + precalculate_lookup_tables::generate()?; + + Ok(()) +} \ No newline at end of file diff --git a/src/fastmath.rs b/src/fastmath.rs new file mode 100644 index 0000000..64b79ac --- /dev/null +++ b/src/fastmath.rs @@ -0,0 +1,208 @@ +//! A collection of fast (often approximate) mathematical functions for accelerating mathematical functions + +// Optimisation note: lookup tables become faster when calculation takes > ~1ms + +use std::f32::consts as f32_consts; +use std::f64::consts as f64_consts; +use crate::lookup::*; + +pub trait FastMath: FastCos + FastPow2 + FastExp + FastSigmoid {} +impl FastMath for f32 {} +impl FastMath for f64 {} + + +pub trait LookupCos { + fn lookup_cos(self: Self) -> Self; +} +impl LookupCos for f64 { + #[inline] + fn lookup_cos(self: Self) -> f64 { + // Look up the value in the table + COS_LOOKUP_F64.lookup(self) + } +} +impl LookupCos for f32 { + #[inline] + fn lookup_cos(self: Self) -> f32 { + // Look up the value in the table + COS_LOOKUP_F32.lookup(self) + } +} + +pub trait FastCos { + fn fast_cos(self: Self) -> Self; +} +impl FastCos for f32 { + #[inline] + fn fast_cos(self: Self) -> f32 { + const BITAND: u32 = u32::MAX / 2; + const ONE: f32 = 1.0; + let mod_x = (((self + f32_consts::PI).abs()) % f32_consts::TAU) - f32_consts::PI; + let v = mod_x.to_bits() & BITAND; + let qpprox = ONE - f32_consts::FRAC_2_PI * f32::from_bits(v); + qpprox + f32_consts::FRAC_PI_6 * qpprox * (ONE - qpprox * qpprox) + } +} +impl FastCos for f64 { + #[inline] + fn fast_cos(self: Self) -> f64 { + const BITAND: u64 = u64::MAX / 2; + const ONE: f64 = 1.0; + let mod_x = (((self + f64_consts::PI).abs()) % f64_consts::TAU) - f64_consts::PI; + let v = mod_x.to_bits() & BITAND; + let qpprox = ONE - f64_consts::FRAC_2_PI * f64::from_bits(v); + qpprox + f64_consts::FRAC_PI_6 * qpprox * (ONE - qpprox * qpprox) + } +} + +pub trait FastPow2 { + fn fast_pow2(self: Self) -> Self; +} +impl FastPow2 for f32 { + #[inline] + fn fast_pow2(self: Self) -> f32 { + // Khinchins constant over 3. IDK why it gives the best fit, but it does + const KHINCHIN_3: f32 = 2.68545200106530644530971483548179569382038229399446295305115234555721885953715200280114117493184769799515 / 3.0; + const CLIPP_THRESH: f32 = 0.12847338; + const V_SCALE: f32 = 8388608.0; // (1_i32 << 23) as f32 + const CLIPP_SHIFT: f32 = 126.67740855; + let abs_p = self.abs(); + let clipp = abs_p.max(CLIPP_THRESH); // if abs_p < CLIPP_THRESH { CLIPP_THRESH } else { abs_p }; + let v = (V_SCALE * (clipp + CLIPP_SHIFT)) as u32; + f32::from_bits(v) - KHINCHIN_3 + } +} +impl FastPow2 for f64 { + #[inline] + fn fast_pow2(self: Self) -> f64 { + const KHINCHIN_3: f64 = 2.68545200106530644530971483548179569382038229399446295305115234555721885953715200280114117493184769799515 / 3.0; + const CLIPP_THRESH: f64 = -45774.9247660416; + const V_SCALE: f64 = 4503599627370496.0; // (1i64 << 52) as f64 + const CLIPP_SHIFT: f64 = 1022.6769200000002; + const ZERO: f64 = 0.; + let abs_p = self.abs(); + let clipp = abs_p.max(CLIPP_THRESH); // if abs_p < CLIPP_THRESH { CLIPP_THRESH } else { abs_p }; + let v = (V_SCALE * (clipp + CLIPP_SHIFT)) as u64; + let y = f64::from_bits(v) - KHINCHIN_3; + if y.is_sign_positive() { + y + } else { + ZERO + } + } +} + +pub trait FastExp { + fn fast_exp(self: Self) -> Self; +} +impl FastExp for f32 { + #[inline] + fn fast_exp(self: Self) -> f32 { + const CLIPP_THRESH: f32 = -126.0; // 0.12847338; + const V_SCALE: f32 = 8388608.0; // (1_i32 << 23) as f32 + const CLIPP_SHIFT: f32 = 126.94269504; // 126.67740855; + + let scaled_p = f32_consts::LOG2_E * self; + let clipp = scaled_p.max(CLIPP_THRESH); // if scaled_p < CLIPP_THRESH { CLIPP_THRESH } else { scaled_p }; + let v = (V_SCALE * (clipp + CLIPP_SHIFT)) as u32; + f32::from_bits(v) + } +} +impl FastExp for f64 { + #[inline] + fn fast_exp(self: Self) -> f64 { + const CLIPP_THRESH: f64 = -180335.51911105003; + const V_SCALE: f64 = 4524653012949098.0; + const CLIPP_SHIFT: f64 = 1018.1563534409383; + + let scaled_p = f64_consts::LOG2_E * self; + let clipp = scaled_p.max(CLIPP_THRESH); // let clipp = if scaled_p < CLIPP_THRESH { CLIPP_THRESH } else { scaled_p }; + let v = (V_SCALE * (clipp + CLIPP_SHIFT)) as u64; + f64::from_bits(v) + } +} + +pub trait FastSigmoid { + fn fast_sigmoid(self: Self) -> Self; +} +impl FastSigmoid for f32 { + #[inline] + fn fast_sigmoid(self: Self) -> f32 { + const ONE: f32 = 1.0; + (ONE + (-self).fast_exp()).recip() + } +} +impl FastSigmoid for f64 { + #[inline] + fn fast_sigmoid(self: Self) -> f64 { + const ONE: f64 = 1.0; + (ONE + (-self).fast_exp()).recip() + } +} + +// A trait for testing and improving implementations of fast functions +pub trait Test { + fn test(self: Self) -> Self; +} +impl Test for f32 { + #[inline] + fn test(self: Self) -> f32 { + // Khinchins constant over 3. IDK why it gives the best fit, but it does + // const KHINCHIN_3: f32 = 2.68545200106530644530971483548179569382038229399446295305115234555721885953715200280114117493184769799515 / 3.0; + const CLIPP_THRESH: f32 = -126.0; // 0.12847338; + const V_SCALE: f32 = 8388608.0; // (1_i32 << 23) as f32 + const CLIPP_SHIFT: f32 = 126.94269504; // 126.67740855; + + let scaled_p = f32_consts::LOG2_E * self; + let clipp = if scaled_p < CLIPP_THRESH { + CLIPP_THRESH + } else { + scaled_p + }; + let v = (V_SCALE * (clipp + CLIPP_SHIFT)) as u32; + f32::from_bits(v) // - KHINCHIN_3 + } +} +impl Test for f64 { + #[inline] + fn test(self: Self) -> f64 { + const CLIPP_THRESH: f64 = -180335.51911105003; + const V_SCALE: f64 = 4524653012949098.0; + const CLIPP_SHIFT: f64 = 1018.1563534409383; + + let scaled_p = f64_consts::LOG2_E * self; + let clipp = if scaled_p < CLIPP_THRESH { + CLIPP_THRESH + } else { + scaled_p + }; + let v = (V_SCALE * (clipp + CLIPP_SHIFT)) as u64; + f64::from_bits(v) + } +} + +#[allow(non_snake_case, dead_code)] +pub fn optimizing(p: f64, CLIPP_THRESH: f64, V_SCALE: f64, CLIPP_SHIFT: f64) -> f64 { + // const CLIPP_THRESH: f64 = -45774.9247660416; + // const V_SCALE: f64 = 4503599627370496.0; + // const CLIPP_SHIFT: f64 = 1022.6769200000002; + + let scaled_p = f64_consts::LOG2_E * p; + let clipp = if scaled_p < CLIPP_THRESH { + CLIPP_THRESH + } else { + scaled_p + }; + let v = (V_SCALE * (clipp + CLIPP_SHIFT)) as u64; + f64::from_bits(v) +} + +#[inline] +pub fn sigmoid_builtin_f32(p: f32) -> f32 { + (1. + (-p).exp()).recip() +} + +#[inline] +pub fn sigmoid_builtin_f64(p: f64) -> f64 { + (1. + (-p).exp()).recip() +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7427728 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +//lib.rs +#![allow(unused_imports)] + +pub mod lookup; +mod fastmath; + +pub use fastmath::*; + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/src/lookup/const_tables.rs b/src/lookup/const_tables.rs new file mode 100644 index 0000000..31a064e --- /dev/null +++ b/src/lookup/const_tables.rs @@ -0,0 +1,30 @@ +// lookup/const_tables.rs + +use once_cell::sync::Lazy; +use std::fs::read; +use bincode::deserialize; +use super::lookup_table::*; + +pub const SIN_LOOKUP_F32: Lazy> = Lazy::new(|| { + deserialize( + &read("src/lookup/data/sin_f32.bin").expect("Failed to read sin_f64.bin") + ).expect("Failed to load SIN_LOOKUP_F32") +}); + +pub const SIN_LOOKUP_F64: Lazy> = Lazy::new(|| { + deserialize( + &read("src/lookup/data/sin_f64.bin").expect("Failed to read sin_f32.bin") + ).expect("Failed to load SIN_LOOKUP_F64") +}); + +pub const COS_LOOKUP_F32: Lazy> = Lazy::new(|| { + deserialize( + &read("src/lookup/data/cos_f32.bin").expect("Failed to read cos_f64.bin") + ).expect("Failed to load COS_LOOKUP_F32") +}); + +pub const COS_LOOKUP_F64: Lazy> = Lazy::new(|| { + deserialize( + &read("src/lookup/data/cos_f64.bin").expect("Failed to read cos_f32.bin") + ).expect("Failed to load COS_LOOKUP_F64") +}); diff --git a/src/lookup/data/cos_f32.bin b/src/lookup/data/cos_f32.bin new file mode 100644 index 0000000..78252d9 Binary files /dev/null and b/src/lookup/data/cos_f32.bin differ diff --git a/src/lookup/data/cos_f64.bin b/src/lookup/data/cos_f64.bin new file mode 100644 index 0000000..0a7b3ac Binary files /dev/null and b/src/lookup/data/cos_f64.bin differ diff --git a/src/lookup/data/sin_f32.bin b/src/lookup/data/sin_f32.bin new file mode 100644 index 0000000..78252d9 Binary files /dev/null and b/src/lookup/data/sin_f32.bin differ diff --git a/src/lookup/data/sin_f64.bin b/src/lookup/data/sin_f64.bin new file mode 100644 index 0000000..0a7b3ac Binary files /dev/null and b/src/lookup/data/sin_f64.bin differ diff --git a/src/lookup/lookup_table.rs b/src/lookup/lookup_table.rs new file mode 100644 index 0000000..0431979 --- /dev/null +++ b/src/lookup/lookup_table.rs @@ -0,0 +1,149 @@ +use num_traits::sign::Signed; +use num_traits::float::{Float, FloatConst}; +use num_traits::NumCast; +use std::ops::{Sub, Rem}; +use serde::{Serialize, Deserialize}; +// use packed_simd::f64x4; + +#[derive(Default, Debug, Serialize, Deserialize, Clone)] +pub struct FloatLookupTable +where T1: Float, + T2: Float, +{ + keys: Vec, + values: Vec, +} +impl FloatLookupTable +where T1: Float, + T2: Float, +{ + pub fn new(mut keys: Vec, mut values: Vec) -> Self { + let mut indices: Vec<_> = (0..keys.len()).collect(); + indices.sort_by(|&i, &j| keys[i].partial_cmp(&keys[j]).unwrap()); + for i in 0..keys.len() { + while i != indices[i] { + let swap_index = indices[i]; + keys.swap(i, swap_index); + values.swap(i, swap_index); + indices.swap(i, swap_index); + } + } + FloatLookupTable { keys, values } + } + + #[allow(dead_code)] + pub fn lookup(&self, key: T1) -> T2 { + match self.keys.binary_search_by(|probe| probe.partial_cmp(&key).unwrap()) { + Ok(index) => self.values[index], + Err(index) => { + let upper_key = &self.keys[index]; + let upper_val = &self.values[index]; + let low_index = index - 1; + let lower_key = &self.keys[low_index]; + let lower_val = &self.values[low_index]; + // select nearest neighbour + let diff_upper = (key - *upper_key).abs(); + let diff_lower = (key - *lower_key).abs(); + let mask = diff_lower <= diff_upper; + (*lower_val * T2::from(mask as u8).expect("Failed to unwrap mask")) + + (*upper_val * T2::from(!mask as u8).expect("Failed to unwrap !mask")) + } + } + } +} + + +#[derive(Default, Debug, Serialize, Deserialize, Clone)] +pub struct CyclingFloatLookupTable +where T1: Float, + T2: Float, +{ + lookup_table: FloatLookupTable, + lower_bound: T1, + upper_bound: T1, + bound_range: T1, +} +impl CyclingFloatLookupTable +where T1: Float, + T2: Float, +{ + pub fn new(keys: Vec, values: Vec, lower_bound: T1, upper_bound: T1) -> Self { + CyclingFloatLookupTable { + lookup_table: FloatLookupTable::new(keys, values), + lower_bound: lower_bound, + upper_bound: upper_bound, + bound_range: upper_bound - lower_bound, + } + } + + pub fn lookup(&self, key: T1) -> T2 { + let key = (key % self.bound_range) + self.lower_bound; + self.lookup_table.lookup(key) + } +} + + +#[derive(Default, Debug, Serialize, Deserialize, Clone)] +pub struct EndoSinLookupTable +where + T: Float + FloatConst, +{ + lookup_table: CyclingFloatLookupTable, +} +impl EndoSinLookupTable +where + T: Float + FloatConst, +{ + pub fn new(precision: usize) -> Self { + let mut keys = Vec::with_capacity(precision); + let mut values = Vec::with_capacity(precision); + let upper_bound = T::PI(); + let step = T::FRAC_PI_2() / ::from(precision).unwrap(); + for i in 0..precision+1 { + let key = step * ::from(i).unwrap(); + let value = key.sin(); + keys.push(key); + values.push(value); + } + EndoSinLookupTable { + lookup_table: CyclingFloatLookupTable::new(keys, values, T::zero(), upper_bound), + } + } + + #[allow(dead_code)] + pub fn lookup(&self, key: T) -> T { + if key < T::zero() { + -self.lookup(-key) + } else if key < T::FRAC_PI_2() { + self.lookup_table.lookup(key) + } else if key < T::PI() { + self.lookup_table.lookup(T::PI() - key) + } else { + -self.lookup(key - T::PI()) + } + } +} + + +#[derive(Default, Debug, Serialize, Deserialize, Clone)] +pub struct EndoCosLookupTable +where + T: Float + FloatConst + Signed + Sub + Rem + NumCast + From, +{ + lookup_table: EndoSinLookupTable, +} +impl EndoCosLookupTable +where + T: Float + FloatConst + Signed + Sub + Rem + NumCast + From, +{ + pub fn new(precision: usize) -> Self { + EndoCosLookupTable { + lookup_table: EndoSinLookupTable::new(precision), + } + } + + #[allow(dead_code)] + pub fn lookup(&self, key: T) -> T { + self.lookup_table.lookup(key + T::FRAC_PI_2()) + } +} \ No newline at end of file diff --git a/src/lookup/mod.rs b/src/lookup/mod.rs new file mode 100644 index 0000000..ce14091 --- /dev/null +++ b/src/lookup/mod.rs @@ -0,0 +1,4 @@ +mod const_tables; +pub mod lookup_table; + +pub use const_tables::*; \ No newline at end of file diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..f7e78a1 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,156 @@ +//tests.rs + +use num_traits::Float; + +fn calculate_percentage_error(vector1: &[T], vector2: &[T]) -> T + where T: Float + std::ops::AddAssign, +{ + let n = vector1.len(); + assert_eq!(n, vector2.len(), "Vectors must have equal lengths."); + + let mut total_error = T::zero(); + for i in 0..n { + let diff = (vector1[i] - vector2[i]).abs(); + let error = diff / if vector1[i] == T::zero() { T::min_positive_value() } else { vector1[i] }; + total_error += error; + } + + let average_error = total_error / T::from(n).unwrap(); + let percentage_error = average_error * T::from(100).expect("Cannot convert 100 to type T"); + percentage_error +} + +#[cfg(test)] +mod tests { + mod f64_error { + use crate::*; + use super::super::calculate_percentage_error; + use once_cell::sync::Lazy; + + const TOLERANCE: f64 = 2.5; + + static X: Lazy> = Lazy::new(|| { + (-10000..10000) + .map(|a| (a as f64) / 1000.) + .collect::>() + }); + + #[test] + fn pow2() -> Result<(), Box> { + let percentage_error = calculate_percentage_error( + &X.iter().map(|&x| x.fast_pow2()).collect::>(), + &X.iter().map(|&x| x.powi(2)).collect::>() + ); + assert!(!percentage_error.is_nan(), "fast_pow2 percentage error is NaN"); + assert!( + percentage_error < TOLERANCE, + "fast_pow2 percentage error: {0}", + percentage_error + ); + Ok(()) + } + + #[test] + fn exp() -> Result<(), Box> { + let percentage_error = calculate_percentage_error( + &X.iter().map(|&x| x.fast_exp()).collect::>(), + &X.iter().map(|&x| x.exp()).collect::>() + ); + assert!(!percentage_error.is_nan(), "fast_exp percentage error is NaN"); + assert!( + percentage_error < TOLERANCE, + "fast_exp percentage error: {0}", + percentage_error + ); + Ok(()) + } + + #[test] + fn cos() -> Result<(), Box> { + let percentage_error = calculate_percentage_error( + &X.iter().map(|&x| x.fast_cos()).collect::>(), + &X.iter().map(|&x| x.cos()).collect::>() + ); + assert!(!percentage_error.is_nan(), "fast_cos percentage error is NaN"); + assert!( + percentage_error < TOLERANCE, + "fast_cos percentage error: {0}", + percentage_error + ); + Ok(()) + } + + #[test] + fn sigmoid() -> Result<(), Box> { + let percentage_error = calculate_percentage_error( + &X.iter().map(|&x| x.fast_sigmoid()).collect::>(), + &X.iter().map(|&x| sigmoid_builtin_f64(x)).collect::>() + ); + assert!(!percentage_error.is_nan(), "fast_sigmoid percentage error is NaN"); + assert!( + percentage_error < TOLERANCE, + "fast_sigmoid percentage error: {0}", + percentage_error + ); + Ok(()) + } + } + + mod f32_error { + use crate::*; + use super::super::calculate_percentage_error; + use once_cell::sync::Lazy; + + const TOLERANCE: f32 = 2.5; + + static X: Lazy> = Lazy::new(|| { + (-10000..10000) + .map(|a| (a as f32) / 1000.) + .collect::>() + }); + + #[test] + fn pow2() -> Result<(), Box> { + assert!( + calculate_percentage_error( + &X.iter().map(|&x| x.fast_pow2()).collect::>(), + &X.iter().map(|&x| x.powi(2)).collect::>() + ) < TOLERANCE + ); + Ok(()) + } + + #[test] + fn exp() -> Result<(), Box> { + assert!( + calculate_percentage_error( + &X.iter().map(|&x| x.fast_exp()).collect::>(), + &X.iter().map(|&x| x.exp()).collect::>() + ) < TOLERANCE + ); + Ok(()) + } + + #[test] + fn cos() -> Result<(), Box> { + assert!( + calculate_percentage_error( + &X.iter().map(|&x| x.fast_cos()).collect::>(), + &X.iter().map(|&x| x.cos()).collect::>() + ) < TOLERANCE + ); + Ok(()) + } + + #[test] + fn sigmoid() -> Result<(), Box> { + assert!( + calculate_percentage_error( + &X.iter().map(|&x| x.fast_sigmoid()).collect::>(), + &X.iter().map(|&x| sigmoid_builtin_f32(x)).collect::>() + ) < TOLERANCE + ); + Ok(()) + } + } +} \ No newline at end of file