Merge "Update num-bigint to 0.4.6." into main
diff --git a/crates/num-bigint/.android-checksum.json b/crates/num-bigint/.android-checksum.json
index 784a38e..c8260be 100644
--- a/crates/num-bigint/.android-checksum.json
+++ b/crates/num-bigint/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"c474d7b1e972aaac3ba2ad751456181f9352609ba5afaa55d79c729908f429ce","Android.bp":"4cdcb535e9fe5c0e81c99ea109648b98e5e5995b887704c5b3c30e60c5419abc","Cargo.toml":"3c5e9454ee88f5777011f279313358e81298e01224f5bea7b854dc483393c526","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"16693a68efd65bc6a9df3448c8f7ee6745418c73faff4ec4d443648c04285fa4","METADATA":"ebb9e723d418045170c370000a45160f22aac6704ce2737c3db66e81017b4621","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"f940329d227cacb8c6764bdfa83a347ca4d21504b2a82d872cd0456d3a70d58c","RELEASES.md":"82711df67e4677900233d9723dc0b733f7310db714ba12c4967bed945d9b31bf","TEST_MAPPING":"c7bad5ec6f7bf48bb856a231acfacaa7f39e438507a269c058f5250421ed683e","benches/bigint.rs":"9e883892483328f2698770d0b5563de5a2034379ac399b13cb5dc372d48669da","benches/factorial.rs":"6a0d78154597de3d3a469e661a2d0d29b0c095696d5387ddd6be4f0cc3da236b","benches/gcd.rs":"e66e9488678528c5386f43bcd1498da579a18b0d7a203b90e459f3fae80ba435","benches/rng/mod.rs":"9d49b4584c9bc6b3b1cbab51b08e00334b09b26b7a758491e8208ec7f21fe0b8","benches/roots.rs":"5d83ab3461ba7ed36b1a6c1250d73519f72bfd7309f2046b5d5437c8d3e68ae5","benches/shootout-pidigits.rs":"1393ffbdc2555e34818ba81a5bf9eb28c8568b71196bad4e811b504d145bea2e","build.rs":"d4a11772dfd067dd98f9657b17a3f8d3bb5f9ae69a5737102b2236b5fd90ef92","cargo_embargo.json":"be3a30d8e5d9126130b453bb35f83bfeaf4275f85e8209865cca2a6609eb4b2d","out/radix_bases.rs":"ed7b02913dc9ea14d5ac607b4c0cb9726549c7350cd9e70aaef564ad97d08112","patches/disable_tests.patch":"8e6908c72054213761d720a7fea46347042fb14b322f1d02216d7c4739a516da","patches/pin-autocfg-version.patch":"ce923d0868aff09af2454e3d09ba8f744609d96419d0da81710e8e8d911cde97","patches/remove-probe-files.patch":"f814eb4a30ccadd11bf62f70067c4fc2b7bee3dfaf05aff2e297af2b1afabd22","src/bigint.rs":"3763bb5be008327bad22ead79e7b2ba841de5f16b6ddb2e6fdcc3fa01d70c745","src/bigint/addition.rs":"582b2e34e48f04a2f9a569b644ff27588e1aa08a1de8ea2cb26edb327514c097","src/bigint/arbitrary.rs":"7d001a5966d0d4d9c69435270b38da102ac03804dba593433653f99d30da9bb8","src/bigint/bits.rs":"3b68b5af1a019923ac110571d7274ff11396ea9a9b1c8068b76be2e0cf99a957","src/bigint/convert.rs":"57e6ad3809058e69a80682c88e58a47105ccc7be1c953cbf219f874733deba74","src/bigint/division.rs":"5f402a3b99fdd8b710ecb4b395b56dda87d0dc3f0e052d82a1d37fca55a04971","src/bigint/multiplication.rs":"10605872fae3ee6e6af13ab7588151b390a11bd0c1b8234947334ffb2d4a0bc8","src/bigint/power.rs":"1d8f4ddef1881ee12b8047c46b1c38271e117aaf64d3ca0d5b0b36c7f200cb60","src/bigint/serde.rs":"6dcef1946d8249c87f3f310e4172fe2d1c8b37fff48924ed9e596ff0eda132f3","src/bigint/shift.rs":"7090e677289786c25e0a6ec13e89b9775dda240e624e4add5ac34f8ff08d70e1","src/bigint/subtraction.rs":"90dae2623ae729cef303fc96010a801481f5a2e0d351dfe51c34467dcf7fb206","src/bigrand.rs":"eb962c11890dff31976d04b964ee6f53b9067bdeb92f72abe05cc492bd255250","src/biguint.rs":"e2d196b385477da6646f3cfeff44b7f285361b362c575625c88d9ffdc7791eee","src/biguint/addition.rs":"60d474ce33a375a733587d8a76a5913f831b81fd3f95f14c734117fe266eedca","src/biguint/arbitrary.rs":"723d7f2b9aaff1cf88b11722a9ea6fc9f1eb7c359d41151f4b7f8eb0dcadbb59","src/biguint/bits.rs":"07fef255da30a2bb10d5c34dacb3c2b2a84c872dcefcaaa5c41bcd1519fe97e8","src/biguint/convert.rs":"0f6f1c6bae3bcc30a0e8ae1f1050c37b20c494d63ec4ff676eb2e86a54ce8ed0","src/biguint/division.rs":"db5fe4e18e62c435d72ff90f4941b401caac527e701f187beb9c0aafe7647029","src/biguint/iter.rs":"40c4d38ef3a0a8d5b85094e06bf6acc82027bc606f68f0cd6b76895b370179aa","src/biguint/monty.rs":"0880fad70df7893f8b2a0f74fae9a2ef51b1f9ff1ea641128d4f64d02e11fe77","src/biguint/multiplication.rs":"71f5bec327b9f9cacdf4a611176475a09449bcc51d34aa8ff495964ccf383982","src/biguint/power.rs":"c1bc2fcd128ffbdfe108dda83e34abc17d8a8e5b3ed60e9c854514cedda7d079","src/biguint/serde.rs":"7ec071edbaadcc7daa17d86d9039e84f1e530eb38337574e935afae607c12803","src/biguint/shift.rs":"d80b5790cf6658a6346c3b225cfe8bce516409ce84c1b0c2a7581655b7c480e6","src/biguint/subtraction.rs":"c23bd19d477d36bc0e4e628291debbc62e62b6419fd0fffacf5f999c68fcd0b7","src/lib.rs":"a2a71876248018ad59fc13622886dbbb9f83e71e07da63185ca03856f36be60b","src/macros.rs":"80b081a2600e235f78a9f8bd8cecbbcee925c3624a351ed4a1301852238beac0","tests/bigint.rs":"7fa53731766e0239db3e9faf1a1ba257704ddc21a2c0dc282398123df93a224e","tests/bigint_bitwise.rs":"f99856586df07fd0126f363b1bee30924135ba14657fb296cc2e35624d2afbac","tests/bigint_scalar.rs":"0ac683c29a4b8f883eca986b3234530fc493fb3e6f0cc16858093f49846492cc","tests/biguint.rs":"aa65b2e68b2d285641c88a4eb35eb99d198bd9e6a1bc2db5e3e3f56da5429962","tests/biguint_scalar.rs":"c8d90abf23d75f603c7b2e8f475959319a2374bd285fc7fc9a9d31e7f7ee4bab","tests/consts/mod.rs":"873ed51d89e18ba0da3c11a985f6a3f0df8be189fa41c2cf8b5e4c25233a9ae5","tests/fuzzed.rs":"b01ddbada1bdee35563ccb953deee3476f1a8cd6daa7de0e1a708bd0845c56fa","tests/macros/mod.rs":"20098c9bd76d007dec91fddf9a99196f9e3455485ca1f7165528597b18b39975","tests/modpow.rs":"fa80f4701af352e301161a465a9f4d374eed799a7de7f239bb4ec0857b1b9377","tests/roots.rs":"849dbe2c32b9488e4f094c1aaaa55f059602fb166b317bccfd01998d54b46474"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"626b607b73b3e005fe49806848857f387f5d6222276fde1628eb5f37de94cca5","Android.bp":"862279e0c8dbab3d68e6ca66cec24f617cd8c57d4cf712489da2b9f79eef31cb","Cargo.toml":"c192cc08038c59c085bc092ae6d73182d8e16c4fc41c88477aa943c33eb7d692","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"16693a68efd65bc6a9df3448c8f7ee6745418c73faff4ec4d443648c04285fa4","METADATA":"7de11c53a3a58ad7394dfa79b309e07bdec5ae6322ca3972f06a5fd83607a5f8","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"50e069a2328fc53a4c2b4186f9cb22f80271c4eba13bb65fe30966c76657ea11","RELEASES.md":"36c08807a67002d7fa82327ca5ebcf6553dc981b3471f11ab82ee3af2be3dce1","TEST_MAPPING":"c7bad5ec6f7bf48bb856a231acfacaa7f39e438507a269c058f5250421ed683e","benches/bigint.rs":"11044fe6ee2c8c7a0f827d42187496689c78a5f168684bde57f7d7ce5ce8fd93","benches/factorial.rs":"6a0d78154597de3d3a469e661a2d0d29b0c095696d5387ddd6be4f0cc3da236b","benches/gcd.rs":"e66e9488678528c5386f43bcd1498da579a18b0d7a203b90e459f3fae80ba435","benches/rng/mod.rs":"9d49b4584c9bc6b3b1cbab51b08e00334b09b26b7a758491e8208ec7f21fe0b8","benches/roots.rs":"5d83ab3461ba7ed36b1a6c1250d73519f72bfd7309f2046b5d5437c8d3e68ae5","benches/shootout-pidigits.rs":"1393ffbdc2555e34818ba81a5bf9eb28c8568b71196bad4e811b504d145bea2e","cargo_embargo.json":"be3a30d8e5d9126130b453bb35f83bfeaf4275f85e8209865cca2a6609eb4b2d","patches/disable_tests.patch":"8e6908c72054213761d720a7fea46347042fb14b322f1d02216d7c4739a516da","src/bigint.rs":"7f392985a5791a611c4facd493788ace49e57900ef3a31264725a8ce915c2bef","src/bigint/addition.rs":"e6a2946eb8721b9c777593d72bb4057bfa5fb1ea11bf73878640352f02960dbe","src/bigint/arbitrary.rs":"51f6937e59f386953ebf2733823be04c0f1861590bfc070888c36e7c3a610d1b","src/bigint/bits.rs":"b76d7abeaf07ebd1255b297590fa9d6b05c963cbc46f9610df728f99dc0a17ae","src/bigint/convert.rs":"98a6ddb01599bee9c1cb09264650a0ae3955cd1e8b22a6ac325315937e20f2a9","src/bigint/division.rs":"b75b3244b354a9cd6df59bed20b0128dcec401ce243de58f8b2bd772fa0f5ac3","src/bigint/multiplication.rs":"10605872fae3ee6e6af13ab7588151b390a11bd0c1b8234947334ffb2d4a0bc8","src/bigint/power.rs":"119ec879b2b65de573c9eb23955fc75655c8b0b05791a808d76c66de9fe78d14","src/bigint/serde.rs":"e49e01da48214a17de01f18b9a37c4787b91858513763af4d086b17d1f2bb411","src/bigint/shift.rs":"7090e677289786c25e0a6ec13e89b9775dda240e624e4add5ac34f8ff08d70e1","src/bigint/subtraction.rs":"687febaa35535cb0f8b81115020d17c09295b474030778500f584957ba0ffc74","src/bigrand.rs":"2a2c352e57d2dd95b40e116675ce2214e9a8f1c100667dac91965942a0564e28","src/biguint.rs":"67707669c4d99453131d8641d02919a4991392bc33bd6fbd3818f006cb6e6a19","src/biguint/addition.rs":"3de84c1c94bc3ea8e427026f3ec266e651144f4cd87354c8d6f7c89abd4c231c","src/biguint/arbitrary.rs":"007e5185a2e909fb7881b7cb4718fa3de31e9f841f192887d30a4075b8d3fac2","src/biguint/bits.rs":"07fef255da30a2bb10d5c34dacb3c2b2a84c872dcefcaaa5c41bcd1519fe97e8","src/biguint/convert.rs":"540bdd333542f54bb52466dc849d7a73503512cf44bb0a10b131dc820ff2b97f","src/biguint/division.rs":"a00701225783744cbe8fb11536f4851c09a39d9c82f41f67996519de76089f17","src/biguint/iter.rs":"e5c10ed11fc11fa2bda75bb1b884b89475626e983545d99132068a2d761792b1","src/biguint/monty.rs":"19ca69a11d0c580510dd65299374cd501feecb712da4b7bdc17d7ac0bba37071","src/biguint/multiplication.rs":"eecc40e564117990e853fe4b088d2f0dd85a1611e57758210d7ab6613cf28ab3","src/biguint/power.rs":"b7bf53f58cabd6ef581c4c5caa4e38407939e438dafcda780aab0e8205151443","src/biguint/serde.rs":"47b8f666ddeda66ea7ca7ff745edbd3543a57221e14ad27e46275a02f52deff7","src/biguint/shift.rs":"a6cfcc0476fd2cff0268614718f7e135b71899f208138293fe7ca99db0199638","src/biguint/subtraction.rs":"75cbdb772971330a27aee32fecf0dcb23e93886b9c4b5cffd5906ac25010a1f3","src/lib.rs":"8d98cc2ac5c23d00d35bcc7971a5ce9bcae0c11070bf462dc8c143de8dd6796a","src/macros.rs":"e5f1c3c3b455c6178e7448cb499d5304bb8e3683f6796aee02496ff7599907a5","tests/bigint.rs":"6b4e118d09fe901ab9ffdcee9e5d40aa0b26e27954db592408bb996b7578633b","tests/bigint_bitwise.rs":"dbc1ccd93320f427ffb84af856b1ba923e911038d5e34d9482dcd9d5b95d870b","tests/bigint_scalar.rs":"0ac683c29a4b8f883eca986b3234530fc493fb3e6f0cc16858093f49846492cc","tests/biguint.rs":"0ee251ef674fa9803281e90cc5b8ab190408418d93a68c4a73d31baf9fa5304f","tests/biguint_scalar.rs":"c8d90abf23d75f603c7b2e8f475959319a2374bd285fc7fc9a9d31e7f7ee4bab","tests/consts/mod.rs":"31035c244b43702f9751c8e48f45eb2a007a40585c3c50083e4054e78c732206","tests/fuzzed.rs":"b01ddbada1bdee35563ccb953deee3476f1a8cd6daa7de0e1a708bd0845c56fa","tests/macros/mod.rs":"20098c9bd76d007dec91fddf9a99196f9e3455485ca1f7165528597b18b39975","tests/modpow.rs":"fa80f4701af352e301161a465a9f4d374eed799a7de7f239bb4ec0857b1b9377","tests/roots.rs":"c632b43665d471a12c0cbfb4eecb9fa010fe3bf4c9df182aad10d206dd324ecb"}}
\ No newline at end of file
diff --git a/crates/num-bigint/.cargo-checksum.json b/crates/num-bigint/.cargo-checksum.json
index 28c7008..5de47f9 100644
--- a/crates/num-bigint/.cargo-checksum.json
+++ b/crates/num-bigint/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"2cfbab335a56779607ae2bd573b3144768321d4aaeb3b48a8fc5e8473bfe61ff","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"1ee66f7d39c572dabe553b5ad698111d58a9ab1f0019ed8b6371af2b67ad0aae","RELEASES.md":"ed8cff3aa3583da2ab9cb3185e93b5644aafbef03e08647b51f57ddc99d27acd","benches/bigint.rs":"7efd4741f53c786bae63d1196492b5657fd0d928b37a59a08629f6efdc35c845","benches/factorial.rs":"ed1d276a780e7e5fe79121b941c22a00c2854dbf92fd8a5372619853ba0c13b7","benches/gcd.rs":"3cc1a3356f680a6fa625f0ece0c8dd778f4091a53a31177f2870ef9a6c858c7d","benches/rng/mod.rs":"38144fc8283955db4be72a1533328fded98986d6f9d0bc7da0b306f7d4b5ca43","benches/roots.rs":"b31846852a7215c26df228940f2e469aff32fa8805eccc2b5ee5e7280ef0eeb4","benches/shootout-pidigits.rs":"c2a48133f5b679928f7e3f4e764c78aaa8c5b811f58b86fe57fae8c63cb07136","build.rs":"9a4c2d95c240f46a54b565a6fc9d5f3b2fef2634bc829175ea7740652207bd34","src/bigint.rs":"f7aa256232ac8b4208ae6c3e026a4313afd6d0a79377d0d989ef6db8b31bb942","src/bigint/addition.rs":"57d88a77b36494441b6b6e969269c4dee237cead018512f2bcd074f28806ef00","src/bigint/arbitrary.rs":"6679833dffb38fa81f251bf9dd35b0d5b4cecb2a368e82aac92b00cef4dfc21b","src/bigint/bits.rs":"0e09e62317fa74a85c7f45c2bf441a7a40bb02b4a2ef52620871573b73ac6808","src/bigint/convert.rs":"8c3763391fc2df1e79268fcc03cae4454ace87455d1994288e55473a8993528a","src/bigint/division.rs":"1a268e7739a7f5be56ff084aa092d1c570e593690ca980ff77254faf51620202","src/bigint/multiplication.rs":"c262f732b86cc36804895df21e5ea5221944cadc1fca7903ff75a26102ba70f1","src/bigint/power.rs":"0ed57f2c9b5bc3debd6811dbb8705d07b0131f594be268321da35e70c08dbea9","src/bigint/serde.rs":"8240ed79ac11ec0ec2dfc85d4657693d5b03379bdd60a42dccee4764b000e5b6","src/bigint/shift.rs":"3f28bca2d52621133cdf567290645d30a746e663d8dacea29c172b5ed7ff3538","src/bigint/subtraction.rs":"6c3cce163293e2b95df38df4797329e2f9b43369e845697744dfbe43a67399ce","src/bigrand.rs":"2af7fde779b326006d1fd00f85f81ab6d3b2e30ec370fe4eaa41eeceeec6b7a6","src/biguint.rs":"a8c6b4d5fd6c3e07e888727385fc7148ad5e952eb940ee48c3086c864ad80941","src/biguint/addition.rs":"4f79fc1fc3157303309dcfa2166ae10ed33edbb11bf5602c43038485b5562746","src/biguint/arbitrary.rs":"895fe5a9bbcf40824d1a342e089fb2aec78cb9bad0dd489cfef489a3323f6c3b","src/biguint/bits.rs":"509036c6c6cb083d4568f92ac852cf5f12510f98c4547d47a80e3db4282a159e","src/biguint/convert.rs":"10d25021dab0bf88a58b96ca786689f72530a946d0a080e29ba40079dc1d844d","src/biguint/division.rs":"bedce3a1a7ffa271e0b74e98929f98a87343bb6a54a9de3c2effdd27041c78a5","src/biguint/iter.rs":"c21e30f573bdf1936e72dd56a89ee662329d25e8b55e37e88e9b1aea2da48abd","src/biguint/monty.rs":"08554859ed7aeaec89b311458c443d3eab375b02da7a9b20df8b42ed18b8c8c1","src/biguint/multiplication.rs":"501da2844830b1699a05e58fae3deb21b158fc0871e2cd4fc50049202faeb2c8","src/biguint/power.rs":"e428489a4aa6db640fde03eca61bce8eb56502303c48794248a6d14154ecf2ae","src/biguint/serde.rs":"23e028b21a9a836f443c8bf996c2011d2a1df87bc53fb96b83ae1545efccba81","src/biguint/shift.rs":"c67a37cf2605e06bb706d493659303992d25d623fa62dfa085e01d53acbaa08a","src/biguint/subtraction.rs":"3b18ab809da5efed5204217b46ccf4d653b3e8abb61820c1e0eebe13e400b3a9","src/lib.rs":"35f99739d938f36701d7951e40aaaf4a5b96efdce6c6d489af775913ed32eed4","src/macros.rs":"a007d89d68848f99119c524ff1d01c4acaf052f46555cb395fff066879153b77","tests/bigint.rs":"f2bfe36ebee59e083a1133838240167e902f1f2580bce68057a86de9dab5fe56","tests/bigint_bitwise.rs":"e6a2f76fa1eb919e7c513d7e30a8a2a963841a295a71103462fb8ab9792419b5","tests/bigint_scalar.rs":"a87e801e370686985d44e1f020c69fceca72b9f048e0f7301d2b8d38469e5636","tests/biguint.rs":"09dc0cc5e7c81974e2097666de3732917b21df93362b17fcab1e180d44296d0a","tests/biguint_scalar.rs":"b09cda9d4fe6ec519e93282653f69b57d70db73b9cb59c0ea5cd0861ca2de266","tests/consts/mod.rs":"e20bc49a7cc95077242cbe4016b37745ea986c779d2385cb367fbfe44f15ff94","tests/fuzzed.rs":"f60a84c446ea2f45d87eb4ee64682ea63fdef05bc74f482739d4e968960e8f4e","tests/macros/mod.rs":"1a8f9f015e5caaac60ce9ccff01a75ae489801c3ede6e7b9b3c5079b6efefc9c","tests/modpow.rs":"f4c81111308d63876ed02229cbab50bec9bac082bfa71f481120e46b5d7e5560","tests/roots.rs":"a3bc2de170a0f6297cc8d8830d608db537ca102ccf204fd4fb8e2d92675622d8"},"package":"608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"}
\ No newline at end of file
+{"files":{"Cargo.toml":"a6042603541ecb91ec1f5c158679c0b47090462434e2a53e65f78395994f223f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"abf3dae8d5ccf7693234cb0101a205f844959366bc906a1a5b53973a73a3054c","RELEASES.md":"80b60df30e92e913d710e558f1a2de0baa8a3e49b242c66bc1a1a18de5ff4618","benches/bigint.rs":"0943fcfdc290db028bc37d63c118284c79681a52a62311cc036da80d3b9ac390","benches/factorial.rs":"ed1d276a780e7e5fe79121b941c22a00c2854dbf92fd8a5372619853ba0c13b7","benches/gcd.rs":"3cc1a3356f680a6fa625f0ece0c8dd778f4091a53a31177f2870ef9a6c858c7d","benches/rng/mod.rs":"38144fc8283955db4be72a1533328fded98986d6f9d0bc7da0b306f7d4b5ca43","benches/roots.rs":"b31846852a7215c26df228940f2e469aff32fa8805eccc2b5ee5e7280ef0eeb4","benches/shootout-pidigits.rs":"c2a48133f5b679928f7e3f4e764c78aaa8c5b811f58b86fe57fae8c63cb07136","src/bigint.rs":"e5735688e1345396c21a7f5e8328bcdf77f5a0c7068028caa74063c68a3e4ef7","src/bigint/addition.rs":"cdc28e59a5dc6fe7dbad2d8d9514b49fe1bea061e82ebefe89c518f448dc8ba9","src/bigint/arbitrary.rs":"9fb1f7f8bbc7a792d19d4c30ed2671ed2d73847d57e5f65380ae31e5db0146c6","src/bigint/bits.rs":"3d696d43bcd336690ce1412af75a38d490fd2af745c2efc7cb2fc51bace07de8","src/bigint/convert.rs":"1463ecd5e39c938ae5b8e2b685d69a85c2e795c1fa96531b2192abb11ccdf638","src/bigint/division.rs":"dea89b4efb06b77bbbf07036c454d52f4d026658bcbacad1d15302c93d61b3dd","src/bigint/multiplication.rs":"c262f732b86cc36804895df21e5ea5221944cadc1fca7903ff75a26102ba70f1","src/bigint/power.rs":"a3823654f022014c5ce2dc2fd97afa6696589d32a54cc4ec65e6c113e8613672","src/bigint/serde.rs":"a7275341ec518f27f4d955e95b6e302b786944e9462bf0c17c7087d073212943","src/bigint/shift.rs":"3f28bca2d52621133cdf567290645d30a746e663d8dacea29c172b5ed7ff3538","src/bigint/subtraction.rs":"c2aa7e0fbfc7747099d0eec77efdedaa8d79cff88b1587d640d6b1c3c7dc0e4a","src/bigrand.rs":"115ab17de1c909b22bf20374b31cf5b1e9b28cd4914bf8cd6852375c35d6a027","src/biguint.rs":"bf69f80a59b528e0743390663f9a6a8f6d3edc8643a65c8d0f7f01a8f434f478","src/biguint/addition.rs":"1429575654102731024259da7d5586a4db44387ae86c1b170083c5565b37b410","src/biguint/arbitrary.rs":"3492432d1b5e9f851ce120549bf82e89ba2b6c51f584f8d9ac6782fe10fb58dc","src/biguint/bits.rs":"509036c6c6cb083d4568f92ac852cf5f12510f98c4547d47a80e3db4282a159e","src/biguint/convert.rs":"85cdb3e60856d281f45dff602960666fe44af0ab54ba2ce26cfa4018a9309c60","src/biguint/division.rs":"13ed075f244510eb0354b899b8b9d99216d202bd4eae445ee18a444e06dd708b","src/biguint/iter.rs":"6f843751c93520817a182a2a335001ff221478499eb765d09a0a6f19ba0f7f97","src/biguint/monty.rs":"68851ce7542acdc180431905668684dcadf7d5b422073c6d8acc418ceb86f5d0","src/biguint/multiplication.rs":"e3908aecda0fbbf8c97c96d1d9b24ff9bbe4e09df3b15e460e00c73ecf1c0e4e","src/biguint/power.rs":"4740974546f85da802f7fee4a7c595a8d1b46611914130237cd26c20c268db84","src/biguint/serde.rs":"f4d4e6f2d56c9bab95d8b178682ebeba88965a36bcff42ccade3334712c9977a","src/biguint/shift.rs":"4962ff0d8d0371e7ab39b00a9fc29907e5cd37b9819f61d5ef82ff13b0f55071","src/biguint/subtraction.rs":"b124b9a12bdb67cadb98050feba8cd04fcd22e985c2f994b7c473d38b94f4821","src/lib.rs":"70493e8293b769076d6a16df962a4913b74aa1925fe8bd448865da097ee2dbe1","src/macros.rs":"ab2a5b6e39538f62fdf39f6f3695e9f2f532715f40109795a787aadbb2b8ea9a","tests/bigint.rs":"929422608f842cfb0bdd5117f5af74c954704d3c84df7da46b0eea1fdedd5ba8","tests/bigint_bitwise.rs":"a69d59c3eb07867f9da66ad9a8ae101a15366f88fb1f72f2015a9fe4de3518db","tests/bigint_scalar.rs":"a87e801e370686985d44e1f020c69fceca72b9f048e0f7301d2b8d38469e5636","tests/biguint.rs":"61fb3cb51ab2102330225adbfbbef9934defc23be9ab04e4c09129cd67b6f380","tests/biguint_scalar.rs":"b09cda9d4fe6ec519e93282653f69b57d70db73b9cb59c0ea5cd0861ca2de266","tests/consts/mod.rs":"f077d1aea22895a29df4e1391b40cc983cde9389f97e5b30923765f61dc3d017","tests/fuzzed.rs":"f60a84c446ea2f45d87eb4ee64682ea63fdef05bc74f482739d4e968960e8f4e","tests/macros/mod.rs":"1a8f9f015e5caaac60ce9ccff01a75ae489801c3ede6e7b9b3c5079b6efefc9c","tests/modpow.rs":"f4c81111308d63876ed02229cbab50bec9bac082bfa71f481120e46b5d7e5560","tests/roots.rs":"1c1576309eef9a6eb7ac8db4358fcf1d5b55403deaf27fd40ba2aa61b9567c87"},"package":"a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"}
\ No newline at end of file
diff --git a/crates/num-bigint/Android.bp b/crates/num-bigint/Android.bp
index 2054852..b389417 100644
--- a/crates/num-bigint/Android.bp
+++ b/crates/num-bigint/Android.bp
@@ -13,30 +13,18 @@
     license_text: ["LICENSE"],
 }
 
-genrule {
-    name: "copy_num-bigint_build_out",
-    srcs: ["out/*"],
-    cmd: "cp $(in) $(genDir)",
-    out: ["radix_bases.rs"],
-}
-
 rust_library {
     name: "libnum_bigint",
     host_supported: true,
     crate_name: "num_bigint",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "src/lib.rs",
-    srcs: [":copy_num-bigint_build_out"],
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_integer",
         "libnum_traits",
@@ -54,23 +42,18 @@
     host_supported: true,
     crate_name: "num_bigint",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "src/lib.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_integer",
         "libnum_traits",
@@ -82,23 +65,18 @@
     host_supported: true,
     crate_name: "bigint",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "tests/bigint.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_bigint",
         "libnum_integer",
@@ -111,23 +89,18 @@
     host_supported: true,
     crate_name: "bigint_bitwise",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "tests/bigint_bitwise.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_bigint",
         "libnum_integer",
@@ -140,23 +113,18 @@
     host_supported: true,
     crate_name: "bigint_scalar",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "tests/bigint_scalar.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_bigint",
         "libnum_integer",
@@ -169,23 +137,18 @@
     host_supported: true,
     crate_name: "biguint",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "tests/biguint.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_bigint",
         "libnum_integer",
@@ -198,23 +161,18 @@
     host_supported: true,
     crate_name: "biguint_scalar",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "tests/biguint_scalar.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_bigint",
         "libnum_integer",
@@ -227,23 +185,18 @@
     host_supported: true,
     crate_name: "fuzzed",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "tests/fuzzed.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_bigint",
         "libnum_integer",
@@ -256,23 +209,18 @@
     host_supported: true,
     crate_name: "modpow",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "tests/modpow.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_bigint",
         "libnum_integer",
@@ -285,23 +233,18 @@
     host_supported: true,
     crate_name: "roots",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.4.4",
+    cargo_pkg_version: "0.4.6",
     crate_root: "tests/roots.rs",
-    srcs: [":copy_num-bigint_build_out"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2018",
+    edition: "2021",
     features: [
         "default",
         "std",
     ],
-    cfgs: [
-        "has_try_from",
-        "u64_digit",
-    ],
     rustlibs: [
         "libnum_bigint",
         "libnum_integer",
diff --git a/crates/num-bigint/Cargo.toml b/crates/num-bigint/Cargo.toml
index 7e93523..0218bbd 100644
--- a/crates/num-bigint/Cargo.toml
+++ b/crates/num-bigint/Cargo.toml
@@ -10,13 +10,12 @@
 # See Cargo.toml.orig for the original contents.
 
 [package]
-edition = "2018"
+edition = "2021"
+rust-version = "1.60"
 name = "num-bigint"
-version = "0.4.4"
+version = "0.4.6"
 authors = ["The Rust Project Developers"]
-build = "build.rs"
 exclude = [
-    "/bors.toml",
     "/ci/*",
     "/.github/*",
 ]
@@ -45,6 +44,10 @@
     "quickcheck",
     "arbitrary",
 ]
+rustdoc-args = [
+    "--cfg",
+    "docsrs",
+]
 
 [[bench]]
 name = "bigint"
@@ -68,12 +71,12 @@
 default-features = false
 
 [dependencies.num-integer]
-version = "0.1.42"
+version = "0.1.46"
 features = ["i128"]
 default-features = false
 
 [dependencies.num-traits]
-version = "0.2.16"
+version = "0.2.18"
 features = ["i128"]
 default-features = false
 
@@ -92,14 +95,12 @@
 optional = true
 default-features = false
 
-[build-dependencies.autocfg]
-# autocfg 1.4 uses different, non-deterministic filenames. We don't
-# actually depend on these files, but they are present in the out/
-# directory, and it's simpler to keep them around.
-version = "=1.1.0"
-
 [features]
+arbitrary = ["dep:arbitrary"]
 default = ["std"]
+quickcheck = ["dep:quickcheck"]
+rand = ["dep:rand"]
+serde = ["dep:serde"]
 std = [
     "num-integer/std",
     "num-traits/std",
diff --git a/crates/num-bigint/METADATA b/crates/num-bigint/METADATA
index 0b227f8..6b3715c 100644
--- a/crates/num-bigint/METADATA
+++ b/crates/num-bigint/METADATA
@@ -1,20 +1,17 @@
-# This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update external/rust/crates/num-bigint
-# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
-
 name: "num-bigint"
 description: "Big integer implementation for Rust"
 third_party {
+  version: "0.4.6"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2024
-    month: 2
-    day: 2
+    year: 2025
+    month: 1
+    day: 22
   }
   homepage: "https://crates.io/crates/num-bigint"
   identifier {
     type: "Archive"
-    value: "https://static.crates.io/crates/num-bigint/num-bigint-0.4.4.crate"
-    version: "0.4.4"
+    value: "https://static.crates.io/crates/num-bigint/num-bigint-0.4.6.crate"
+    version: "0.4.6"
   }
 }
diff --git a/crates/num-bigint/README.md b/crates/num-bigint/README.md
index 21f7749..cce185b 100644
--- a/crates/num-bigint/README.md
+++ b/crates/num-bigint/README.md
@@ -2,7 +2,7 @@
 
 [![crate](https://img.shields.io/crates/v/num-bigint.svg)](https://crates.io/crates/num-bigint)
 [![documentation](https://docs.rs/num-bigint/badge.svg)](https://docs.rs/num-bigint)
-[![minimum rustc 1.31](https://img.shields.io/badge/rustc-1.31+-red.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
+[![minimum rustc 1.60](https://img.shields.io/badge/rustc-1.60+-red.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html)
 [![build status](https://github.com/rust-num/num-bigint/workflows/master/badge.svg)](https://github.com/rust-num/num-bigint/actions)
 
 Big integer types for Rust, `BigInt` and `BigUint`.
@@ -42,7 +42,7 @@
 
 ## Compatibility
 
-The `num-bigint` crate is tested for rustc 1.31 and greater.
+The `num-bigint` crate is tested for rustc 1.60 and greater.
 
 ## Alternatives
 
@@ -52,10 +52,10 @@
 
 | Crate             | License        | Min rustc | Implementation | Features |
 | :---------------  | :------------- | :-------- | :------------- | :------- |
-| **`num-bigint`**  | MIT/Apache-2.0 | 1.31      | pure rust | dynamic width, number theoretical functions |
+| **`num-bigint`**  | MIT/Apache-2.0 | 1.60      | pure rust | dynamic width, number theoretical functions |
 | [`awint`]         | MIT/Apache-2.0 | 1.66      | pure rust | fixed width, heap or stack, concatenation macros |
-| [`bnum`]          | MIT/Apache-2.0 | 1.61      | pure rust | fixed width, parity with Rust primitives including floats |
-| [`crypto-bigint`] | MIT/Apache-2.0 | 1.57      | pure rust | fixed width, stack only |
+| [`bnum`]          | MIT/Apache-2.0 | 1.65      | pure rust | fixed width, parity with Rust primitives including floats |
+| [`crypto-bigint`] | MIT/Apache-2.0 | 1.73      | pure rust | fixed width, stack only |
 | [`ibig`]          | MIT/Apache-2.0 | 1.49      | pure rust | dynamic width, number theoretical functions |
 | [`rug`]           | LGPL-3.0+      | 1.65      | bundles [GMP] via [`gmp-mpfr-sys`] | all the features of GMP, MPFR, and MPC |
 
diff --git a/crates/num-bigint/RELEASES.md b/crates/num-bigint/RELEASES.md
index ad5dd49..bc4c353 100644
--- a/crates/num-bigint/RELEASES.md
+++ b/crates/num-bigint/RELEASES.md
@@ -1,3 +1,27 @@
+# Release 0.4.6 (2024-06-27)
+
+- [Fixed compilation on `x86_64-unknown-linux-gnux32`.][312]
+
+**Contributors**: @cuviper, @ralphtandetzky, @yhx-12243
+
+[312]: https://github.com/rust-num/num-bigint/pull/312
+
+# Release 0.4.5 (2024-05-06)
+
+- [Upgrade to 2021 edition, **MSRV 1.60**][292]
+- [Add `const ZERO` and implement `num_traits::ConstZero`][298]
+- [Add `modinv` methods for the modular inverse][288]
+- [Optimize multiplication with imbalanced operands][295]
+- [Optimize scalar division on x86 and x86-64][236]
+
+**Contributors**: @cuviper, @joelonsql, @waywardmonkeys
+
+[236]: https://github.com/rust-num/num-bigint/pull/236
+[288]: https://github.com/rust-num/num-bigint/pull/288
+[292]: https://github.com/rust-num/num-bigint/pull/292
+[295]: https://github.com/rust-num/num-bigint/pull/295
+[298]: https://github.com/rust-num/num-bigint/pull/298
+
 # Release 0.4.4 (2023-08-22)
 
 - [Implemented `From<bool>` for `BigInt` and `BigUint`.][239]
diff --git a/crates/num-bigint/benches/bigint.rs b/crates/num-bigint/benches/bigint.rs
index 80ec191..2279527 100644
--- a/crates/num-bigint/benches/bigint.rs
+++ b/crates/num-bigint/benches/bigint.rs
@@ -88,6 +88,16 @@
 }
 
 #[bench]
+fn multiply_4(b: &mut Bencher) {
+    multiply_bench(b, 1 << 12, 1 << 13);
+}
+
+#[bench]
+fn multiply_5(b: &mut Bencher) {
+    multiply_bench(b, 1 << 12, 1 << 14);
+}
+
+#[bench]
 fn divide_0(b: &mut Bencher) {
     divide_bench(b, 1 << 8, 1 << 6);
 }
diff --git a/crates/num-bigint/build.rs b/crates/num-bigint/build.rs
deleted file mode 100644
index 82b7a43..0000000
--- a/crates/num-bigint/build.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-use std::env;
-use std::error::Error;
-use std::fs::File;
-use std::io::Write;
-use std::path::Path;
-
-fn main() {
-    let ptr_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH");
-    let u64_digit = ptr_width
-        .as_ref()
-        .map(|x| x == "64" || x == "128")
-        .unwrap_or(false);
-
-    if u64_digit {
-        autocfg::emit("u64_digit");
-    }
-
-    let ac = autocfg::new();
-    let std = if ac.probe_sysroot_crate("std") {
-        "std"
-    } else {
-        "core"
-    };
-
-    if ac.probe_path(&format!("{}::convert::TryFrom", std)) {
-        autocfg::emit("has_try_from");
-    }
-
-    if let Ok(arch) = env::var("CARGO_CFG_TARGET_ARCH") {
-        if arch == "x86_64" || arch == "x86" {
-            let digit = if u64_digit { "u64" } else { "u32" };
-
-            let addcarry = format!("{}::arch::{}::_addcarry_{}", std, arch, digit);
-            if ac.probe_path(&addcarry) {
-                autocfg::emit("use_addcarry");
-            }
-        }
-    }
-
-    // autocfg generates probe files in $OUT_DIR with nondeterministic contents.
-    // (In autocfg 1.4, the filenames are nondeterministic as well.)
-    let out_dir_env = env::var("OUT_DIR").unwrap();
-    let out_dir = Path::new(&out_dir_env);
-    std::fs::remove_file(out_dir.join("probe0.ll")).unwrap();
-    std::fs::remove_file(out_dir.join("probe1.ll")).unwrap();
-    std::fs::remove_file(out_dir.join("probe2.ll")).unwrap();
-    std::fs::remove_file(out_dir.join("probe3.ll")).unwrap();
-
-    autocfg::rerun_path("build.rs");
-
-    write_radix_bases().unwrap();
-}
-
-/// Write tables of the greatest power of each radix for the given bit size.  These are returned
-/// from `biguint::get_radix_base` to batch the multiplication/division of radix conversions on
-/// full `BigUint` values, operating on primitive integers as much as possible.
-///
-/// e.g. BASES_16[3] = (59049, 10) // 3¹⁰ fits in u16, but 3¹¹ is too big
-///      BASES_32[3] = (3486784401, 20)
-///      BASES_64[3] = (12157665459056928801, 40)
-///
-/// Powers of two are not included, just zeroed, as they're implemented with shifts.
-fn write_radix_bases() -> Result<(), Box<dyn Error>> {
-    let out_dir = env::var("OUT_DIR")?;
-    let dest_path = Path::new(&out_dir).join("radix_bases.rs");
-    let mut f = File::create(&dest_path)?;
-
-    for &bits in &[16, 32, 64] {
-        let max = if bits < 64 {
-            (1 << bits) - 1
-        } else {
-            std::u64::MAX
-        };
-
-        writeln!(f, "#[deny(overflowing_literals)]")?;
-        writeln!(
-            f,
-            "pub(crate) static BASES_{bits}: [(u{bits}, usize); 257] = [",
-            bits = bits
-        )?;
-        for radix in 0u64..257 {
-            let (base, power) = if radix == 0 || radix.is_power_of_two() {
-                (0, 0)
-            } else {
-                let mut power = 1;
-                let mut base = radix;
-
-                while let Some(b) = base.checked_mul(radix) {
-                    if b > max {
-                        break;
-                    }
-                    base = b;
-                    power += 1;
-                }
-                (base, power)
-            };
-            writeln!(f, "    ({}, {}), // {}", base, power, radix)?;
-        }
-        writeln!(f, "];")?;
-    }
-
-    Ok(())
-}
diff --git a/crates/num-bigint/out/radix_bases.rs b/crates/num-bigint/out/radix_bases.rs
deleted file mode 100644
index 8702741..0000000
--- a/crates/num-bigint/out/radix_bases.rs
+++ /dev/null
@@ -1,780 +0,0 @@
-#[deny(overflowing_literals)]
-pub(crate) static BASES_16: [(u16, usize); 257] = [
-    (0, 0), // 0
-    (0, 0), // 1
-    (0, 0), // 2
-    (59049, 10), // 3
-    (0, 0), // 4
-    (15625, 6), // 5
-    (46656, 6), // 6
-    (16807, 5), // 7
-    (0, 0), // 8
-    (59049, 5), // 9
-    (10000, 4), // 10
-    (14641, 4), // 11
-    (20736, 4), // 12
-    (28561, 4), // 13
-    (38416, 4), // 14
-    (50625, 4), // 15
-    (0, 0), // 16
-    (4913, 3), // 17
-    (5832, 3), // 18
-    (6859, 3), // 19
-    (8000, 3), // 20
-    (9261, 3), // 21
-    (10648, 3), // 22
-    (12167, 3), // 23
-    (13824, 3), // 24
-    (15625, 3), // 25
-    (17576, 3), // 26
-    (19683, 3), // 27
-    (21952, 3), // 28
-    (24389, 3), // 29
-    (27000, 3), // 30
-    (29791, 3), // 31
-    (0, 0), // 32
-    (35937, 3), // 33
-    (39304, 3), // 34
-    (42875, 3), // 35
-    (46656, 3), // 36
-    (50653, 3), // 37
-    (54872, 3), // 38
-    (59319, 3), // 39
-    (64000, 3), // 40
-    (1681, 2), // 41
-    (1764, 2), // 42
-    (1849, 2), // 43
-    (1936, 2), // 44
-    (2025, 2), // 45
-    (2116, 2), // 46
-    (2209, 2), // 47
-    (2304, 2), // 48
-    (2401, 2), // 49
-    (2500, 2), // 50
-    (2601, 2), // 51
-    (2704, 2), // 52
-    (2809, 2), // 53
-    (2916, 2), // 54
-    (3025, 2), // 55
-    (3136, 2), // 56
-    (3249, 2), // 57
-    (3364, 2), // 58
-    (3481, 2), // 59
-    (3600, 2), // 60
-    (3721, 2), // 61
-    (3844, 2), // 62
-    (3969, 2), // 63
-    (0, 0), // 64
-    (4225, 2), // 65
-    (4356, 2), // 66
-    (4489, 2), // 67
-    (4624, 2), // 68
-    (4761, 2), // 69
-    (4900, 2), // 70
-    (5041, 2), // 71
-    (5184, 2), // 72
-    (5329, 2), // 73
-    (5476, 2), // 74
-    (5625, 2), // 75
-    (5776, 2), // 76
-    (5929, 2), // 77
-    (6084, 2), // 78
-    (6241, 2), // 79
-    (6400, 2), // 80
-    (6561, 2), // 81
-    (6724, 2), // 82
-    (6889, 2), // 83
-    (7056, 2), // 84
-    (7225, 2), // 85
-    (7396, 2), // 86
-    (7569, 2), // 87
-    (7744, 2), // 88
-    (7921, 2), // 89
-    (8100, 2), // 90
-    (8281, 2), // 91
-    (8464, 2), // 92
-    (8649, 2), // 93
-    (8836, 2), // 94
-    (9025, 2), // 95
-    (9216, 2), // 96
-    (9409, 2), // 97
-    (9604, 2), // 98
-    (9801, 2), // 99
-    (10000, 2), // 100
-    (10201, 2), // 101
-    (10404, 2), // 102
-    (10609, 2), // 103
-    (10816, 2), // 104
-    (11025, 2), // 105
-    (11236, 2), // 106
-    (11449, 2), // 107
-    (11664, 2), // 108
-    (11881, 2), // 109
-    (12100, 2), // 110
-    (12321, 2), // 111
-    (12544, 2), // 112
-    (12769, 2), // 113
-    (12996, 2), // 114
-    (13225, 2), // 115
-    (13456, 2), // 116
-    (13689, 2), // 117
-    (13924, 2), // 118
-    (14161, 2), // 119
-    (14400, 2), // 120
-    (14641, 2), // 121
-    (14884, 2), // 122
-    (15129, 2), // 123
-    (15376, 2), // 124
-    (15625, 2), // 125
-    (15876, 2), // 126
-    (16129, 2), // 127
-    (0, 0), // 128
-    (16641, 2), // 129
-    (16900, 2), // 130
-    (17161, 2), // 131
-    (17424, 2), // 132
-    (17689, 2), // 133
-    (17956, 2), // 134
-    (18225, 2), // 135
-    (18496, 2), // 136
-    (18769, 2), // 137
-    (19044, 2), // 138
-    (19321, 2), // 139
-    (19600, 2), // 140
-    (19881, 2), // 141
-    (20164, 2), // 142
-    (20449, 2), // 143
-    (20736, 2), // 144
-    (21025, 2), // 145
-    (21316, 2), // 146
-    (21609, 2), // 147
-    (21904, 2), // 148
-    (22201, 2), // 149
-    (22500, 2), // 150
-    (22801, 2), // 151
-    (23104, 2), // 152
-    (23409, 2), // 153
-    (23716, 2), // 154
-    (24025, 2), // 155
-    (24336, 2), // 156
-    (24649, 2), // 157
-    (24964, 2), // 158
-    (25281, 2), // 159
-    (25600, 2), // 160
-    (25921, 2), // 161
-    (26244, 2), // 162
-    (26569, 2), // 163
-    (26896, 2), // 164
-    (27225, 2), // 165
-    (27556, 2), // 166
-    (27889, 2), // 167
-    (28224, 2), // 168
-    (28561, 2), // 169
-    (28900, 2), // 170
-    (29241, 2), // 171
-    (29584, 2), // 172
-    (29929, 2), // 173
-    (30276, 2), // 174
-    (30625, 2), // 175
-    (30976, 2), // 176
-    (31329, 2), // 177
-    (31684, 2), // 178
-    (32041, 2), // 179
-    (32400, 2), // 180
-    (32761, 2), // 181
-    (33124, 2), // 182
-    (33489, 2), // 183
-    (33856, 2), // 184
-    (34225, 2), // 185
-    (34596, 2), // 186
-    (34969, 2), // 187
-    (35344, 2), // 188
-    (35721, 2), // 189
-    (36100, 2), // 190
-    (36481, 2), // 191
-    (36864, 2), // 192
-    (37249, 2), // 193
-    (37636, 2), // 194
-    (38025, 2), // 195
-    (38416, 2), // 196
-    (38809, 2), // 197
-    (39204, 2), // 198
-    (39601, 2), // 199
-    (40000, 2), // 200
-    (40401, 2), // 201
-    (40804, 2), // 202
-    (41209, 2), // 203
-    (41616, 2), // 204
-    (42025, 2), // 205
-    (42436, 2), // 206
-    (42849, 2), // 207
-    (43264, 2), // 208
-    (43681, 2), // 209
-    (44100, 2), // 210
-    (44521, 2), // 211
-    (44944, 2), // 212
-    (45369, 2), // 213
-    (45796, 2), // 214
-    (46225, 2), // 215
-    (46656, 2), // 216
-    (47089, 2), // 217
-    (47524, 2), // 218
-    (47961, 2), // 219
-    (48400, 2), // 220
-    (48841, 2), // 221
-    (49284, 2), // 222
-    (49729, 2), // 223
-    (50176, 2), // 224
-    (50625, 2), // 225
-    (51076, 2), // 226
-    (51529, 2), // 227
-    (51984, 2), // 228
-    (52441, 2), // 229
-    (52900, 2), // 230
-    (53361, 2), // 231
-    (53824, 2), // 232
-    (54289, 2), // 233
-    (54756, 2), // 234
-    (55225, 2), // 235
-    (55696, 2), // 236
-    (56169, 2), // 237
-    (56644, 2), // 238
-    (57121, 2), // 239
-    (57600, 2), // 240
-    (58081, 2), // 241
-    (58564, 2), // 242
-    (59049, 2), // 243
-    (59536, 2), // 244
-    (60025, 2), // 245
-    (60516, 2), // 246
-    (61009, 2), // 247
-    (61504, 2), // 248
-    (62001, 2), // 249
-    (62500, 2), // 250
-    (63001, 2), // 251
-    (63504, 2), // 252
-    (64009, 2), // 253
-    (64516, 2), // 254
-    (65025, 2), // 255
-    (0, 0), // 256
-];
-#[deny(overflowing_literals)]
-pub(crate) static BASES_32: [(u32, usize); 257] = [
-    (0, 0), // 0
-    (0, 0), // 1
-    (0, 0), // 2
-    (3486784401, 20), // 3
-    (0, 0), // 4
-    (1220703125, 13), // 5
-    (2176782336, 12), // 6
-    (1977326743, 11), // 7
-    (0, 0), // 8
-    (3486784401, 10), // 9
-    (1000000000, 9), // 10
-    (2357947691, 9), // 11
-    (429981696, 8), // 12
-    (815730721, 8), // 13
-    (1475789056, 8), // 14
-    (2562890625, 8), // 15
-    (0, 0), // 16
-    (410338673, 7), // 17
-    (612220032, 7), // 18
-    (893871739, 7), // 19
-    (1280000000, 7), // 20
-    (1801088541, 7), // 21
-    (2494357888, 7), // 22
-    (3404825447, 7), // 23
-    (191102976, 6), // 24
-    (244140625, 6), // 25
-    (308915776, 6), // 26
-    (387420489, 6), // 27
-    (481890304, 6), // 28
-    (594823321, 6), // 29
-    (729000000, 6), // 30
-    (887503681, 6), // 31
-    (0, 0), // 32
-    (1291467969, 6), // 33
-    (1544804416, 6), // 34
-    (1838265625, 6), // 35
-    (2176782336, 6), // 36
-    (2565726409, 6), // 37
-    (3010936384, 6), // 38
-    (3518743761, 6), // 39
-    (4096000000, 6), // 40
-    (115856201, 5), // 41
-    (130691232, 5), // 42
-    (147008443, 5), // 43
-    (164916224, 5), // 44
-    (184528125, 5), // 45
-    (205962976, 5), // 46
-    (229345007, 5), // 47
-    (254803968, 5), // 48
-    (282475249, 5), // 49
-    (312500000, 5), // 50
-    (345025251, 5), // 51
-    (380204032, 5), // 52
-    (418195493, 5), // 53
-    (459165024, 5), // 54
-    (503284375, 5), // 55
-    (550731776, 5), // 56
-    (601692057, 5), // 57
-    (656356768, 5), // 58
-    (714924299, 5), // 59
-    (777600000, 5), // 60
-    (844596301, 5), // 61
-    (916132832, 5), // 62
-    (992436543, 5), // 63
-    (0, 0), // 64
-    (1160290625, 5), // 65
-    (1252332576, 5), // 66
-    (1350125107, 5), // 67
-    (1453933568, 5), // 68
-    (1564031349, 5), // 69
-    (1680700000, 5), // 70
-    (1804229351, 5), // 71
-    (1934917632, 5), // 72
-    (2073071593, 5), // 73
-    (2219006624, 5), // 74
-    (2373046875, 5), // 75
-    (2535525376, 5), // 76
-    (2706784157, 5), // 77
-    (2887174368, 5), // 78
-    (3077056399, 5), // 79
-    (3276800000, 5), // 80
-    (3486784401, 5), // 81
-    (3707398432, 5), // 82
-    (3939040643, 5), // 83
-    (4182119424, 5), // 84
-    (52200625, 4), // 85
-    (54700816, 4), // 86
-    (57289761, 4), // 87
-    (59969536, 4), // 88
-    (62742241, 4), // 89
-    (65610000, 4), // 90
-    (68574961, 4), // 91
-    (71639296, 4), // 92
-    (74805201, 4), // 93
-    (78074896, 4), // 94
-    (81450625, 4), // 95
-    (84934656, 4), // 96
-    (88529281, 4), // 97
-    (92236816, 4), // 98
-    (96059601, 4), // 99
-    (100000000, 4), // 100
-    (104060401, 4), // 101
-    (108243216, 4), // 102
-    (112550881, 4), // 103
-    (116985856, 4), // 104
-    (121550625, 4), // 105
-    (126247696, 4), // 106
-    (131079601, 4), // 107
-    (136048896, 4), // 108
-    (141158161, 4), // 109
-    (146410000, 4), // 110
-    (151807041, 4), // 111
-    (157351936, 4), // 112
-    (163047361, 4), // 113
-    (168896016, 4), // 114
-    (174900625, 4), // 115
-    (181063936, 4), // 116
-    (187388721, 4), // 117
-    (193877776, 4), // 118
-    (200533921, 4), // 119
-    (207360000, 4), // 120
-    (214358881, 4), // 121
-    (221533456, 4), // 122
-    (228886641, 4), // 123
-    (236421376, 4), // 124
-    (244140625, 4), // 125
-    (252047376, 4), // 126
-    (260144641, 4), // 127
-    (0, 0), // 128
-    (276922881, 4), // 129
-    (285610000, 4), // 130
-    (294499921, 4), // 131
-    (303595776, 4), // 132
-    (312900721, 4), // 133
-    (322417936, 4), // 134
-    (332150625, 4), // 135
-    (342102016, 4), // 136
-    (352275361, 4), // 137
-    (362673936, 4), // 138
-    (373301041, 4), // 139
-    (384160000, 4), // 140
-    (395254161, 4), // 141
-    (406586896, 4), // 142
-    (418161601, 4), // 143
-    (429981696, 4), // 144
-    (442050625, 4), // 145
-    (454371856, 4), // 146
-    (466948881, 4), // 147
-    (479785216, 4), // 148
-    (492884401, 4), // 149
-    (506250000, 4), // 150
-    (519885601, 4), // 151
-    (533794816, 4), // 152
-    (547981281, 4), // 153
-    (562448656, 4), // 154
-    (577200625, 4), // 155
-    (592240896, 4), // 156
-    (607573201, 4), // 157
-    (623201296, 4), // 158
-    (639128961, 4), // 159
-    (655360000, 4), // 160
-    (671898241, 4), // 161
-    (688747536, 4), // 162
-    (705911761, 4), // 163
-    (723394816, 4), // 164
-    (741200625, 4), // 165
-    (759333136, 4), // 166
-    (777796321, 4), // 167
-    (796594176, 4), // 168
-    (815730721, 4), // 169
-    (835210000, 4), // 170
-    (855036081, 4), // 171
-    (875213056, 4), // 172
-    (895745041, 4), // 173
-    (916636176, 4), // 174
-    (937890625, 4), // 175
-    (959512576, 4), // 176
-    (981506241, 4), // 177
-    (1003875856, 4), // 178
-    (1026625681, 4), // 179
-    (1049760000, 4), // 180
-    (1073283121, 4), // 181
-    (1097199376, 4), // 182
-    (1121513121, 4), // 183
-    (1146228736, 4), // 184
-    (1171350625, 4), // 185
-    (1196883216, 4), // 186
-    (1222830961, 4), // 187
-    (1249198336, 4), // 188
-    (1275989841, 4), // 189
-    (1303210000, 4), // 190
-    (1330863361, 4), // 191
-    (1358954496, 4), // 192
-    (1387488001, 4), // 193
-    (1416468496, 4), // 194
-    (1445900625, 4), // 195
-    (1475789056, 4), // 196
-    (1506138481, 4), // 197
-    (1536953616, 4), // 198
-    (1568239201, 4), // 199
-    (1600000000, 4), // 200
-    (1632240801, 4), // 201
-    (1664966416, 4), // 202
-    (1698181681, 4), // 203
-    (1731891456, 4), // 204
-    (1766100625, 4), // 205
-    (1800814096, 4), // 206
-    (1836036801, 4), // 207
-    (1871773696, 4), // 208
-    (1908029761, 4), // 209
-    (1944810000, 4), // 210
-    (1982119441, 4), // 211
-    (2019963136, 4), // 212
-    (2058346161, 4), // 213
-    (2097273616, 4), // 214
-    (2136750625, 4), // 215
-    (2176782336, 4), // 216
-    (2217373921, 4), // 217
-    (2258530576, 4), // 218
-    (2300257521, 4), // 219
-    (2342560000, 4), // 220
-    (2385443281, 4), // 221
-    (2428912656, 4), // 222
-    (2472973441, 4), // 223
-    (2517630976, 4), // 224
-    (2562890625, 4), // 225
-    (2608757776, 4), // 226
-    (2655237841, 4), // 227
-    (2702336256, 4), // 228
-    (2750058481, 4), // 229
-    (2798410000, 4), // 230
-    (2847396321, 4), // 231
-    (2897022976, 4), // 232
-    (2947295521, 4), // 233
-    (2998219536, 4), // 234
-    (3049800625, 4), // 235
-    (3102044416, 4), // 236
-    (3154956561, 4), // 237
-    (3208542736, 4), // 238
-    (3262808641, 4), // 239
-    (3317760000, 4), // 240
-    (3373402561, 4), // 241
-    (3429742096, 4), // 242
-    (3486784401, 4), // 243
-    (3544535296, 4), // 244
-    (3603000625, 4), // 245
-    (3662186256, 4), // 246
-    (3722098081, 4), // 247
-    (3782742016, 4), // 248
-    (3844124001, 4), // 249
-    (3906250000, 4), // 250
-    (3969126001, 4), // 251
-    (4032758016, 4), // 252
-    (4097152081, 4), // 253
-    (4162314256, 4), // 254
-    (4228250625, 4), // 255
-    (0, 0), // 256
-];
-#[deny(overflowing_literals)]
-pub(crate) static BASES_64: [(u64, usize); 257] = [
-    (0, 0), // 0
-    (0, 0), // 1
-    (0, 0), // 2
-    (12157665459056928801, 40), // 3
-    (0, 0), // 4
-    (7450580596923828125, 27), // 5
-    (4738381338321616896, 24), // 6
-    (3909821048582988049, 22), // 7
-    (0, 0), // 8
-    (12157665459056928801, 20), // 9
-    (10000000000000000000, 19), // 10
-    (5559917313492231481, 18), // 11
-    (2218611106740436992, 17), // 12
-    (8650415919381337933, 17), // 13
-    (2177953337809371136, 16), // 14
-    (6568408355712890625, 16), // 15
-    (0, 0), // 16
-    (2862423051509815793, 15), // 17
-    (6746640616477458432, 15), // 18
-    (15181127029874798299, 15), // 19
-    (1638400000000000000, 14), // 20
-    (3243919932521508681, 14), // 21
-    (6221821273427820544, 14), // 22
-    (11592836324538749809, 14), // 23
-    (876488338465357824, 13), // 24
-    (1490116119384765625, 13), // 25
-    (2481152873203736576, 13), // 26
-    (4052555153018976267, 13), // 27
-    (6502111422497947648, 13), // 28
-    (10260628712958602189, 13), // 29
-    (15943230000000000000, 13), // 30
-    (787662783788549761, 12), // 31
-    (0, 0), // 32
-    (1667889514952984961, 12), // 33
-    (2386420683693101056, 12), // 34
-    (3379220508056640625, 12), // 35
-    (4738381338321616896, 12), // 36
-    (6582952005840035281, 12), // 37
-    (9065737908494995456, 12), // 38
-    (12381557655576425121, 12), // 39
-    (16777216000000000000, 12), // 40
-    (550329031716248441, 11), // 41
-    (717368321110468608, 11), // 42
-    (929293739471222707, 11), // 43
-    (1196683881290399744, 11), // 44
-    (1532278301220703125, 11), // 45
-    (1951354384207722496, 11), // 46
-    (2472159215084012303, 11), // 47
-    (3116402981210161152, 11), // 48
-    (3909821048582988049, 11), // 49
-    (4882812500000000000, 11), // 50
-    (6071163615208263051, 11), // 51
-    (7516865509350965248, 11), // 52
-    (9269035929372191597, 11), // 53
-    (11384956040305711104, 11), // 54
-    (13931233916552734375, 11), // 55
-    (16985107389382393856, 11), // 56
-    (362033331456891249, 10), // 57
-    (430804206899405824, 10), // 58
-    (511116753300641401, 10), // 59
-    (604661760000000000, 10), // 60
-    (713342911662882601, 10), // 61
-    (839299365868340224, 10), // 62
-    (984930291881790849, 10), // 63
-    (0, 0), // 64
-    (1346274334462890625, 10), // 65
-    (1568336880910795776, 10), // 66
-    (1822837804551761449, 10), // 67
-    (2113922820157210624, 10), // 68
-    (2446194060654759801, 10), // 69
-    (2824752490000000000, 10), // 70
-    (3255243551009881201, 10), // 71
-    (3743906242624487424, 10), // 72
-    (4297625829703557649, 10), // 73
-    (4923990397355877376, 10), // 74
-    (5631351470947265625, 10), // 75
-    (6428888932339941376, 10), // 76
-    (7326680472586200649, 10), // 77
-    (8335775831236199424, 10), // 78
-    (9468276082626847201, 10), // 79
-    (10737418240000000000, 10), // 80
-    (12157665459056928801, 10), // 81
-    (13744803133596058624, 10), // 82
-    (15516041187205853449, 10), // 83
-    (17490122876598091776, 10), // 84
-    (231616946283203125, 9), // 85
-    (257327417311663616, 9), // 86
-    (285544154243029527, 9), // 87
-    (316478381828866048, 9), // 88
-    (350356403707485209, 9), // 89
-    (387420489000000000, 9), // 90
-    (427929800129788411, 9), // 91
-    (472161363286556672, 9), // 92
-    (520411082988487293, 9), // 93
-    (572994802228616704, 9), // 94
-    (630249409724609375, 9), // 95
-    (692533995824480256, 9), // 96
-    (760231058654565217, 9), // 97
-    (833747762130149888, 9), // 98
-    (913517247483640899, 9), // 99
-    (1000000000000000000, 9), // 100
-    (1093685272684360901, 9), // 101
-    (1195092568622310912, 9), // 102
-    (1304773183829244583, 9), // 103
-    (1423311812421484544, 9), // 104
-    (1551328215978515625, 9), // 105
-    (1689478959002692096, 9), // 106
-    (1838459212420154507, 9), // 107
-    (1999004627104432128, 9), // 108
-    (2171893279442309389, 9), // 109
-    (2357947691000000000, 9), // 110
-    (2558036924386500591, 9), // 111
-    (2773078757450186752, 9), // 112
-    (3004041937984268273, 9), // 113
-    (3251948521156637184, 9), // 114
-    (3517876291919921875, 9), // 115
-    (3802961274698203136, 9), // 116
-    (4108400332687853397, 9), // 117
-    (4435453859151328768, 9), // 118
-    (4785448563124474679, 9), // 119
-    (5159780352000000000, 9), // 120
-    (5559917313492231481, 9), // 121
-    (5987402799531080192, 9), // 122
-    (6443858614676334363, 9), // 123
-    (6930988311686938624, 9), // 124
-    (7450580596923828125, 9), // 125
-    (8004512848309157376, 9), // 126
-    (8594754748609397887, 9), // 127
-    (0, 0), // 128
-    (9892530380752880769, 9), // 129
-    (10604499373000000000, 9), // 130
-    (11361656654439817571, 9), // 131
-    (12166492167065567232, 9), // 132
-    (13021612539908538853, 9), // 133
-    (13929745610903012864, 9), // 134
-    (14893745087865234375, 9), // 135
-    (15916595351771938816, 9), // 136
-    (17001416405572203977, 9), // 137
-    (18151468971815029248, 9), // 138
-    (139353667211683681, 8), // 139
-    (147578905600000000, 8), // 140
-    (156225851787813921, 8), // 141
-    (165312903998914816, 8), // 142
-    (174859124550883201, 8), // 143
-    (184884258895036416, 8), // 144
-    (195408755062890625, 8), // 145
-    (206453783524884736, 8), // 146
-    (218041257467152161, 8), // 147
-    (230193853492166656, 8), // 148
-    (242935032749128801, 8), // 149
-    (256289062500000000, 8), // 150
-    (270281038127131201, 8), // 151
-    (284936905588473856, 8), // 152
-    (300283484326400961, 8), // 153
-    (316348490636206336, 8), // 154
-    (333160561500390625, 8), // 155
-    (350749278894882816, 8), // 156
-    (369145194573386401, 8), // 157
-    (388379855336079616, 8), // 158
-    (408485828788939521, 8), // 159
-    (429496729600000000, 8), // 160
-    (451447246258894081, 8), // 161
-    (474373168346071296, 8), // 162
-    (498311414318121121, 8), // 163
-    (523300059815673856, 8), // 164
-    (549378366500390625, 8), // 165
-    (576586811427594496, 8), // 166
-    (604967116961135041, 8), // 167
-    (634562281237118976, 8), // 168
-    (665416609183179841, 8), // 169
-    (697575744100000000, 8), // 170
-    (731086699811838561, 8), // 171
-    (765997893392859136, 8), // 172
-    (802359178476091681, 8), // 173
-    (840221879151902976, 8), // 174
-    (879638824462890625, 8), // 175
-    (920664383502155776, 8), // 176
-    (963354501121950081, 8), // 177
-    (1007766734259732736, 8), // 178
-    (1053960288888713761, 8), // 179
-    (1101996057600000000, 8), // 180
-    (1151936657823500641, 8), // 181
-    (1203846470694789376, 8), // 182
-    (1257791680575160641, 8), // 183
-    (1313840315232157696, 8), // 184
-    (1372062286687890625, 8), // 185
-    (1432529432742502656, 8), // 186
-    (1495315559180183521, 8), // 187
-    (1560496482665168896, 8), // 188
-    (1628150074335205281, 8), // 189
-    (1698356304100000000, 8), // 190
-    (1771197285652216321, 8), // 191
-    (1846757322198614016, 8), // 192
-    (1925122952918976001, 8), // 193
-    (2006383000160502016, 8), // 194
-    (2090628617375390625, 8), // 195
-    (2177953337809371136, 8), // 196
-    (2268453123948987361, 8), // 197
-    (2362226417735475456, 8), // 198
-    (2459374191553118401, 8), // 199
-    (2560000000000000000, 8), // 200
-    (2664210032449121601, 8), // 201
-    (2772113166407885056, 8), // 202
-    (2883821021683985761, 8), // 203
-    (2999448015365799936, 8), // 204
-    (3119111417625390625, 8), // 205
-    (3242931408352297216, 8), // 206
-    (3371031134626313601, 8), // 207
-    (3503536769037500416, 8), // 208
-    (3640577568861717121, 8), // 209
-    (3782285936100000000, 8), // 210
-    (3928797478390152481, 8), // 211
-    (4080251070798954496, 8), // 212
-    (4236788918503437921, 8), // 213
-    (4398556620369715456, 8), // 214
-    (4565703233437890625, 8), // 215
-    (4738381338321616896, 8), // 216
-    (4916747105530914241, 8), // 217
-    (5100960362726891776, 8), // 218
-    (5291184662917065441, 8), // 219
-    (5487587353600000000, 8), // 220
-    (5690339646868044961, 8), // 221
-    (5899616690476974336, 8), // 222
-    (6115597639891380481, 8), // 223
-    (6338465731314712576, 8), // 224
-    (6568408355712890625, 8), // 225
-    (6805617133840466176, 8), // 226
-    (7050287992278341281, 8), // 227
-    (7302621240492097536, 8), // 228
-    (7562821648920027361, 8), // 229
-    (7831098528100000000, 8), // 230
-    (8107665808844335041, 8), // 231
-    (8392742123471896576, 8), // 232
-    (8686550888106661441, 8), // 233
-    (8989320386052055296, 8), // 234
-    (9301283852250390625, 8), // 235
-    (9622679558836781056, 8), // 236
-    (9953750901796946721, 8), // 237
-    (10294746488738365696, 8), // 238
-    (10645920227784266881, 8), // 239
-    (11007531417600000000, 8), // 240
-    (11379844838561358721, 8), // 241
-    (11763130845074473216, 8), // 242
-    (12157665459056928801, 8), // 243
-    (12563730464589807616, 8), // 244
-    (12981613503750390625, 8), // 245
-    (13411608173635297536, 8), // 246
-    (13854014124583882561, 8), // 247
-    (14309137159611744256, 8), // 248
-    (14777289335064248001, 8), // 249
-    (15258789062500000000, 8), // 250
-    (15753961211814252001, 8), // 251
-    (16263137215612256256, 8), // 252
-    (16786655174842630561, 8), // 253
-    (17324859965700833536, 8), // 254
-    (17878103347812890625, 8), // 255
-    (0, 0), // 256
-];
diff --git a/crates/num-bigint/patches/pin-autocfg-version.patch b/crates/num-bigint/patches/pin-autocfg-version.patch
deleted file mode 100644
index 7930756..0000000
--- a/crates/num-bigint/patches/pin-autocfg-version.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-diff --git a/Cargo.toml b/Cargo.toml
-index 2656b5f..159937f 100644
---- a/Cargo.toml
-+++ b/Cargo.toml
-@@ -93,7 +93,10 @@ optional = true
- default-features = false
- 
- [build-dependencies.autocfg]
--version = "1"
-+# autocfg 1.4 uses different, non-deterministic filenames. We don't
-+# actually depend on these files, but they are present in the out/
-+# directory, and it's simpler to keep them around.
-+version = "=1.1.0"
- 
- [features]
- default = ["std"]
diff --git a/crates/num-bigint/patches/remove-probe-files.patch b/crates/num-bigint/patches/remove-probe-files.patch
deleted file mode 100644
index 0292603..0000000
--- a/crates/num-bigint/patches/remove-probe-files.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/build.rs b/build.rs
-index 5d5406c..82b7a43 100644
---- a/build.rs
-+++ b/build.rs
-@@ -37,6 +37,15 @@ fn main() {
-         }
-     }
- 
-+    // autocfg generates probe files in $OUT_DIR with nondeterministic contents.
-+    // (In autocfg 1.4, the filenames are nondeterministic as well.)
-+    let out_dir_env = env::var("OUT_DIR").unwrap();
-+    let out_dir = Path::new(&out_dir_env);
-+    std::fs::remove_file(out_dir.join("probe0.ll")).unwrap();
-+    std::fs::remove_file(out_dir.join("probe1.ll")).unwrap();
-+    std::fs::remove_file(out_dir.join("probe2.ll")).unwrap();
-+    std::fs::remove_file(out_dir.join("probe3.ll")).unwrap();
-+
-     autocfg::rerun_path("build.rs");
- 
-     write_radix_bases().unwrap();
diff --git a/crates/num-bigint/src/bigint.rs b/crates/num-bigint/src/bigint.rs
index 97faa83..b4f84b9 100644
--- a/crates/num-bigint/src/bigint.rs
+++ b/crates/num-bigint/src/bigint.rs
@@ -1,18 +1,17 @@
 // `Add`/`Sub` ops may flip from `BigInt` to its `BigUint` magnitude
 #![allow(clippy::suspicious_arithmetic_impl)]
 
-use crate::std_alloc::{String, Vec};
+use alloc::string::String;
+use alloc::vec::Vec;
 use core::cmp::Ordering::{self, Equal};
 use core::default::Default;
 use core::fmt;
 use core::hash;
 use core::ops::{Neg, Not};
 use core::str;
-use core::{i128, u128};
-use core::{i64, u64};
 
 use num_integer::{Integer, Roots};
-use num_traits::{Num, One, Pow, Signed, Zero};
+use num_traits::{ConstZero, Num, One, Pow, Signed, Zero};
 
 use self::Sign::{Minus, NoSign, Plus};
 
@@ -25,16 +24,12 @@
 mod multiplication;
 mod subtraction;
 
+mod arbitrary;
 mod bits;
 mod convert;
 mod power;
-mod shift;
-
-#[cfg(any(feature = "quickcheck", feature = "arbitrary"))]
-mod arbitrary;
-
-#[cfg(feature = "serde")]
 mod serde;
+mod shift;
 
 /// A `Sign` is a [`BigInt`]'s composing element.
 #[derive(PartialEq, PartialOrd, Eq, Ord, Copy, Clone, Debug, Hash)]
@@ -132,7 +127,7 @@
 impl Default for BigInt {
     #[inline]
     fn default() -> BigInt {
-        Zero::zero()
+        Self::ZERO
     }
 }
 
@@ -211,10 +206,7 @@
 impl Zero for BigInt {
     #[inline]
     fn zero() -> BigInt {
-        BigInt {
-            sign: NoSign,
-            data: BigUint::zero(),
-        }
+        Self::ZERO
     }
 
     #[inline]
@@ -229,6 +221,11 @@
     }
 }
 
+impl ConstZero for BigInt {
+    // forward to the inherent const
+    const ZERO: Self = Self::ZERO;
+}
+
 impl One for BigInt {
     #[inline]
     fn one() -> BigInt {
@@ -262,7 +259,7 @@
     #[inline]
     fn abs_sub(&self, other: &BigInt) -> BigInt {
         if *self <= *other {
-            Zero::zero()
+            Self::ZERO
         } else {
             self - other
         }
@@ -273,7 +270,7 @@
         match self.sign {
             Plus => BigInt::one(),
             Minus => -BigInt::one(),
-            NoSign => BigInt::zero(),
+            NoSign => Self::ZERO,
         }
     }
 
@@ -291,10 +288,6 @@
 trait UnsignedAbs {
     type Unsigned;
 
-    /// A convenience method for getting the absolute value of a signed primitive as unsigned
-    /// See also `unsigned_abs`: <https://github.com/rust-lang/rust/issues/74913>
-    fn uabs(self) -> Self::Unsigned;
-
     fn checked_uabs(self) -> CheckedUnsignedAbs<Self::Unsigned>;
 }
 
@@ -310,11 +303,6 @@
             type Unsigned = $Unsigned;
 
             #[inline]
-            fn uabs(self) -> $Unsigned {
-                self.wrapping_abs() as $Unsigned
-            }
-
-            #[inline]
             fn checked_uabs(self) -> CheckedUnsignedAbs<Self::Unsigned> {
                 if self >= 0 {
                     Positive(self as $Unsigned)
@@ -462,7 +450,7 @@
     fn extended_gcd_lcm(&self, other: &BigInt) -> (num_integer::ExtendedGcd<BigInt>, BigInt) {
         let egcd = self.extended_gcd(other);
         let lcm = if egcd.gcd.is_zero() {
-            BigInt::zero()
+            Self::ZERO
         } else {
             BigInt::from(&self.data / &egcd.gcd.data * &other.data)
         };
@@ -508,6 +496,14 @@
     fn prev_multiple_of(&self, other: &Self) -> Self {
         self - self.mod_floor(other)
     }
+
+    fn dec(&mut self) {
+        *self -= 1u32;
+    }
+
+    fn inc(&mut self) {
+        *self += 1u32;
+    }
 }
 
 impl Roots for BigInt {
@@ -567,6 +563,12 @@
 }
 
 impl BigInt {
+    /// A constant `BigInt` with value 0, useful for static initialization.
+    pub const ZERO: Self = BigInt {
+        sign: NoSign,
+        data: BigUint::ZERO,
+    };
+
     /// Creates and initializes a [`BigInt`].
     ///
     /// The base 2<sup>32</sup> digits are ordered least significant digit first.
@@ -922,11 +924,10 @@
     ///
     /// ```
     /// use num_bigint::{BigInt, Sign};
-    /// use num_traits::Zero;
     ///
     /// assert_eq!(BigInt::from(1234).sign(), Sign::Plus);
     /// assert_eq!(BigInt::from(-4321).sign(), Sign::Minus);
-    /// assert_eq!(BigInt::zero().sign(), Sign::NoSign);
+    /// assert_eq!(BigInt::ZERO.sign(), Sign::NoSign);
     /// ```
     #[inline]
     pub fn sign(&self) -> Sign {
@@ -943,7 +944,7 @@
     ///
     /// assert_eq!(BigInt::from(1234).magnitude(), &BigUint::from(1234u32));
     /// assert_eq!(BigInt::from(-4321).magnitude(), &BigUint::from(4321u32));
-    /// assert!(BigInt::zero().magnitude().is_zero());
+    /// assert!(BigInt::ZERO.magnitude().is_zero());
     /// ```
     #[inline]
     pub fn magnitude(&self) -> &BigUint {
@@ -957,11 +958,10 @@
     ///
     /// ```
     /// use num_bigint::{BigInt, BigUint, Sign};
-    /// use num_traits::Zero;
     ///
     /// assert_eq!(BigInt::from(1234).into_parts(), (Sign::Plus, BigUint::from(1234u32)));
     /// assert_eq!(BigInt::from(-4321).into_parts(), (Sign::Minus, BigUint::from(4321u32)));
-    /// assert_eq!(BigInt::zero().into_parts(), (Sign::NoSign, BigUint::zero()));
+    /// assert_eq!(BigInt::ZERO.into_parts(), (Sign::NoSign, BigUint::ZERO));
     /// ```
     #[inline]
     pub fn into_parts(self) -> (Sign, BigUint) {
@@ -980,7 +980,7 @@
     pub fn to_biguint(&self) -> Option<BigUint> {
         match self.sign {
             Plus => Some(self.data.clone()),
-            NoSign => Some(Zero::zero()),
+            NoSign => Some(BigUint::ZERO),
             Minus => None,
         }
     }
@@ -1025,6 +1025,64 @@
         power::modpow(self, exponent, modulus)
     }
 
+    /// Returns the modular multiplicative inverse if it exists, otherwise `None`.
+    ///
+    /// This solves for `x` such that `self * x ≡ 1 (mod modulus)`.
+    /// Note that this rounds like `mod_floor`, not like the `%` operator,
+    /// which makes a difference when given a negative `self` or `modulus`.
+    /// The solution will be in the interval `[0, modulus)` for `modulus > 0`,
+    /// or in the interval `(modulus, 0]` for `modulus < 0`,
+    /// and it exists if and only if `gcd(self, modulus) == 1`.
+    ///
+    /// ```
+    /// use num_bigint::BigInt;
+    /// use num_integer::Integer;
+    /// use num_traits::{One, Zero};
+    ///
+    /// let m = BigInt::from(383);
+    ///
+    /// // Trivial cases
+    /// assert_eq!(BigInt::zero().modinv(&m), None);
+    /// assert_eq!(BigInt::one().modinv(&m), Some(BigInt::one()));
+    /// let neg1 = &m - 1u32;
+    /// assert_eq!(neg1.modinv(&m), Some(neg1));
+    ///
+    /// // Positive self and modulus
+    /// let a = BigInt::from(271);
+    /// let x = a.modinv(&m).unwrap();
+    /// assert_eq!(x, BigInt::from(106));
+    /// assert_eq!(x.modinv(&m).unwrap(), a);
+    /// assert_eq!((&a * x).mod_floor(&m), BigInt::one());
+    ///
+    /// // Negative self and positive modulus
+    /// let b = -&a;
+    /// let x = b.modinv(&m).unwrap();
+    /// assert_eq!(x, BigInt::from(277));
+    /// assert_eq!((&b * x).mod_floor(&m), BigInt::one());
+    ///
+    /// // Positive self and negative modulus
+    /// let n = -&m;
+    /// let x = a.modinv(&n).unwrap();
+    /// assert_eq!(x, BigInt::from(-277));
+    /// assert_eq!((&a * x).mod_floor(&n), &n + 1);
+    ///
+    /// // Negative self and modulus
+    /// let x = b.modinv(&n).unwrap();
+    /// assert_eq!(x, BigInt::from(-106));
+    /// assert_eq!((&b * x).mod_floor(&n), &n + 1);
+    /// ```
+    pub fn modinv(&self, modulus: &Self) -> Option<Self> {
+        let result = self.data.modinv(&modulus.data)?;
+        // The sign of the result follows the modulus, like `mod_floor`.
+        let (sign, mag) = match (self.is_negative(), modulus.is_negative()) {
+            (false, false) => (Plus, result),
+            (true, false) => (Plus, &modulus.data - result),
+            (false, true) => (Minus, &modulus.data - result),
+            (true, true) => (Minus, result),
+        };
+        Some(BigInt::from_biguint(sign, mag))
+    }
+
     /// Returns the truncated principal square root of `self` --
     /// see [`num_integer::Roots::sqrt()`].
     pub fn sqrt(&self) -> Self {
diff --git a/crates/num-bigint/src/bigint/addition.rs b/crates/num-bigint/src/bigint/addition.rs
index 76aeb99..0d3a0e2 100644
--- a/crates/num-bigint/src/bigint/addition.rs
+++ b/crates/num-bigint/src/bigint/addition.rs
@@ -8,7 +8,7 @@
 use core::iter::Sum;
 use core::mem;
 use core::ops::{Add, AddAssign};
-use num_traits::{CheckedAdd, Zero};
+use num_traits::CheckedAdd;
 
 // We want to forward to BigUint::add, but it's not clear how that will go until
 // we compare both sign and magnitude.  So we duplicate this body for every
@@ -24,7 +24,7 @@
             (Plus, Minus) | (Minus, Plus) => match $a.data.cmp(&$b.data) {
                 Less => BigInt::from_biguint($b.sign, $b_data - $a_data),
                 Greater => BigInt::from_biguint($a.sign, $a_data - $b_data),
-                Equal => Zero::zero(),
+                Equal => BigInt::ZERO,
             },
         }
     };
@@ -76,7 +76,7 @@
 impl AddAssign<&BigInt> for BigInt {
     #[inline]
     fn add_assign(&mut self, other: &BigInt) {
-        let n = mem::replace(self, BigInt::zero());
+        let n = mem::replace(self, Self::ZERO);
         *self = n + other;
     }
 }
@@ -97,7 +97,7 @@
             NoSign => From::from(other),
             Plus => BigInt::from(self.data + other),
             Minus => match self.data.cmp(&From::from(other)) {
-                Equal => Zero::zero(),
+                Equal => Self::ZERO,
                 Less => BigInt::from(other - self.data),
                 Greater => -BigInt::from(self.data - other),
             },
@@ -108,7 +108,7 @@
 impl AddAssign<u32> for BigInt {
     #[inline]
     fn add_assign(&mut self, other: u32) {
-        let n = mem::replace(self, BigInt::zero());
+        let n = mem::replace(self, Self::ZERO);
         *self = n + other;
     }
 }
@@ -122,7 +122,7 @@
             NoSign => From::from(other),
             Plus => BigInt::from(self.data + other),
             Minus => match self.data.cmp(&From::from(other)) {
-                Equal => Zero::zero(),
+                Equal => Self::ZERO,
                 Less => BigInt::from(other - self.data),
                 Greater => -BigInt::from(self.data - other),
             },
@@ -133,7 +133,7 @@
 impl AddAssign<u64> for BigInt {
     #[inline]
     fn add_assign(&mut self, other: u64) {
-        let n = mem::replace(self, BigInt::zero());
+        let n = mem::replace(self, Self::ZERO);
         *self = n + other;
     }
 }
@@ -147,7 +147,7 @@
             NoSign => BigInt::from(other),
             Plus => BigInt::from(self.data + other),
             Minus => match self.data.cmp(&From::from(other)) {
-                Equal => BigInt::zero(),
+                Equal => Self::ZERO,
                 Less => BigInt::from(other - self.data),
                 Greater => -BigInt::from(self.data - other),
             },
@@ -157,7 +157,7 @@
 impl AddAssign<u128> for BigInt {
     #[inline]
     fn add_assign(&mut self, other: u128) {
-        let n = mem::replace(self, BigInt::zero());
+        let n = mem::replace(self, Self::ZERO);
         *self = n + other;
     }
 }
diff --git a/crates/num-bigint/src/bigint/arbitrary.rs b/crates/num-bigint/src/bigint/arbitrary.rs
index df66050..3cb90b3 100644
--- a/crates/num-bigint/src/bigint/arbitrary.rs
+++ b/crates/num-bigint/src/bigint/arbitrary.rs
@@ -1,10 +1,13 @@
-use super::{BigInt, Sign};
+#![cfg(any(feature = "quickcheck", feature = "arbitrary"))]
 
-#[cfg(feature = "quickcheck")]
-use crate::std_alloc::Box;
+use super::{BigInt, Sign};
 use crate::BigUint;
 
 #[cfg(feature = "quickcheck")]
+use alloc::boxed::Box;
+
+#[cfg(feature = "quickcheck")]
+#[cfg_attr(docsrs, doc(cfg(feature = "quickcheck")))]
 impl quickcheck::Arbitrary for BigInt {
     fn arbitrary(g: &mut quickcheck::Gen) -> Self {
         let positive = bool::arbitrary(g);
@@ -20,6 +23,7 @@
 }
 
 #[cfg(feature = "arbitrary")]
+#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
 impl arbitrary::Arbitrary<'_> for BigInt {
     fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
         let positive = bool::arbitrary(u)?;
diff --git a/crates/num-bigint/src/bigint/bits.rs b/crates/num-bigint/src/bigint/bits.rs
index 80f4e2c..ac4fe1d 100644
--- a/crates/num-bigint/src/bigint/bits.rs
+++ b/crates/num-bigint/src/bigint/bits.rs
@@ -3,8 +3,8 @@
 
 use crate::big_digit::{self, BigDigit, DoubleBigDigit};
 use crate::biguint::IntDigits;
-use crate::std_alloc::Vec;
 
+use alloc::vec::Vec;
 use core::cmp::Ordering::{Equal, Greater, Less};
 use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
 use num_traits::{ToPrimitive, Zero};
@@ -36,7 +36,7 @@
 // + 1 & -ff = ...0 01 & ...f 01 = ...0 01 = + 1
 // +ff & - 1 = ...0 ff & ...f ff = ...0 ff = +ff
 // answer is pos, has length of a
-fn bitand_pos_neg(a: &mut Vec<BigDigit>, b: &[BigDigit]) {
+fn bitand_pos_neg(a: &mut [BigDigit], b: &[BigDigit]) {
     let mut carry_b = 1;
     for (ai, &bi) in a.iter_mut().zip(b.iter()) {
         let twos_b = negate_carry(bi, &mut carry_b);
@@ -114,7 +114,7 @@
     #[inline]
     fn bitand(self, other: &BigInt) -> BigInt {
         match (self.sign, other.sign) {
-            (NoSign, _) | (_, NoSign) => BigInt::zero(),
+            (NoSign, _) | (_, NoSign) => BigInt::ZERO,
             (Plus, Plus) => BigInt::from(&self.data & &other.data),
             (Plus, Minus) => self.clone() & other,
             (Minus, Plus) => other.clone() & self,
@@ -202,7 +202,7 @@
 // - 1 | +ff = ...f ff | ...0 ff = ...f ff = - 1
 // -ff | + 1 = ...f 01 | ...0 01 = ...f 01 = -ff
 // answer is neg, has length of a
-fn bitor_neg_pos(a: &mut Vec<BigDigit>, b: &[BigDigit]) {
+fn bitor_neg_pos(a: &mut [BigDigit], b: &[BigDigit]) {
     let mut carry_a = 1;
     let mut carry_or = 1;
     for (ai, &bi) in a.iter_mut().zip(b.iter()) {
diff --git a/crates/num-bigint/src/bigint/convert.rs b/crates/num-bigint/src/bigint/convert.rs
index c4f888b..d0e28b6 100644
--- a/crates/num-bigint/src/bigint/convert.rs
+++ b/crates/num-bigint/src/bigint/convert.rs
@@ -1,13 +1,11 @@
 use super::Sign::{self, Minus, NoSign, Plus};
 use super::{BigInt, ToBigInt};
 
-use crate::std_alloc::Vec;
-#[cfg(has_try_from)]
 use crate::TryFromBigIntError;
 use crate::{BigUint, ParseBigIntError, ToBigUint};
 
+use alloc::vec::Vec;
 use core::cmp::Ordering::{Equal, Greater, Less};
-#[cfg(has_try_from)]
 use core::convert::TryFrom;
 use core::str::{self, FromStr};
 use num_traits::{FromPrimitive, Num, One, ToPrimitive, Zero};
@@ -27,8 +25,7 @@
     /// Creates and initializes a [`BigInt`].
     #[inline]
     fn from_str_radix(mut s: &str, radix: u32) -> Result<BigInt, ParseBigIntError> {
-        let sign = if s.starts_with('-') {
-            let tail = &s[1..];
+        let sign = if let Some(tail) = s.strip_prefix('-') {
             if !tail.starts_with('+') {
                 s = tail
             }
@@ -52,7 +49,7 @@
                 let m: u64 = 1 << 63;
                 match n.cmp(&m) {
                     Less => Some(-(n as i64)),
-                    Equal => Some(core::i64::MIN),
+                    Equal => Some(i64::MIN),
                     Greater => None,
                 }
             }
@@ -69,7 +66,7 @@
                 let m: u128 = 1 << 127;
                 match n.cmp(&m) {
                     Less => Some(-(n as i128)),
-                    Equal => Some(core::i128::MIN),
+                    Equal => Some(i128::MIN),
                     Greater => None,
                 }
             }
@@ -109,7 +106,6 @@
 
 macro_rules! impl_try_from_bigint {
     ($T:ty, $to_ty:path) => {
-        #[cfg(has_try_from)]
         impl TryFrom<&BigInt> for $T {
             type Error = TryFromBigIntError<()>;
 
@@ -119,7 +115,6 @@
             }
         }
 
-        #[cfg(has_try_from)]
         impl TryFrom<BigInt> for $T {
             type Error = TryFromBigIntError<BigInt>;
 
@@ -183,7 +178,7 @@
         if n >= 0 {
             BigInt::from(n as u64)
         } else {
-            let u = core::u64::MAX - (n as u64) + 1;
+            let u = u64::MAX - (n as u64) + 1;
             BigInt {
                 sign: Minus,
                 data: BigUint::from(u),
@@ -198,7 +193,7 @@
         if n >= 0 {
             BigInt::from(n as u128)
         } else {
-            let u = core::u128::MAX - (n as u128) + 1;
+            let u = u128::MAX - (n as u128) + 1;
             BigInt {
                 sign: Minus,
                 data: BigUint::from(u),
@@ -232,7 +227,7 @@
                 data: BigUint::from(n),
             }
         } else {
-            BigInt::zero()
+            Self::ZERO
         }
     }
 }
@@ -246,7 +241,7 @@
                 data: BigUint::from(n),
             }
         } else {
-            BigInt::zero()
+            Self::ZERO
         }
     }
 }
@@ -271,7 +266,7 @@
     #[inline]
     fn from(n: BigUint) -> Self {
         if n.is_zero() {
-            BigInt::zero()
+            Self::ZERO
         } else {
             BigInt {
                 sign: Plus,
@@ -292,7 +287,7 @@
     #[inline]
     fn to_bigint(&self) -> Option<BigInt> {
         if self.is_zero() {
-            Some(Zero::zero())
+            Some(BigInt::ZERO)
         } else {
             Some(BigInt {
                 sign: Plus,
@@ -307,13 +302,12 @@
     fn to_biguint(&self) -> Option<BigUint> {
         match self.sign() {
             Plus => Some(self.data.clone()),
-            NoSign => Some(Zero::zero()),
+            NoSign => Some(BigUint::ZERO),
             Minus => None,
         }
     }
 }
 
-#[cfg(has_try_from)]
 impl TryFrom<&BigInt> for BigUint {
     type Error = TryFromBigIntError<()>;
 
@@ -325,7 +319,6 @@
     }
 }
 
-#[cfg(has_try_from)]
 impl TryFrom<BigInt> for BigUint {
     type Error = TryFromBigIntError<BigInt>;
 
@@ -372,7 +365,7 @@
         if x {
             One::one()
         } else {
-            Zero::zero()
+            Self::ZERO
         }
     }
 }
@@ -382,7 +375,7 @@
     let sign = match digits.first() {
         Some(v) if *v > 0x7f => Sign::Minus,
         Some(_) => Sign::Plus,
-        None => return BigInt::zero(),
+        None => return BigInt::ZERO,
     };
 
     if sign == Sign::Minus {
@@ -400,7 +393,7 @@
     let sign = match digits.last() {
         Some(v) if *v > 0x7f => Sign::Minus,
         Some(_) => Sign::Plus,
-        None => return BigInt::zero(),
+        None => return BigInt::ZERO,
     };
 
     if sign == Sign::Minus {
diff --git a/crates/num-bigint/src/bigint/division.rs b/crates/num-bigint/src/bigint/division.rs
index 318d1fb..0d4d23f 100644
--- a/crates/num-bigint/src/bigint/division.rs
+++ b/crates/num-bigint/src/bigint/division.rs
@@ -358,14 +358,14 @@
 
     #[inline]
     fn rem(self, other: i32) -> BigInt {
-        self % other.uabs()
+        self % other.unsigned_abs()
     }
 }
 
 impl RemAssign<i32> for BigInt {
     #[inline]
     fn rem_assign(&mut self, other: i32) {
-        *self %= other.uabs();
+        *self %= other.unsigned_abs();
     }
 }
 
@@ -386,14 +386,14 @@
 
     #[inline]
     fn rem(self, other: i64) -> BigInt {
-        self % other.uabs()
+        self % other.unsigned_abs()
     }
 }
 
 impl RemAssign<i64> for BigInt {
     #[inline]
     fn rem_assign(&mut self, other: i64) {
-        *self %= other.uabs();
+        *self %= other.unsigned_abs();
     }
 }
 
@@ -414,14 +414,14 @@
 
     #[inline]
     fn rem(self, other: i128) -> BigInt {
-        self % other.uabs()
+        self % other.unsigned_abs()
     }
 }
 
 impl RemAssign<i128> for BigInt {
     #[inline]
     fn rem_assign(&mut self, other: i128) {
-        *self %= other.uabs();
+        *self %= other.unsigned_abs();
     }
 }
 
@@ -463,6 +463,10 @@
         }
         Some(self.rem_euclid(v))
     }
+
+    fn checked_div_rem_euclid(&self, v: &Self) -> Option<(Self, Self)> {
+        Some(self.div_rem_euclid(v))
+    }
 }
 
 impl Euclid for BigInt {
@@ -493,4 +497,17 @@
             r
         }
     }
+
+    fn div_rem_euclid(&self, v: &Self) -> (Self, Self) {
+        let (q, r) = self.div_rem(v);
+        if r.is_negative() {
+            if v.is_positive() {
+                (q - 1, r + v)
+            } else {
+                (q + 1, r - v)
+            }
+        } else {
+            (q, r)
+        }
+    }
 }
diff --git a/crates/num-bigint/src/bigint/power.rs b/crates/num-bigint/src/bigint/power.rs
index 4b41f4f..ef254c8 100644
--- a/crates/num-bigint/src/bigint/power.rs
+++ b/crates/num-bigint/src/bigint/power.rs
@@ -80,7 +80,7 @@
 
     let result = x.data.modpow(&exponent.data, &modulus.data);
     if result.is_zero() {
-        return BigInt::zero();
+        return BigInt::ZERO;
     }
 
     // The sign of the result follows the modulus, like `mod_floor`.
diff --git a/crates/num-bigint/src/bigint/serde.rs b/crates/num-bigint/src/bigint/serde.rs
index 5c232f9..6c47692 100644
--- a/crates/num-bigint/src/bigint/serde.rs
+++ b/crates/num-bigint/src/bigint/serde.rs
@@ -1,3 +1,6 @@
+#![cfg(feature = "serde")]
+#![cfg_attr(docsrs, doc(cfg(feature = "serde")))]
+
 use super::{BigInt, Sign};
 
 use serde::de::{Error, Unexpected};
diff --git a/crates/num-bigint/src/bigint/subtraction.rs b/crates/num-bigint/src/bigint/subtraction.rs
index 548f314..ef77854 100644
--- a/crates/num-bigint/src/bigint/subtraction.rs
+++ b/crates/num-bigint/src/bigint/subtraction.rs
@@ -7,7 +7,7 @@
 use core::cmp::Ordering::{Equal, Greater, Less};
 use core::mem;
 use core::ops::{Sub, SubAssign};
-use num_traits::{CheckedSub, Zero};
+use num_traits::CheckedSub;
 
 // We want to forward to BigUint::sub, but it's not clear how that will go until
 // we compare both sign and magnitude.  So we duplicate this body for every
@@ -23,7 +23,7 @@
             (Plus, Plus) | (Minus, Minus) => match $a.data.cmp(&$b.data) {
                 Less => BigInt::from_biguint(-$a.sign, $b_data - $a_data),
                 Greater => BigInt::from_biguint($a.sign, $a_data - $b_data),
-                Equal => Zero::zero(),
+                Equal => BigInt::ZERO,
             },
         }
     };
@@ -75,7 +75,7 @@
 impl SubAssign<&BigInt> for BigInt {
     #[inline]
     fn sub_assign(&mut self, other: &BigInt) {
-        let n = mem::replace(self, BigInt::zero());
+        let n = mem::replace(self, Self::ZERO);
         *self = n - other;
     }
 }
@@ -96,7 +96,7 @@
             NoSign => -BigInt::from(other),
             Minus => -BigInt::from(self.data + other),
             Plus => match self.data.cmp(&From::from(other)) {
-                Equal => Zero::zero(),
+                Equal => Self::ZERO,
                 Greater => BigInt::from(self.data - other),
                 Less => -BigInt::from(other - self.data),
             },
@@ -106,7 +106,7 @@
 impl SubAssign<u32> for BigInt {
     #[inline]
     fn sub_assign(&mut self, other: u32) {
-        let n = mem::replace(self, BigInt::zero());
+        let n = mem::replace(self, Self::ZERO);
         *self = n - other;
     }
 }
@@ -147,7 +147,7 @@
             NoSign => -BigInt::from(other),
             Minus => -BigInt::from(self.data + other),
             Plus => match self.data.cmp(&From::from(other)) {
-                Equal => Zero::zero(),
+                Equal => Self::ZERO,
                 Greater => BigInt::from(self.data - other),
                 Less => -BigInt::from(other - self.data),
             },
@@ -158,7 +158,7 @@
 impl SubAssign<u64> for BigInt {
     #[inline]
     fn sub_assign(&mut self, other: u64) {
-        let n = mem::replace(self, BigInt::zero());
+        let n = mem::replace(self, Self::ZERO);
         *self = n - other;
     }
 }
@@ -172,7 +172,7 @@
             NoSign => -BigInt::from(other),
             Minus => -BigInt::from(self.data + other),
             Plus => match self.data.cmp(&From::from(other)) {
-                Equal => Zero::zero(),
+                Equal => Self::ZERO,
                 Greater => BigInt::from(self.data - other),
                 Less => -BigInt::from(other - self.data),
             },
@@ -183,7 +183,7 @@
 impl SubAssign<u128> for BigInt {
     #[inline]
     fn sub_assign(&mut self, other: u128) {
-        let n = mem::replace(self, BigInt::zero());
+        let n = mem::replace(self, Self::ZERO);
         *self = n - other;
     }
 }
diff --git a/crates/num-bigint/src/bigrand.rs b/crates/num-bigint/src/bigrand.rs
index ec03224..e5cbacd 100644
--- a/crates/num-bigint/src/bigrand.rs
+++ b/crates/num-bigint/src/bigrand.rs
@@ -1,4 +1,6 @@
 //! Randomization of big integers
+#![cfg(feature = "rand")]
+#![cfg_attr(docsrs, doc(cfg(feature = "rand")))]
 
 use rand::distributions::uniform::{SampleBorrow, SampleUniform, UniformSampler};
 use rand::prelude::*;
@@ -47,42 +49,42 @@
 }
 
 impl<R: Rng + ?Sized> RandBigInt for R {
-    #[cfg(not(u64_digit))]
-    fn gen_biguint(&mut self, bit_size: u64) -> BigUint {
-        let (digits, rem) = bit_size.div_rem(&32);
-        let len = (digits + (rem > 0) as u64)
-            .to_usize()
-            .expect("capacity overflow");
-        let mut data = vec![0u32; len];
-        gen_bits(self, &mut data, rem);
-        biguint_from_vec(data)
-    }
-
-    #[cfg(u64_digit)]
-    fn gen_biguint(&mut self, bit_size: u64) -> BigUint {
-        use core::slice;
-
-        let (digits, rem) = bit_size.div_rem(&32);
-        let len = (digits + (rem > 0) as u64)
-            .to_usize()
-            .expect("capacity overflow");
-        let native_digits = Integer::div_ceil(&bit_size, &64);
-        let native_len = native_digits.to_usize().expect("capacity overflow");
-        let mut data = vec![0u64; native_len];
-        unsafe {
-            // Generate bits in a `&mut [u32]` slice for value stability
-            let ptr = data.as_mut_ptr() as *mut u32;
-            debug_assert!(native_len * 2 >= len);
-            let data = slice::from_raw_parts_mut(ptr, len);
-            gen_bits(self, data, rem);
+    cfg_digit!(
+        fn gen_biguint(&mut self, bit_size: u64) -> BigUint {
+            let (digits, rem) = bit_size.div_rem(&32);
+            let len = (digits + (rem > 0) as u64)
+                .to_usize()
+                .expect("capacity overflow");
+            let mut data = vec![0u32; len];
+            gen_bits(self, &mut data, rem);
+            biguint_from_vec(data)
         }
-        #[cfg(target_endian = "big")]
-        for digit in &mut data {
-            // swap u32 digits into u64 endianness
-            *digit = (*digit << 32) | (*digit >> 32);
+
+        fn gen_biguint(&mut self, bit_size: u64) -> BigUint {
+            use core::slice;
+
+            let (digits, rem) = bit_size.div_rem(&32);
+            let len = (digits + (rem > 0) as u64)
+                .to_usize()
+                .expect("capacity overflow");
+            let native_digits = Integer::div_ceil(&bit_size, &64);
+            let native_len = native_digits.to_usize().expect("capacity overflow");
+            let mut data = vec![0u64; native_len];
+            unsafe {
+                // Generate bits in a `&mut [u32]` slice for value stability
+                let ptr = data.as_mut_ptr() as *mut u32;
+                debug_assert!(native_len * 2 >= len);
+                let data = slice::from_raw_parts_mut(ptr, len);
+                gen_bits(self, data, rem);
+            }
+            #[cfg(target_endian = "big")]
+            for digit in &mut data {
+                // swap u32 digits into u64 endianness
+                *digit = (*digit << 32) | (*digit >> 32);
+            }
+            biguint_from_vec(data)
         }
-        biguint_from_vec(data)
-    }
+    );
 
     fn gen_bigint(&mut self, bit_size: u64) -> BigInt {
         loop {
diff --git a/crates/num-bigint/src/biguint.rs b/crates/num-bigint/src/biguint.rs
index 1554eb0..196fa32 100644
--- a/crates/num-bigint/src/biguint.rs
+++ b/crates/num-bigint/src/biguint.rs
@@ -1,6 +1,7 @@
 use crate::big_digit::{self, BigDigit};
-use crate::std_alloc::{String, Vec};
 
+use alloc::string::String;
+use alloc::vec::Vec;
 use core::cmp;
 use core::cmp::Ordering;
 use core::default::Default;
@@ -8,28 +9,23 @@
 use core::hash;
 use core::mem;
 use core::str;
-use core::{u32, u64, u8};
 
 use num_integer::{Integer, Roots};
-use num_traits::{Num, One, Pow, ToPrimitive, Unsigned, Zero};
+use num_traits::{ConstZero, Num, One, Pow, ToPrimitive, Unsigned, Zero};
 
 mod addition;
 mod division;
 mod multiplication;
 mod subtraction;
 
+mod arbitrary;
 mod bits;
 mod convert;
 mod iter;
 mod monty;
 mod power;
-mod shift;
-
-#[cfg(any(feature = "quickcheck", feature = "arbitrary"))]
-mod arbitrary;
-
-#[cfg(feature = "serde")]
 mod serde;
+mod shift;
 
 pub(crate) use self::convert::to_str_radix_reversed;
 pub use self::iter::{U32Digits, U64Digits};
@@ -101,7 +97,7 @@
 impl Default for BigUint {
     #[inline]
     fn default() -> BigUint {
-        Zero::zero()
+        Self::ZERO
     }
 }
 
@@ -146,7 +142,7 @@
 impl Zero for BigUint {
     #[inline]
     fn zero() -> BigUint {
-        BigUint { data: Vec::new() }
+        Self::ZERO
     }
 
     #[inline]
@@ -160,6 +156,11 @@
     }
 }
 
+impl ConstZero for BigUint {
+    // forward to the inherent const
+    const ZERO: Self = Self::ZERO; // BigUint { data: Vec::new() };
+}
+
 impl One for BigUint {
     #[inline]
     fn one() -> BigUint {
@@ -255,7 +256,7 @@
     #[inline]
     fn lcm(&self, other: &BigUint) -> BigUint {
         if self.is_zero() && other.is_zero() {
-            Self::zero()
+            Self::ZERO
         } else {
             self / self.gcd(other) * other
         }
@@ -267,7 +268,7 @@
     fn gcd_lcm(&self, other: &Self) -> (Self, Self) {
         let gcd = self.gcd(other);
         let lcm = if gcd.is_zero() {
-            Self::zero()
+            Self::ZERO
         } else {
             self / &gcd * other
         };
@@ -320,6 +321,14 @@
     fn prev_multiple_of(&self, other: &Self) -> Self {
         self - self.mod_floor(other)
     }
+
+    fn dec(&mut self) {
+        *self -= 1u32;
+    }
+
+    fn inc(&mut self) {
+        *self += 1u32;
+    }
 }
 
 #[inline]
@@ -397,7 +406,7 @@
             _ => {
                 // Try to guess by scaling down such that it does fit in `f64`.
                 // With some (x * 2ⁿᵏ), its nth root ≈ (ⁿ√x * 2ᵏ)
-                let extra_bits = bits - (core::f64::MAX_EXP as u64 - 1);
+                let extra_bits = bits - (f64::MAX_EXP as u64 - 1);
                 let root_scale = Integer::div_ceil(&extra_bits, &n64);
                 let scale = root_scale * n64;
                 if scale < bits && bits - scale > n64 {
@@ -445,7 +454,7 @@
             _ => {
                 // Try to guess by scaling down such that it does fit in `f64`.
                 // With some (x * 2²ᵏ), its sqrt ≈ (√x * 2ᵏ)
-                let extra_bits = bits - (core::f64::MAX_EXP as u64 - 1);
+                let extra_bits = bits - (f64::MAX_EXP as u64 - 1);
                 let root_scale = (extra_bits + 1) / 2;
                 let scale = root_scale * 2;
                 (self >> scale).sqrt() << root_scale
@@ -486,7 +495,7 @@
             _ => {
                 // Try to guess by scaling down such that it does fit in `f64`.
                 // With some (x * 2³ᵏ), its cbrt ≈ (∛x * 2ᵏ)
-                let extra_bits = bits - (core::f64::MAX_EXP as u64 - 1);
+                let extra_bits = bits - (f64::MAX_EXP as u64 - 1);
                 let root_scale = (extra_bits + 2) / 3;
                 let scale = root_scale * 3;
                 (self >> scale).cbrt() << root_scale
@@ -519,21 +528,23 @@
 }
 
 impl BigUint {
+    /// A constant `BigUint` with value 0, useful for static initialization.
+    pub const ZERO: Self = BigUint { data: Vec::new() };
+
     /// Creates and initializes a [`BigUint`].
     ///
     /// The base 2<sup>32</sup> digits are ordered least significant digit first.
     #[inline]
     pub fn new(digits: Vec<u32>) -> BigUint {
-        let mut big = BigUint::zero();
+        let mut big = Self::ZERO;
 
-        #[cfg(not(u64_digit))]
-        {
-            big.data = digits;
-            big.normalize();
-        }
-
-        #[cfg(u64_digit)]
-        big.assign_from_slice(&digits);
+        cfg_digit_expr!(
+            {
+                big.data = digits;
+                big.normalize();
+            },
+            big.assign_from_slice(&digits)
+        );
 
         big
     }
@@ -543,7 +554,7 @@
     /// The base 2<sup>32</sup> digits are ordered least significant digit first.
     #[inline]
     pub fn from_slice(slice: &[u32]) -> BigUint {
-        let mut big = BigUint::zero();
+        let mut big = Self::ZERO;
         big.assign_from_slice(slice);
         big
     }
@@ -555,11 +566,10 @@
     pub fn assign_from_slice(&mut self, slice: &[u32]) {
         self.data.clear();
 
-        #[cfg(not(u64_digit))]
-        self.data.extend_from_slice(slice);
-
-        #[cfg(u64_digit)]
-        self.data.extend(slice.chunks(2).map(u32_chunk_to_u64));
+        cfg_digit_expr!(
+            self.data.extend_from_slice(slice),
+            self.data.extend(slice.chunks(2).map(u32_chunk_to_u64))
+        );
 
         self.normalize();
     }
@@ -585,7 +595,7 @@
     #[inline]
     pub fn from_bytes_be(bytes: &[u8]) -> BigUint {
         if bytes.is_empty() {
-            Zero::zero()
+            Self::ZERO
         } else {
             let mut v = bytes.to_vec();
             v.reverse();
@@ -599,7 +609,7 @@
     #[inline]
     pub fn from_bytes_le(bytes: &[u8]) -> BigUint {
         if bytes.is_empty() {
-            Zero::zero()
+            Self::ZERO
         } else {
             convert::from_bitwise_digits_le(bytes, 8)
         }
@@ -877,6 +887,86 @@
         power::modpow(self, exponent, modulus)
     }
 
+    /// Returns the modular multiplicative inverse if it exists, otherwise `None`.
+    ///
+    /// This solves for `x` in the interval `[0, modulus)` such that `self * x ≡ 1 (mod modulus)`.
+    /// The solution exists if and only if `gcd(self, modulus) == 1`.
+    ///
+    /// ```
+    /// use num_bigint::BigUint;
+    /// use num_traits::{One, Zero};
+    ///
+    /// let m = BigUint::from(383_u32);
+    ///
+    /// // Trivial cases
+    /// assert_eq!(BigUint::zero().modinv(&m), None);
+    /// assert_eq!(BigUint::one().modinv(&m), Some(BigUint::one()));
+    /// let neg1 = &m - 1u32;
+    /// assert_eq!(neg1.modinv(&m), Some(neg1));
+    ///
+    /// let a = BigUint::from(271_u32);
+    /// let x = a.modinv(&m).unwrap();
+    /// assert_eq!(x, BigUint::from(106_u32));
+    /// assert_eq!(x.modinv(&m).unwrap(), a);
+    /// assert!((a * x % m).is_one());
+    /// ```
+    pub fn modinv(&self, modulus: &Self) -> Option<Self> {
+        // Based on the inverse pseudocode listed here:
+        // https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers
+        // TODO: consider Binary or Lehmer's GCD algorithms for optimization.
+
+        assert!(
+            !modulus.is_zero(),
+            "attempt to calculate with zero modulus!"
+        );
+        if modulus.is_one() {
+            return Some(Self::zero());
+        }
+
+        let mut r0; // = modulus.clone();
+        let mut r1 = self % modulus;
+        let mut t0; // = Self::zero();
+        let mut t1; // = Self::one();
+
+        // Lift and simplify the first iteration to avoid some initial allocations.
+        if r1.is_zero() {
+            return None;
+        } else if r1.is_one() {
+            return Some(r1);
+        } else {
+            let (q, r2) = modulus.div_rem(&r1);
+            if r2.is_zero() {
+                return None;
+            }
+            r0 = r1;
+            r1 = r2;
+            t0 = Self::one();
+            t1 = modulus - q;
+        }
+
+        while !r1.is_zero() {
+            let (q, r2) = r0.div_rem(&r1);
+            r0 = r1;
+            r1 = r2;
+
+            // let t2 = (t0 - q * t1) % modulus;
+            let qt1 = q * &t1 % modulus;
+            let t2 = if t0 < qt1 {
+                t0 + (modulus - qt1)
+            } else {
+                t0 - qt1
+            };
+            t0 = t1;
+            t1 = t2;
+        }
+
+        if r0.is_one() {
+            Some(t0)
+        } else {
+            None
+        }
+    }
+
     /// Returns the truncated principal square root of `self` --
     /// see [Roots::sqrt](https://docs.rs/num-integer/0.1/num_integer/trait.Roots.html#method.sqrt)
     pub fn sqrt(&self) -> Self {
@@ -906,10 +996,7 @@
     /// Returns the number of least-significant bits that are ones.
     pub fn trailing_ones(&self) -> u64 {
         if let Some(i) = self.data.iter().position(|&digit| !digit != 0) {
-            // XXX u64::trailing_ones() introduced in Rust 1.46,
-            // but we need to be compatible further back.
-            // Thanks to cuviper for this workaround.
-            let ones: u64 = (!self.data[i]).trailing_zeros().into();
+            let ones: u64 = self.data[i].trailing_ones().into();
             i as u64 * u64::from(big_digit::BITS) + ones
         } else {
             self.data.len() as u64 * u64::from(big_digit::BITS)
@@ -941,9 +1028,7 @@
         // Note: we're saturating `digit_index` and `new_len` -- any such case is guaranteed to
         // fail allocation, and that's more consistent than adding our own overflow panics.
         let bits_per_digit = u64::from(big_digit::BITS);
-        let digit_index = (bit / bits_per_digit)
-            .to_usize()
-            .unwrap_or(core::usize::MAX);
+        let digit_index = (bit / bits_per_digit).to_usize().unwrap_or(usize::MAX);
         let bit_mask = (1 as BigDigit) << (bit % bits_per_digit);
         if value {
             if digit_index >= self.data.len() {
@@ -1025,86 +1110,77 @@
     digit
 }
 
-/// Combine four `u32`s into a single `u128`.
-#[cfg(any(test, not(u64_digit)))]
-#[inline]
-fn u32_to_u128(a: u32, b: u32, c: u32, d: u32) -> u128 {
-    u128::from(d) | (u128::from(c) << 32) | (u128::from(b) << 64) | (u128::from(a) << 96)
-}
-
-/// Split a single `u128` into four `u32`.
-#[cfg(any(test, not(u64_digit)))]
-#[inline]
-fn u32_from_u128(n: u128) -> (u32, u32, u32, u32) {
-    (
-        (n >> 96) as u32,
-        (n >> 64) as u32,
-        (n >> 32) as u32,
-        n as u32,
-    )
-}
-
-#[cfg(not(u64_digit))]
-#[test]
-fn test_from_slice() {
-    fn check(slice: &[u32], data: &[BigDigit]) {
-        assert_eq!(BigUint::from_slice(slice).data, data);
+cfg_32_or_test!(
+    /// Combine four `u32`s into a single `u128`.
+    #[inline]
+    fn u32_to_u128(a: u32, b: u32, c: u32, d: u32) -> u128 {
+        u128::from(d) | (u128::from(c) << 32) | (u128::from(b) << 64) | (u128::from(a) << 96)
     }
-    check(&[1], &[1]);
-    check(&[0, 0, 0], &[]);
-    check(&[1, 2, 0, 0], &[1, 2]);
-    check(&[0, 0, 1, 2], &[0, 0, 1, 2]);
-    check(&[0, 0, 1, 2, 0, 0], &[0, 0, 1, 2]);
-    check(&[-1i32 as u32], &[-1i32 as BigDigit]);
-}
+);
 
-#[cfg(u64_digit)]
-#[test]
-fn test_from_slice() {
-    fn check(slice: &[u32], data: &[BigDigit]) {
-        assert_eq!(
-            BigUint::from_slice(slice).data,
-            data,
-            "from {:?}, to {:?}",
-            slice,
-            data
-        );
+cfg_32_or_test!(
+    /// Split a single `u128` into four `u32`.
+    #[inline]
+    fn u32_from_u128(n: u128) -> (u32, u32, u32, u32) {
+        (
+            (n >> 96) as u32,
+            (n >> 64) as u32,
+            (n >> 32) as u32,
+            n as u32,
+        )
     }
-    check(&[1], &[1]);
-    check(&[0, 0, 0], &[]);
-    check(&[1, 2], &[8_589_934_593]);
-    check(&[1, 2, 0, 0], &[8_589_934_593]);
-    check(&[0, 0, 1, 2], &[0, 8_589_934_593]);
-    check(&[0, 0, 1, 2, 0, 0], &[0, 8_589_934_593]);
-    check(&[-1i32 as u32], &[(-1i32 as u32) as BigDigit]);
-}
+);
+
+cfg_digit!(
+    #[test]
+    fn test_from_slice() {
+        fn check(slice: &[u32], data: &[BigDigit]) {
+            assert_eq!(BigUint::from_slice(slice).data, data);
+        }
+        check(&[1], &[1]);
+        check(&[0, 0, 0], &[]);
+        check(&[1, 2, 0, 0], &[1, 2]);
+        check(&[0, 0, 1, 2], &[0, 0, 1, 2]);
+        check(&[0, 0, 1, 2, 0, 0], &[0, 0, 1, 2]);
+        check(&[-1i32 as u32], &[-1i32 as BigDigit]);
+    }
+
+    #[test]
+    fn test_from_slice() {
+        fn check(slice: &[u32], data: &[BigDigit]) {
+            assert_eq!(
+                BigUint::from_slice(slice).data,
+                data,
+                "from {:?}, to {:?}",
+                slice,
+                data
+            );
+        }
+        check(&[1], &[1]);
+        check(&[0, 0, 0], &[]);
+        check(&[1, 2], &[8_589_934_593]);
+        check(&[1, 2, 0, 0], &[8_589_934_593]);
+        check(&[0, 0, 1, 2], &[0, 8_589_934_593]);
+        check(&[0, 0, 1, 2, 0, 0], &[0, 8_589_934_593]);
+        check(&[-1i32 as u32], &[(-1i32 as u32) as BigDigit]);
+    }
+);
 
 #[test]
 fn test_u32_u128() {
     assert_eq!(u32_from_u128(0u128), (0, 0, 0, 0));
     assert_eq!(
-        u32_from_u128(u128::max_value()),
-        (
-            u32::max_value(),
-            u32::max_value(),
-            u32::max_value(),
-            u32::max_value()
-        )
+        u32_from_u128(u128::MAX),
+        (u32::MAX, u32::MAX, u32::MAX, u32::MAX)
     );
 
-    assert_eq!(
-        u32_from_u128(u32::max_value() as u128),
-        (0, 0, 0, u32::max_value())
-    );
+    assert_eq!(u32_from_u128(u32::MAX as u128), (0, 0, 0, u32::MAX));
+
+    assert_eq!(u32_from_u128(u64::MAX as u128), (0, 0, u32::MAX, u32::MAX));
 
     assert_eq!(
-        u32_from_u128(u64::max_value() as u128),
-        (0, 0, u32::max_value(), u32::max_value())
-    );
-
-    assert_eq!(
-        u32_from_u128((u64::max_value() as u128) + u32::max_value() as u128),
-        (0, 1, 0, u32::max_value() - 1)
+        u32_from_u128((u64::MAX as u128) + u32::MAX as u128),
+        (0, 1, 0, u32::MAX - 1)
     );
 
     assert_eq!(u32_from_u128(36_893_488_151_714_070_528), (0, 2, 1, 0));
@@ -1116,11 +1192,11 @@
     let values = vec![
         0u128,
         1u128,
-        u64::max_value() as u128 * 3,
-        u32::max_value() as u128,
-        u64::max_value() as u128,
-        (u64::max_value() as u128) + u32::max_value() as u128,
-        u128::max_value(),
+        u64::MAX as u128 * 3,
+        u32::MAX as u128,
+        u64::MAX as u128,
+        (u64::MAX as u128) + u32::MAX as u128,
+        u128::MAX,
     ];
 
     for val in &values {
diff --git a/crates/num-bigint/src/biguint/addition.rs b/crates/num-bigint/src/biguint/addition.rs
index ac6c0dd..b671131 100644
--- a/crates/num-bigint/src/biguint/addition.rs
+++ b/crates/num-bigint/src/biguint/addition.rs
@@ -1,5 +1,3 @@
-#[cfg(not(u64_digit))]
-use super::u32_from_u128;
 use super::{BigUint, IntDigits};
 
 use crate::big_digit::{self, BigDigit};
@@ -7,40 +5,44 @@
 
 use core::iter::Sum;
 use core::ops::{Add, AddAssign};
-use num_traits::{CheckedAdd, Zero};
+use num_traits::CheckedAdd;
 
-#[cfg(all(use_addcarry, target_arch = "x86_64"))]
+#[cfg(target_arch = "x86_64")]
 use core::arch::x86_64 as arch;
 
-#[cfg(all(use_addcarry, target_arch = "x86"))]
+#[cfg(target_arch = "x86")]
 use core::arch::x86 as arch;
 
 // Add with carry:
-#[cfg(all(use_addcarry, u64_digit))]
-#[inline]
-fn adc(carry: u8, a: u64, b: u64, out: &mut u64) -> u8 {
-    // Safety: There are absolutely no safety concerns with calling `_addcarry_u64`.
-    // It's just unsafe for API consistency with other intrinsics.
-    unsafe { arch::_addcarry_u64(carry, a, b, out) }
-}
+#[cfg(target_arch = "x86_64")]
+cfg_64!(
+    #[inline]
+    fn adc(carry: u8, a: u64, b: u64, out: &mut u64) -> u8 {
+        // Safety: There are absolutely no safety concerns with calling `_addcarry_u64`.
+        // It's just unsafe for API consistency with other intrinsics.
+        unsafe { arch::_addcarry_u64(carry, a, b, out) }
+    }
+);
 
-#[cfg(all(use_addcarry, not(u64_digit)))]
-#[inline]
-fn adc(carry: u8, a: u32, b: u32, out: &mut u32) -> u8 {
-    // Safety: There are absolutely no safety concerns with calling `_addcarry_u32`.
-    // It's just unsafe for API consistency with other intrinsics.
-    unsafe { arch::_addcarry_u32(carry, a, b, out) }
-}
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+cfg_32!(
+    #[inline]
+    fn adc(carry: u8, a: u32, b: u32, out: &mut u32) -> u8 {
+        // Safety: There are absolutely no safety concerns with calling `_addcarry_u32`.
+        // It's just unsafe for API consistency with other intrinsics.
+        unsafe { arch::_addcarry_u32(carry, a, b, out) }
+    }
+);
 
 // fallback for environments where we don't have an addcarry intrinsic
-#[cfg(not(use_addcarry))]
+// (copied from the standard library's `carrying_add`)
+#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
 #[inline]
-fn adc(carry: u8, a: BigDigit, b: BigDigit, out: &mut BigDigit) -> u8 {
-    use crate::big_digit::DoubleBigDigit;
-
-    let sum = DoubleBigDigit::from(a) + DoubleBigDigit::from(b) + DoubleBigDigit::from(carry);
-    *out = sum as BigDigit;
-    (sum >> big_digit::BITS) as u8
+fn adc(carry: u8, lhs: BigDigit, rhs: BigDigit, out: &mut BigDigit) -> u8 {
+    let (a, b) = lhs.overflowing_add(rhs);
+    let (c, d) = a.overflowing_add(carry as BigDigit);
+    *out = c;
+    u8::from(b || d)
 }
 
 /// Two argument addition of raw slices, `a += b`, returning the carry.
@@ -154,38 +156,38 @@
 }
 
 impl AddAssign<u64> for BigUint {
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn add_assign(&mut self, other: u64) {
-        let (hi, lo) = big_digit::from_doublebigdigit(other);
-        if hi == 0 {
-            *self += lo;
-        } else {
-            while self.data.len() < 2 {
-                self.data.push(0);
-            }
+    cfg_digit!(
+        #[inline]
+        fn add_assign(&mut self, other: u64) {
+            let (hi, lo) = big_digit::from_doublebigdigit(other);
+            if hi == 0 {
+                *self += lo;
+            } else {
+                while self.data.len() < 2 {
+                    self.data.push(0);
+                }
 
-            let carry = __add2(&mut self.data, &[lo, hi]);
-            if carry != 0 {
-                self.data.push(carry);
+                let carry = __add2(&mut self.data, &[lo, hi]);
+                if carry != 0 {
+                    self.data.push(carry);
+                }
             }
         }
-    }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn add_assign(&mut self, other: u64) {
-        if other != 0 {
-            if self.data.is_empty() {
-                self.data.push(0);
-            }
+        #[inline]
+        fn add_assign(&mut self, other: u64) {
+            if other != 0 {
+                if self.data.is_empty() {
+                    self.data.push(0);
+                }
 
-            let carry = __add2(&mut self.data, &[other as BigDigit]);
-            if carry != 0 {
-                self.data.push(carry);
+                let carry = __add2(&mut self.data, &[other as BigDigit]);
+                if carry != 0 {
+                    self.data.push(carry);
+                }
             }
         }
-    }
+    );
 }
 
 impl Add<u128> for BigUint {
@@ -199,49 +201,49 @@
 }
 
 impl AddAssign<u128> for BigUint {
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn add_assign(&mut self, other: u128) {
-        if other <= u128::from(u64::max_value()) {
-            *self += other as u64
-        } else {
-            let (a, b, c, d) = u32_from_u128(other);
-            let carry = if a > 0 {
-                while self.data.len() < 4 {
-                    self.data.push(0);
-                }
-                __add2(&mut self.data, &[d, c, b, a])
+    cfg_digit!(
+        #[inline]
+        fn add_assign(&mut self, other: u128) {
+            if other <= u128::from(u64::MAX) {
+                *self += other as u64
             } else {
-                debug_assert!(b > 0);
-                while self.data.len() < 3 {
+                let (a, b, c, d) = super::u32_from_u128(other);
+                let carry = if a > 0 {
+                    while self.data.len() < 4 {
+                        self.data.push(0);
+                    }
+                    __add2(&mut self.data, &[d, c, b, a])
+                } else {
+                    debug_assert!(b > 0);
+                    while self.data.len() < 3 {
+                        self.data.push(0);
+                    }
+                    __add2(&mut self.data, &[d, c, b])
+                };
+
+                if carry != 0 {
+                    self.data.push(carry);
+                }
+            }
+        }
+
+        #[inline]
+        fn add_assign(&mut self, other: u128) {
+            let (hi, lo) = big_digit::from_doublebigdigit(other);
+            if hi == 0 {
+                *self += lo;
+            } else {
+                while self.data.len() < 2 {
                     self.data.push(0);
                 }
-                __add2(&mut self.data, &[d, c, b])
-            };
 
-            if carry != 0 {
-                self.data.push(carry);
+                let carry = __add2(&mut self.data, &[lo, hi]);
+                if carry != 0 {
+                    self.data.push(carry);
+                }
             }
         }
-    }
-
-    #[cfg(u64_digit)]
-    #[inline]
-    fn add_assign(&mut self, other: u128) {
-        let (hi, lo) = big_digit::from_doublebigdigit(other);
-        if hi == 0 {
-            *self += lo;
-        } else {
-            while self.data.len() < 2 {
-                self.data.push(0);
-            }
-
-            let carry = __add2(&mut self.data, &[lo, hi]);
-            if carry != 0 {
-                self.data.push(carry);
-            }
-        }
-    }
+    );
 }
 
 impl CheckedAdd for BigUint {
diff --git a/crates/num-bigint/src/biguint/arbitrary.rs b/crates/num-bigint/src/biguint/arbitrary.rs
index 6fa91c0..9d5c5cc 100644
--- a/crates/num-bigint/src/biguint/arbitrary.rs
+++ b/crates/num-bigint/src/biguint/arbitrary.rs
@@ -1,11 +1,14 @@
+#![cfg(any(feature = "quickcheck", feature = "arbitrary"))]
+
 use super::{biguint_from_vec, BigUint};
 
 use crate::big_digit::BigDigit;
 #[cfg(feature = "quickcheck")]
-use crate::std_alloc::Box;
-use crate::std_alloc::Vec;
+use alloc::boxed::Box;
+use alloc::vec::Vec;
 
 #[cfg(feature = "quickcheck")]
+#[cfg_attr(docsrs, doc(cfg(feature = "quickcheck")))]
 impl quickcheck::Arbitrary for BigUint {
     fn arbitrary(g: &mut quickcheck::Gen) -> Self {
         // Use arbitrary from Vec
@@ -19,6 +22,7 @@
 }
 
 #[cfg(feature = "arbitrary")]
+#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
 impl arbitrary::Arbitrary<'_> for BigUint {
     fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
         Ok(biguint_from_vec(Vec::<BigDigit>::arbitrary(u)?))
diff --git a/crates/num-bigint/src/biguint/convert.rs b/crates/num-bigint/src/biguint/convert.rs
index f19bc75..3daf3dc 100644
--- a/crates/num-bigint/src/biguint/convert.rs
+++ b/crates/num-bigint/src/biguint/convert.rs
@@ -4,17 +4,15 @@
 use super::{biguint_from_vec, BigUint, ToBigUint};
 
 use super::addition::add2;
-use super::division::div_rem_digit;
+use super::division::{div_rem_digit, FAST_DIV_WIDE};
 use super::multiplication::mac_with_carry;
 
 use crate::big_digit::{self, BigDigit};
-use crate::std_alloc::Vec;
 use crate::ParseBigIntError;
-#[cfg(has_try_from)]
 use crate::TryFromBigIntError;
 
+use alloc::vec::Vec;
 use core::cmp::Ordering::{Equal, Greater, Less};
-#[cfg(has_try_from)]
 use core::convert::TryFrom;
 use core::mem;
 use core::str::FromStr;
@@ -71,7 +69,7 @@
     let total_bits = (v.len() as u64).saturating_mul(bits.into());
     let big_digits = Integer::div_ceil(&total_bits, &big_digit::BITS.into())
         .to_usize()
-        .unwrap_or(core::usize::MAX);
+        .unwrap_or(usize::MAX);
     let mut data = Vec::with_capacity(big_digits);
 
     let mut d = 0;
@@ -121,7 +119,7 @@
 
     let mut data = Vec::with_capacity(big_digits.to_usize().unwrap_or(0));
 
-    let (base, power) = get_radix_base(radix, big_digit::BITS);
+    let (base, power) = get_radix_base(radix);
     let radix = radix as BigDigit;
 
     let r = v.len() % power;
@@ -161,7 +159,7 @@
     );
 
     if buf.is_empty() {
-        return Some(Zero::zero());
+        return Some(BigUint::ZERO);
     }
 
     if radix != 256 && buf.iter().any(|&b| b >= radix as u8) {
@@ -192,7 +190,7 @@
     );
 
     if buf.is_empty() {
-        return Some(Zero::zero());
+        return Some(BigUint::ZERO);
     }
 
     if radix != 256 && buf.iter().any(|&b| b >= radix as u8) {
@@ -223,8 +221,7 @@
     fn from_str_radix(s: &str, radix: u32) -> Result<BigUint, ParseBigIntError> {
         assert!(2 <= radix && radix <= 36, "The radix must be within 2...36");
         let mut s = s;
-        if s.starts_with('+') {
-            let tail = &s[1..];
+        if let Some(tail) = s.strip_prefix('+') {
             if !tail.starts_with('+') {
                 s = tail
             }
@@ -247,7 +244,7 @@
                 b'a'..=b'z' => b - b'a' + 10,
                 b'A'..=b'Z' => b - b'A' + 10,
                 b'_' => continue,
-                _ => core::u8::MAX,
+                _ => u8::MAX,
             };
             if d < radix as u8 {
                 v.push(d);
@@ -374,8 +371,8 @@
         let mantissa = high_bits_to_u64(self);
         let exponent = self.bits() - u64::from(fls(mantissa));
 
-        if exponent > core::f32::MAX_EXP as u64 {
-            Some(core::f32::INFINITY)
+        if exponent > f32::MAX_EXP as u64 {
+            Some(f32::INFINITY)
         } else {
             Some((mantissa as f32) * 2.0f32.powi(exponent as i32))
         }
@@ -386,8 +383,8 @@
         let mantissa = high_bits_to_u64(self);
         let exponent = self.bits() - u64::from(fls(mantissa));
 
-        if exponent > core::f64::MAX_EXP as u64 {
-            Some(core::f64::INFINITY)
+        if exponent > f64::MAX_EXP as u64 {
+            Some(f64::INFINITY)
         } else {
             Some((mantissa as f64) * 2.0f64.powi(exponent as i32))
         }
@@ -396,7 +393,6 @@
 
 macro_rules! impl_try_from_biguint {
     ($T:ty, $to_ty:path) => {
-        #[cfg(has_try_from)]
         impl TryFrom<&BigUint> for $T {
             type Error = TryFromBigIntError<()>;
 
@@ -406,7 +402,6 @@
             }
         }
 
-        #[cfg(has_try_from)]
         impl TryFrom<BigUint> for $T {
             type Error = TryFromBigIntError<BigUint>;
 
@@ -473,7 +468,7 @@
 
         // handle 0.x, -0.x
         if n.is_zero() {
-            return Some(BigUint::zero());
+            return Some(Self::ZERO);
         }
 
         let (mantissa, exponent, sign) = FloatCore::integer_decode(n);
@@ -495,7 +490,7 @@
 impl From<u64> for BigUint {
     #[inline]
     fn from(mut n: u64) -> Self {
-        let mut ret: BigUint = Zero::zero();
+        let mut ret: BigUint = Self::ZERO;
 
         while n != 0 {
             ret.data.push(n as BigDigit);
@@ -510,7 +505,7 @@
 impl From<u128> for BigUint {
     #[inline]
     fn from(mut n: u128) -> Self {
-        let mut ret: BigUint = Zero::zero();
+        let mut ret: BigUint = Self::ZERO;
 
         while n != 0 {
             ret.data.push(n as BigDigit);
@@ -539,7 +534,6 @@
 
 macro_rules! impl_biguint_try_from_int {
     ($T:ty, $from_ty:path) => {
-        #[cfg(has_try_from)]
         impl TryFrom<$T> for BigUint {
             type Error = TryFromBigIntError<()>;
 
@@ -598,7 +592,7 @@
         if x {
             One::one()
         } else {
-            Zero::zero()
+            Self::ZERO
         }
     }
 }
@@ -612,7 +606,7 @@
     let digits_per_big_digit = big_digit::BITS / bits;
     let digits = Integer::div_ceil(&u.bits(), &u64::from(bits))
         .to_usize()
-        .unwrap_or(core::usize::MAX);
+        .unwrap_or(usize::MAX);
     let mut res = Vec::with_capacity(digits);
 
     for mut r in u.data[..last_i].iter().cloned() {
@@ -638,7 +632,7 @@
     let mask: BigDigit = (1 << bits) - 1;
     let digits = Integer::div_ceil(&u.bits(), &u64::from(bits))
         .to_usize()
-        .unwrap_or(core::usize::MAX);
+        .unwrap_or(usize::MAX);
     let mut res = Vec::with_capacity(digits);
 
     let mut r = 0;
@@ -693,7 +687,13 @@
 
     let mut digits = u.clone();
 
-    let (base, power) = get_radix_base(radix, big_digit::HALF_BITS);
+    // X86 DIV can quickly divide by a full digit, otherwise we choose a divisor
+    // that's suitable for `div_half` to avoid slow `DoubleBigDigit` division.
+    let (base, power) = if FAST_DIV_WIDE {
+        get_radix_base(radix)
+    } else {
+        get_half_radix_base(radix)
+    };
     let radix = radix as BigDigit;
 
     // For very large numbers, the O(n²) loop of repeated `div_rem_digit` dominates the
@@ -701,8 +701,8 @@
     // The threshold for this was chosen by anecdotal performance measurements to
     // approximate where this starts to make a noticeable difference.
     if digits.data.len() >= 64 {
-        let mut big_base = BigUint::from(base * base);
-        let mut big_power = 2usize;
+        let mut big_base = BigUint::from(base);
+        let mut big_power = 1usize;
 
         // Choose a target base length near √n.
         let target_len = digits.data.len().sqrt();
@@ -788,33 +788,79 @@
     res
 }
 
-/// Returns the greatest power of the radix for the given bit size
+/// Returns the greatest power of the radix for the `BigDigit` bit size
 #[inline]
-fn get_radix_base(radix: u32, bits: u8) -> (BigDigit, usize) {
-    mod gen {
-        include! { concat!(env!("OUT_DIR"), "/radix_bases.rs") }
+fn get_radix_base(radix: u32) -> (BigDigit, usize) {
+    static BASES: [(BigDigit, usize); 257] = generate_radix_bases(big_digit::MAX);
+    debug_assert!(!radix.is_power_of_two());
+    debug_assert!((3..256).contains(&radix));
+    BASES[radix as usize]
+}
+
+/// Returns the greatest power of the radix for half the `BigDigit` bit size
+#[inline]
+fn get_half_radix_base(radix: u32) -> (BigDigit, usize) {
+    static BASES: [(BigDigit, usize); 257] = generate_radix_bases(big_digit::HALF);
+    debug_assert!(!radix.is_power_of_two());
+    debug_assert!((3..256).contains(&radix));
+    BASES[radix as usize]
+}
+
+/// Generate tables of the greatest power of each radix that is less that the given maximum. These
+/// are returned from `get_radix_base` to batch the multiplication/division of radix conversions on
+/// full `BigUint` values, operating on primitive integers as much as possible.
+///
+/// e.g. BASES_16[3] = (59049, 10) // 3¹⁰ fits in u16, but 3¹¹ is too big
+///      BASES_32[3] = (3486784401, 20)
+///      BASES_64[3] = (12157665459056928801, 40)
+///
+/// Powers of two are not included, just zeroed, as they're implemented with shifts.
+const fn generate_radix_bases(max: BigDigit) -> [(BigDigit, usize); 257] {
+    let mut bases = [(0, 0); 257];
+
+    let mut radix: BigDigit = 3;
+    while radix < 256 {
+        if !radix.is_power_of_two() {
+            let mut power = 1;
+            let mut base = radix;
+
+            while let Some(b) = base.checked_mul(radix) {
+                if b > max {
+                    break;
+                }
+                base = b;
+                power += 1;
+            }
+            bases[radix as usize] = (base, power)
+        }
+        radix += 1;
     }
 
-    debug_assert!(
-        2 <= radix && radix <= 256,
-        "The radix must be within 2...256"
-    );
-    debug_assert!(!radix.is_power_of_two());
-    debug_assert!(bits <= big_digit::BITS);
+    bases
+}
 
-    match bits {
-        16 => {
-            let (base, power) = gen::BASES_16[radix as usize];
-            (base as BigDigit, power)
+#[test]
+fn test_radix_bases() {
+    for radix in 3u32..256 {
+        if !radix.is_power_of_two() {
+            let (base, power) = get_radix_base(radix);
+            let radix = BigDigit::from(radix);
+            let power = u32::try_from(power).unwrap();
+            assert_eq!(base, radix.pow(power));
+            assert!(radix.checked_pow(power + 1).is_none());
         }
-        32 => {
-            let (base, power) = gen::BASES_32[radix as usize];
-            (base as BigDigit, power)
+    }
+}
+
+#[test]
+fn test_half_radix_bases() {
+    for radix in 3u32..256 {
+        if !radix.is_power_of_two() {
+            let (base, power) = get_half_radix_base(radix);
+            let radix = BigDigit::from(radix);
+            let power = u32::try_from(power).unwrap();
+            assert_eq!(base, radix.pow(power));
+            assert!(radix.pow(power + 1) > big_digit::HALF);
         }
-        64 => {
-            let (base, power) = gen::BASES_64[radix as usize];
-            (base as BigDigit, power)
-        }
-        _ => panic!("Invalid bigdigit size"),
     }
 }
diff --git a/crates/num-bigint/src/biguint/division.rs b/crates/num-bigint/src/biguint/division.rs
index 2999838..3dfb0bb 100644
--- a/crates/num-bigint/src/biguint/division.rs
+++ b/crates/num-bigint/src/biguint/division.rs
@@ -1,6 +1,4 @@
 use super::addition::__add2;
-#[cfg(not(u64_digit))]
-use super::u32_to_u128;
 use super::{cmp_slice, BigUint};
 
 use crate::big_digit::{self, BigDigit, DoubleBigDigit};
@@ -12,12 +10,15 @@
 use num_integer::Integer;
 use num_traits::{CheckedDiv, CheckedEuclid, Euclid, One, ToPrimitive, Zero};
 
+pub(super) const FAST_DIV_WIDE: bool = cfg!(any(target_arch = "x86", target_arch = "x86_64"));
+
 /// Divide a two digit numerator by a one digit divisor, returns quotient and remainder:
 ///
 /// Note: the caller must ensure that both the quotient and remainder will fit into a single digit.
 /// This is _not_ true for an arbitrary numerator/denominator.
 ///
 /// (This function also matches what the x86 divide instruction does).
+#[cfg(any(miri, not(any(target_arch = "x86", target_arch = "x86_64"))))]
 #[inline]
 fn div_wide(hi: BigDigit, lo: BigDigit, divisor: BigDigit) -> (BigDigit, BigDigit) {
     debug_assert!(hi < divisor);
@@ -27,6 +28,47 @@
     ((lhs / rhs) as BigDigit, (lhs % rhs) as BigDigit)
 }
 
+/// x86 and x86_64 can use a real `div` instruction.
+#[cfg(all(not(miri), any(target_arch = "x86", target_arch = "x86_64")))]
+#[inline]
+fn div_wide(hi: BigDigit, lo: BigDigit, divisor: BigDigit) -> (BigDigit, BigDigit) {
+    // This debug assertion covers the potential #DE for divisor==0 or a quotient too large for one
+    // register, otherwise in release mode it will become a target-specific fault like SIGFPE.
+    // This should never occur with the inputs from our few `div_wide` callers.
+    debug_assert!(hi < divisor);
+
+    // SAFETY: The `div` instruction only affects registers, reading the explicit operand as the
+    // divisor, and implicitly reading RDX:RAX or EDX:EAX as the dividend. The result is implicitly
+    // written back to RAX or EAX for the quotient and RDX or EDX for the remainder. No memory is
+    // used, and flags are not preserved.
+    unsafe {
+        let (div, rem);
+
+        cfg_digit!(
+            macro_rules! div {
+                () => {
+                    "div {0:e}"
+                };
+            }
+            macro_rules! div {
+                () => {
+                    "div {0:r}"
+                };
+            }
+        );
+
+        core::arch::asm!(
+            div!(),
+            in(reg) divisor,
+            inout("dx") hi => rem,
+            inout("ax") lo => div,
+            options(pure, nomem, nostack),
+        );
+
+        (div, rem)
+    }
+}
+
 /// For small divisors, we can divide without promoting to `DoubleBigDigit` by
 /// using half-size pieces of digit, like long-division.
 #[inline]
@@ -47,7 +89,7 @@
 
     let mut rem = 0;
 
-    if b <= big_digit::HALF {
+    if !FAST_DIV_WIDE && b <= big_digit::HALF {
         for d in a.data.iter_mut().rev() {
             let (q, r) = div_half(rem, *d, b);
             *d = q;
@@ -72,7 +114,7 @@
 
     let mut rem = 0;
 
-    if b <= big_digit::HALF {
+    if !FAST_DIV_WIDE && b <= big_digit::HALF {
         for &digit in a.data.iter().rev() {
             let (_, r) = div_half(rem, digit, b);
             rem = r;
@@ -121,12 +163,12 @@
         panic!("attempt to divide by zero")
     }
     if u.is_zero() {
-        return (Zero::zero(), Zero::zero());
+        return (BigUint::ZERO, BigUint::ZERO);
     }
 
     if d.data.len() == 1 {
         if d.data == [1] {
-            return (u, Zero::zero());
+            return (u, BigUint::ZERO);
         }
         let (div, rem) = div_rem_digit(u, d.data[0]);
         // reuse d
@@ -137,10 +179,10 @@
 
     // Required or the q_len calculation below can underflow:
     match u.cmp(&d) {
-        Less => return (Zero::zero(), u),
+        Less => return (BigUint::ZERO, u),
         Equal => {
             u.set_one();
-            return (u, Zero::zero());
+            return (u, BigUint::ZERO);
         }
         Greater => {} // Do nothing
     }
@@ -168,12 +210,12 @@
         panic!("attempt to divide by zero")
     }
     if u.is_zero() {
-        return (Zero::zero(), Zero::zero());
+        return (BigUint::ZERO, BigUint::ZERO);
     }
 
     if d.data.len() == 1 {
         if d.data == [1] {
-            return (u.clone(), Zero::zero());
+            return (u.clone(), BigUint::ZERO);
         }
 
         let (div, rem) = div_rem_digit(u.clone(), d.data[0]);
@@ -182,8 +224,8 @@
 
     // Required or the q_len calculation below can underflow:
     match u.cmp(d) {
-        Less => return (Zero::zero(), u.clone()),
-        Equal => return (One::one(), Zero::zero()),
+        Less => return (BigUint::ZERO, u.clone()),
+        Equal => return (One::one(), BigUint::ZERO),
         Greater => {} // Do nothing
     }
 
@@ -232,7 +274,7 @@
     let mut a0 = 0;
 
     // [b1, b0] are the two most significant digits of the divisor. They never change.
-    let b0 = *b.last().unwrap();
+    let b0 = b[b.len() - 1];
     let b1 = b[b.len() - 2];
 
     let q_len = a.data.len() - b.len() + 1;
@@ -360,7 +402,7 @@
         match other.data.len() {
             0 => panic!("attempt to divide by zero"),
             1 => From::from(self as BigDigit / other.data[0]),
-            _ => Zero::zero(),
+            _ => BigUint::ZERO,
         }
     }
 }
@@ -378,7 +420,7 @@
     #[inline]
     fn div_assign(&mut self, other: u64) {
         // a vec of size 0 does not allocate, so this is fairly cheap
-        let temp = mem::replace(self, Zero::zero());
+        let temp = mem::replace(self, Self::ZERO);
         *self = temp / other;
     }
 }
@@ -386,26 +428,26 @@
 impl Div<BigUint> for u64 {
     type Output = BigUint;
 
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn div(self, other: BigUint) -> BigUint {
-        match other.data.len() {
-            0 => panic!("attempt to divide by zero"),
-            1 => From::from(self / u64::from(other.data[0])),
-            2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])),
-            _ => Zero::zero(),
+    cfg_digit!(
+        #[inline]
+        fn div(self, other: BigUint) -> BigUint {
+            match other.data.len() {
+                0 => panic!("attempt to divide by zero"),
+                1 => From::from(self / u64::from(other.data[0])),
+                2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])),
+                _ => BigUint::ZERO,
+            }
         }
-    }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn div(self, other: BigUint) -> BigUint {
-        match other.data.len() {
-            0 => panic!("attempt to divide by zero"),
-            1 => From::from(self / other.data[0]),
-            _ => Zero::zero(),
+        #[inline]
+        fn div(self, other: BigUint) -> BigUint {
+            match other.data.len() {
+                0 => panic!("attempt to divide by zero"),
+                1 => From::from(self / other.data[0]),
+                _ => BigUint::ZERO,
+            }
         }
-    }
+    );
 }
 
 impl Div<u128> for BigUint {
@@ -428,33 +470,34 @@
 impl Div<BigUint> for u128 {
     type Output = BigUint;
 
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn div(self, other: BigUint) -> BigUint {
-        match other.data.len() {
-            0 => panic!("attempt to divide by zero"),
-            1 => From::from(self / u128::from(other.data[0])),
-            2 => From::from(
-                self / u128::from(big_digit::to_doublebigdigit(other.data[1], other.data[0])),
-            ),
-            3 => From::from(self / u32_to_u128(0, other.data[2], other.data[1], other.data[0])),
-            4 => From::from(
-                self / u32_to_u128(other.data[3], other.data[2], other.data[1], other.data[0]),
-            ),
-            _ => Zero::zero(),
+    cfg_digit!(
+        #[inline]
+        fn div(self, other: BigUint) -> BigUint {
+            use super::u32_to_u128;
+            match other.data.len() {
+                0 => panic!("attempt to divide by zero"),
+                1 => From::from(self / u128::from(other.data[0])),
+                2 => From::from(
+                    self / u128::from(big_digit::to_doublebigdigit(other.data[1], other.data[0])),
+                ),
+                3 => From::from(self / u32_to_u128(0, other.data[2], other.data[1], other.data[0])),
+                4 => From::from(
+                    self / u32_to_u128(other.data[3], other.data[2], other.data[1], other.data[0]),
+                ),
+                _ => BigUint::ZERO,
+            }
         }
-    }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn div(self, other: BigUint) -> BigUint {
-        match other.data.len() {
-            0 => panic!("attempt to divide by zero"),
-            1 => From::from(self / other.data[0] as u128),
-            2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])),
-            _ => Zero::zero(),
+        #[inline]
+        fn div(self, other: BigUint) -> BigUint {
+            match other.data.len() {
+                0 => panic!("attempt to divide by zero"),
+                1 => From::from(self / other.data[0] as u128),
+                2 => From::from(self / big_digit::to_doublebigdigit(other.data[1], other.data[0])),
+                _ => BigUint::ZERO,
+            }
         }
-    }
+    );
 }
 
 forward_val_ref_binop!(impl Rem for BigUint, rem);
@@ -635,6 +678,10 @@
         }
         Some(self.rem_euclid(v))
     }
+
+    fn checked_div_rem_euclid(&self, v: &Self) -> Option<(Self, Self)> {
+        Some(self.div_rem_euclid(v))
+    }
 }
 
 impl Euclid for BigUint {
@@ -649,4 +696,9 @@
         // trivially same as regular remainder
         self % v
     }
+
+    fn div_rem_euclid(&self, v: &Self) -> (Self, Self) {
+        // trivially same as regular division and remainder
+        self.div_rem(v)
+    }
 }
diff --git a/crates/num-bigint/src/biguint/iter.rs b/crates/num-bigint/src/biguint/iter.rs
index 1e673e4..066c9c1 100644
--- a/crates/num-bigint/src/biguint/iter.rs
+++ b/crates/num-bigint/src/biguint/iter.rs
@@ -1,268 +1,271 @@
 use core::iter::FusedIterator;
 
-#[cfg(not(u64_digit))]
-use super::u32_chunk_to_u64;
-
-/// An iterator of `u32` digits representation of a `BigUint` or `BigInt`,
-/// ordered least significant digit first.
-pub struct U32Digits<'a> {
-    #[cfg(u64_digit)]
-    data: &'a [u64],
-    #[cfg(u64_digit)]
-    next_is_lo: bool,
-    #[cfg(u64_digit)]
-    last_hi_is_zero: bool,
-
-    #[cfg(not(u64_digit))]
-    it: core::slice::Iter<'a, u32>,
-}
-
-#[cfg(u64_digit)]
-impl<'a> U32Digits<'a> {
-    #[inline]
-    pub(super) fn new(data: &'a [u64]) -> Self {
-        let last_hi_is_zero = data
-            .last()
-            .map(|&last| {
-                let last_hi = (last >> 32) as u32;
-                last_hi == 0
-            })
-            .unwrap_or(false);
-        U32Digits {
-            data,
-            next_is_lo: true,
-            last_hi_is_zero,
-        }
+cfg_digit!(
+    /// An iterator of `u32` digits representation of a `BigUint` or `BigInt`,
+    /// ordered least significant digit first.
+    pub struct U32Digits<'a> {
+        it: core::slice::Iter<'a, u32>,
     }
-}
 
-#[cfg(u64_digit)]
-impl Iterator for U32Digits<'_> {
-    type Item = u32;
-    #[inline]
-    fn next(&mut self) -> Option<u32> {
-        match self.data.split_first() {
-            Some((&first, data)) => {
-                let next_is_lo = self.next_is_lo;
-                self.next_is_lo = !next_is_lo;
-                if next_is_lo {
-                    Some(first as u32)
-                } else {
-                    self.data = data;
-                    if data.is_empty() && self.last_hi_is_zero {
-                        self.last_hi_is_zero = false;
-                        None
-                    } else {
-                        Some((first >> 32) as u32)
-                    }
+    /// An iterator of `u32` digits representation of a `BigUint` or `BigInt`,
+    /// ordered least significant digit first.
+    pub struct U32Digits<'a> {
+        data: &'a [u64],
+        next_is_lo: bool,
+        last_hi_is_zero: bool,
+    }
+);
+
+cfg_digit!(
+    const _: () = {
+        impl<'a> U32Digits<'a> {
+            #[inline]
+            pub(super) fn new(data: &'a [u32]) -> Self {
+                Self { it: data.iter() }
+            }
+        }
+
+        impl Iterator for U32Digits<'_> {
+            type Item = u32;
+            #[inline]
+            fn next(&mut self) -> Option<u32> {
+                self.it.next().cloned()
+            }
+
+            #[inline]
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                self.it.size_hint()
+            }
+
+            #[inline]
+            fn nth(&mut self, n: usize) -> Option<u32> {
+                self.it.nth(n).cloned()
+            }
+
+            #[inline]
+            fn last(self) -> Option<u32> {
+                self.it.last().cloned()
+            }
+
+            #[inline]
+            fn count(self) -> usize {
+                self.it.count()
+            }
+        }
+
+        impl DoubleEndedIterator for U32Digits<'_> {
+            fn next_back(&mut self) -> Option<Self::Item> {
+                self.it.next_back().cloned()
+            }
+        }
+
+        impl ExactSizeIterator for U32Digits<'_> {
+            #[inline]
+            fn len(&self) -> usize {
+                self.it.len()
+            }
+        }
+    };
+
+    const _: () = {
+        impl<'a> U32Digits<'a> {
+            #[inline]
+            pub(super) fn new(data: &'a [u64]) -> Self {
+                let last_hi_is_zero = data
+                    .last()
+                    .map(|&last| {
+                        let last_hi = (last >> 32) as u32;
+                        last_hi == 0
+                    })
+                    .unwrap_or(false);
+                U32Digits {
+                    data,
+                    next_is_lo: true,
+                    last_hi_is_zero,
                 }
             }
-            None => None,
         }
-    }
 
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = self.len();
-        (len, Some(len))
-    }
-
-    #[inline]
-    fn last(self) -> Option<u32> {
-        self.data.last().map(|&last| {
-            if self.last_hi_is_zero {
-                last as u32
-            } else {
-                (last >> 32) as u32
-            }
-        })
-    }
-
-    #[inline]
-    fn count(self) -> usize {
-        self.len()
-    }
-}
-
-#[cfg(u64_digit)]
-impl DoubleEndedIterator for U32Digits<'_> {
-    fn next_back(&mut self) -> Option<Self::Item> {
-        match self.data.split_last() {
-            Some((&last, data)) => {
-                let last_is_lo = self.last_hi_is_zero;
-                self.last_hi_is_zero = !last_is_lo;
-                if last_is_lo {
-                    self.data = data;
-                    if data.is_empty() && !self.next_is_lo {
-                        self.next_is_lo = true;
-                        None
-                    } else {
-                        Some(last as u32)
+        impl Iterator for U32Digits<'_> {
+            type Item = u32;
+            #[inline]
+            fn next(&mut self) -> Option<u32> {
+                match self.data.split_first() {
+                    Some((&first, data)) => {
+                        let next_is_lo = self.next_is_lo;
+                        self.next_is_lo = !next_is_lo;
+                        if next_is_lo {
+                            Some(first as u32)
+                        } else {
+                            self.data = data;
+                            if data.is_empty() && self.last_hi_is_zero {
+                                self.last_hi_is_zero = false;
+                                None
+                            } else {
+                                Some((first >> 32) as u32)
+                            }
+                        }
                     }
-                } else {
-                    Some((last >> 32) as u32)
+                    None => None,
                 }
             }
-            None => None,
+
+            #[inline]
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                let len = self.len();
+                (len, Some(len))
+            }
+
+            #[inline]
+            fn last(self) -> Option<u32> {
+                self.data.last().map(|&last| {
+                    if self.last_hi_is_zero {
+                        last as u32
+                    } else {
+                        (last >> 32) as u32
+                    }
+                })
+            }
+
+            #[inline]
+            fn count(self) -> usize {
+                self.len()
+            }
         }
-    }
-}
 
-#[cfg(u64_digit)]
-impl ExactSizeIterator for U32Digits<'_> {
-    #[inline]
-    fn len(&self) -> usize {
-        self.data.len() * 2 - usize::from(self.last_hi_is_zero) - usize::from(!self.next_is_lo)
-    }
-}
+        impl DoubleEndedIterator for U32Digits<'_> {
+            fn next_back(&mut self) -> Option<Self::Item> {
+                match self.data.split_last() {
+                    Some((&last, data)) => {
+                        let last_is_lo = self.last_hi_is_zero;
+                        self.last_hi_is_zero = !last_is_lo;
+                        if last_is_lo {
+                            self.data = data;
+                            if data.is_empty() && !self.next_is_lo {
+                                self.next_is_lo = true;
+                                None
+                            } else {
+                                Some(last as u32)
+                            }
+                        } else {
+                            Some((last >> 32) as u32)
+                        }
+                    }
+                    None => None,
+                }
+            }
+        }
 
-#[cfg(not(u64_digit))]
-impl<'a> U32Digits<'a> {
-    #[inline]
-    pub(super) fn new(data: &'a [u32]) -> Self {
-        Self { it: data.iter() }
-    }
-}
-
-#[cfg(not(u64_digit))]
-impl Iterator for U32Digits<'_> {
-    type Item = u32;
-    #[inline]
-    fn next(&mut self) -> Option<u32> {
-        self.it.next().cloned()
-    }
-
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.it.size_hint()
-    }
-
-    #[inline]
-    fn nth(&mut self, n: usize) -> Option<u32> {
-        self.it.nth(n).cloned()
-    }
-
-    #[inline]
-    fn last(self) -> Option<u32> {
-        self.it.last().cloned()
-    }
-
-    #[inline]
-    fn count(self) -> usize {
-        self.it.count()
-    }
-}
-
-#[cfg(not(u64_digit))]
-impl DoubleEndedIterator for U32Digits<'_> {
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.it.next_back().copied()
-    }
-}
-
-#[cfg(not(u64_digit))]
-impl ExactSizeIterator for U32Digits<'_> {
-    #[inline]
-    fn len(&self) -> usize {
-        self.it.len()
-    }
-}
+        impl ExactSizeIterator for U32Digits<'_> {
+            #[inline]
+            fn len(&self) -> usize {
+                self.data.len() * 2
+                    - usize::from(self.last_hi_is_zero)
+                    - usize::from(!self.next_is_lo)
+            }
+        }
+    };
+);
 
 impl FusedIterator for U32Digits<'_> {}
 
-/// An iterator of `u64` digits representation of a `BigUint` or `BigInt`,
-/// ordered least significant digit first.
-pub struct U64Digits<'a> {
-    #[cfg(not(u64_digit))]
-    it: core::slice::Chunks<'a, u32>,
-
-    #[cfg(u64_digit)]
-    it: core::slice::Iter<'a, u64>,
-}
-
-#[cfg(not(u64_digit))]
-impl<'a> U64Digits<'a> {
-    #[inline]
-    pub(super) fn new(data: &'a [u32]) -> Self {
-        U64Digits { it: data.chunks(2) }
-    }
-}
-
-#[cfg(not(u64_digit))]
-impl Iterator for U64Digits<'_> {
-    type Item = u64;
-    #[inline]
-    fn next(&mut self) -> Option<u64> {
-        self.it.next().map(u32_chunk_to_u64)
+cfg_digit!(
+    /// An iterator of `u64` digits representation of a `BigUint` or `BigInt`,
+    /// ordered least significant digit first.
+    pub struct U64Digits<'a> {
+        it: core::slice::Chunks<'a, u32>,
     }
 
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let len = self.len();
-        (len, Some(len))
+    /// An iterator of `u64` digits representation of a `BigUint` or `BigInt`,
+    /// ordered least significant digit first.
+    pub struct U64Digits<'a> {
+        it: core::slice::Iter<'a, u64>,
     }
+);
 
-    #[inline]
-    fn last(self) -> Option<u64> {
-        self.it.last().map(u32_chunk_to_u64)
-    }
+cfg_digit!(
+    const _: () = {
+        impl<'a> U64Digits<'a> {
+            #[inline]
+            pub(super) fn new(data: &'a [u32]) -> Self {
+                U64Digits { it: data.chunks(2) }
+            }
+        }
 
-    #[inline]
-    fn count(self) -> usize {
-        self.len()
-    }
-}
+        impl Iterator for U64Digits<'_> {
+            type Item = u64;
+            #[inline]
+            fn next(&mut self) -> Option<u64> {
+                self.it.next().map(super::u32_chunk_to_u64)
+            }
 
-#[cfg(not(u64_digit))]
-impl DoubleEndedIterator for U64Digits<'_> {
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.it.next_back().map(u32_chunk_to_u64)
-    }
-}
+            #[inline]
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                let len = self.len();
+                (len, Some(len))
+            }
 
-#[cfg(u64_digit)]
-impl<'a> U64Digits<'a> {
-    #[inline]
-    pub(super) fn new(data: &'a [u64]) -> Self {
-        Self { it: data.iter() }
-    }
-}
+            #[inline]
+            fn last(self) -> Option<u64> {
+                self.it.last().map(super::u32_chunk_to_u64)
+            }
 
-#[cfg(u64_digit)]
-impl Iterator for U64Digits<'_> {
-    type Item = u64;
-    #[inline]
-    fn next(&mut self) -> Option<u64> {
-        self.it.next().cloned()
-    }
+            #[inline]
+            fn count(self) -> usize {
+                self.len()
+            }
+        }
 
-    #[inline]
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        self.it.size_hint()
-    }
+        impl DoubleEndedIterator for U64Digits<'_> {
+            fn next_back(&mut self) -> Option<Self::Item> {
+                self.it.next_back().map(super::u32_chunk_to_u64)
+            }
+        }
+    };
 
-    #[inline]
-    fn nth(&mut self, n: usize) -> Option<u64> {
-        self.it.nth(n).cloned()
-    }
+    const _: () = {
+        impl<'a> U64Digits<'a> {
+            #[inline]
+            pub(super) fn new(data: &'a [u64]) -> Self {
+                Self { it: data.iter() }
+            }
+        }
 
-    #[inline]
-    fn last(self) -> Option<u64> {
-        self.it.last().cloned()
-    }
+        impl Iterator for U64Digits<'_> {
+            type Item = u64;
+            #[inline]
+            fn next(&mut self) -> Option<u64> {
+                self.it.next().cloned()
+            }
 
-    #[inline]
-    fn count(self) -> usize {
-        self.it.count()
-    }
-}
+            #[inline]
+            fn size_hint(&self) -> (usize, Option<usize>) {
+                self.it.size_hint()
+            }
 
-#[cfg(u64_digit)]
-impl DoubleEndedIterator for U64Digits<'_> {
-    fn next_back(&mut self) -> Option<Self::Item> {
-        self.it.next_back().cloned()
-    }
-}
+            #[inline]
+            fn nth(&mut self, n: usize) -> Option<u64> {
+                self.it.nth(n).cloned()
+            }
+
+            #[inline]
+            fn last(self) -> Option<u64> {
+                self.it.last().cloned()
+            }
+
+            #[inline]
+            fn count(self) -> usize {
+                self.it.count()
+            }
+        }
+
+        impl DoubleEndedIterator for U64Digits<'_> {
+            fn next_back(&mut self) -> Option<Self::Item> {
+                self.it.next_back().cloned()
+            }
+        }
+    };
+);
 
 impl ExactSizeIterator for U64Digits<'_> {
     #[inline]
diff --git a/crates/num-bigint/src/biguint/monty.rs b/crates/num-bigint/src/biguint/monty.rs
index abaca50..a0b26b5 100644
--- a/crates/num-bigint/src/biguint/monty.rs
+++ b/crates/num-bigint/src/biguint/monty.rs
@@ -1,9 +1,9 @@
-use crate::std_alloc::Vec;
+use alloc::vec::Vec;
 use core::mem;
 use core::ops::Shl;
-use num_traits::{One, Zero};
+use num_traits::One;
 
-use crate::big_digit::{self, BigDigit, DoubleBigDigit, SignedDoubleBigDigit};
+use crate::big_digit::{self, BigDigit, DoubleBigDigit};
 use crate::biguint::BigUint;
 
 struct MontyReducer {
@@ -15,8 +15,8 @@
 fn inv_mod_alt(b: BigDigit) -> BigDigit {
     assert_ne!(b & 1, 0);
 
-    let mut k0 = 2 - b as SignedDoubleBigDigit;
-    let mut t = (b - 1) as SignedDoubleBigDigit;
+    let mut k0 = BigDigit::wrapping_sub(2, b);
+    let mut t = b - 1;
     let mut i = 1;
     while i < big_digit::BITS {
         t = t.wrapping_mul(t);
@@ -24,7 +24,8 @@
 
         i <<= 1;
     }
-    -k0 as BigDigit
+    debug_assert_eq!(k0.wrapping_mul(b), 1);
+    k0.wrapping_neg()
 }
 
 impl MontyReducer {
@@ -56,7 +57,7 @@
         n
     );
 
-    let mut z = BigUint::zero();
+    let mut z = BigUint::ZERO;
     z.data.resize(n * 2, 0);
 
     let mut c: BigDigit = 0;
@@ -173,7 +174,7 @@
     // initialize z = 1 (Montgomery 1)
     let mut z = powers[0].clone();
     z.data.resize(num_words, 0);
-    let mut zz = BigUint::zero();
+    let mut zz = BigUint::ZERO;
     zz.data.resize(num_words, 0);
 
     // same windowed exponent, but with Montgomery multiplications
diff --git a/crates/num-bigint/src/biguint/multiplication.rs b/crates/num-bigint/src/biguint/multiplication.rs
index 4d7f1f2..e9d2138 100644
--- a/crates/num-bigint/src/biguint/multiplication.rs
+++ b/crates/num-bigint/src/biguint/multiplication.rs
@@ -1,7 +1,5 @@
 use super::addition::{__add2, add2};
 use super::subtraction::sub2;
-#[cfg(not(u64_digit))]
-use super::u32_from_u128;
 use super::{biguint_from_vec, cmp_slice, BigUint, IntDigits};
 
 use crate::big_digit::{self, BigDigit, DoubleBigDigit};
@@ -88,9 +86,10 @@
     let acc = acc;
     let (x, y) = if b.len() < c.len() { (b, c) } else { (c, b) };
 
-    // We use three algorithms for different input sizes.
+    // We use four algorithms for different input sizes.
     //
     // - For small inputs, long multiplication is fastest.
+    // - If y is at least least twice as long as x, split using Half-Karatsuba.
     // - Next we use Karatsuba multiplication (Toom-2), which we have optimized
     //   to avoid unnecessary allocations for intermediate values.
     // - For the largest inputs we use Toom-3, which better optimizes the
@@ -104,6 +103,65 @@
         for (i, xi) in x.iter().enumerate() {
             mac_digit(&mut acc[i..], y, *xi);
         }
+    } else if x.len() * 2 <= y.len() {
+        // Karatsuba Multiplication for factors with significant length disparity.
+        //
+        // The Half-Karatsuba Multiplication Algorithm is a specialized case of
+        // the normal Karatsuba multiplication algorithm, designed for the scenario
+        // where y has at least twice as many base digits as x.
+        //
+        // In this case y (the longer input) is split into high2 and low2,
+        // at m2 (half the length of y) and x (the shorter input),
+        // is used directly without splitting.
+        //
+        // The algorithm then proceeds as follows:
+        //
+        // 1. Compute the product z0 = x * low2.
+        // 2. Compute the product temp = x * high2.
+        // 3. Adjust the weight of temp by adding m2 (* NBASE ^ m2)
+        // 4. Add temp and z0 to obtain the final result.
+        //
+        // Proof:
+        //
+        // The algorithm can be derived from the original Karatsuba algorithm by
+        // simplifying the formula when the shorter factor x is not split into
+        // high and low parts, as shown below.
+        //
+        // Original Karatsuba formula:
+        //
+        //     result = (z2 * NBASE ^ (m2 × 2)) + ((z1 - z2 - z0) * NBASE ^ m2) + z0
+        //
+        // Substitutions:
+        //
+        //     low1 = x
+        //     high1 = 0
+        //
+        // Applying substitutions:
+        //
+        //     z0 = (low1 * low2)
+        //        = (x * low2)
+        //
+        //     z1 = ((low1 + high1) * (low2 + high2))
+        //        = ((x + 0) * (low2 + high2))
+        //        = (x * low2) + (x * high2)
+        //
+        //     z2 = (high1 * high2)
+        //        = (0 * high2)
+        //        = 0
+        //
+        // Simplified using the above substitutions:
+        //
+        //     result = (z2 * NBASE ^ (m2 × 2)) + ((z1 - z2 - z0) * NBASE ^ m2) + z0
+        //            = (0 * NBASE ^ (m2 × 2)) + ((z1 - 0 - z0) * NBASE ^ m2) + z0
+        //            = ((z1 - z0) * NBASE ^ m2) + z0
+        //            = ((z1 - z0) * NBASE ^ m2) + z0
+        //            = (x * high2) * NBASE ^ m2 + z0
+        let m2 = y.len() / 2;
+        let (low2, high2) = y.split_at(m2);
+
+        // (x * high2) * NBASE ^ m2 + z0
+        mac3(acc, x, low2);
+        mac3(&mut acc[m2..], x, high2);
     } else if x.len() <= 256 {
         // Karatsuba multiplication:
         //
@@ -311,9 +369,9 @@
         // in terms of its coefficients:
         //
         // w0 = w(0)
-        // w1 = w(0)/2 + w(1)/3 - w(-1) + w(2)/6 - 2*w(inf)
+        // w1 = w(0)/2 + w(1)/3 - w(-1) + w(-2)/6 - 2*w(inf)
         // w2 = -w(0) + w(1)/2 + w(-1)/2 - w(inf)
-        // w3 = -w(0)/2 + w(1)/6 + w(-1)/2 - w(1)/6
+        // w3 = -w(0)/2 + w(1)/6 + w(-1)/2 - w(-2)/6 + 2*w(inf)
         // w4 = w(inf)
         //
         // This particular sequence is given by Bodrato and is an interpolation
@@ -397,7 +455,7 @@
             sub2(&mut b, a);
             (Minus, biguint_from_vec(b))
         }
-        Ordering::Equal => (NoSign, Zero::zero()),
+        Ordering::Equal => (NoSign, BigUint::ZERO),
     }
 }
 
@@ -410,7 +468,7 @@
             fn mul(self, other: $Other) -> BigUint {
                 match (&*self.data, &*other.data) {
                     // multiply by zero
-                    (&[], _) | (_, &[]) => BigUint::zero(),
+                    (&[], _) | (_, &[]) => BigUint::ZERO,
                     // multiply by a scalar
                     (_, &[digit]) => self * digit,
                     (&[digit], _) => other * digit,
@@ -484,22 +542,22 @@
     }
 }
 impl MulAssign<u64> for BigUint {
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn mul_assign(&mut self, other: u64) {
-        if let Some(other) = BigDigit::from_u64(other) {
-            scalar_mul(self, other);
-        } else {
-            let (hi, lo) = big_digit::from_doublebigdigit(other);
-            *self = mul3(&self.data, &[lo, hi]);
+    cfg_digit!(
+        #[inline]
+        fn mul_assign(&mut self, other: u64) {
+            if let Some(other) = BigDigit::from_u64(other) {
+                scalar_mul(self, other);
+            } else {
+                let (hi, lo) = big_digit::from_doublebigdigit(other);
+                *self = mul3(&self.data, &[lo, hi]);
+            }
         }
-    }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn mul_assign(&mut self, other: u64) {
-        scalar_mul(self, other);
-    }
+        #[inline]
+        fn mul_assign(&mut self, other: u64) {
+            scalar_mul(self, other);
+        }
+    );
 }
 
 impl Mul<u128> for BigUint {
@@ -513,30 +571,30 @@
 }
 
 impl MulAssign<u128> for BigUint {
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn mul_assign(&mut self, other: u128) {
-        if let Some(other) = BigDigit::from_u128(other) {
-            scalar_mul(self, other);
-        } else {
-            *self = match u32_from_u128(other) {
-                (0, 0, c, d) => mul3(&self.data, &[d, c]),
-                (0, b, c, d) => mul3(&self.data, &[d, c, b]),
-                (a, b, c, d) => mul3(&self.data, &[d, c, b, a]),
-            };
+    cfg_digit!(
+        #[inline]
+        fn mul_assign(&mut self, other: u128) {
+            if let Some(other) = BigDigit::from_u128(other) {
+                scalar_mul(self, other);
+            } else {
+                *self = match super::u32_from_u128(other) {
+                    (0, 0, c, d) => mul3(&self.data, &[d, c]),
+                    (0, b, c, d) => mul3(&self.data, &[d, c, b]),
+                    (a, b, c, d) => mul3(&self.data, &[d, c, b, a]),
+                };
+            }
         }
-    }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn mul_assign(&mut self, other: u128) {
-        if let Some(other) = BigDigit::from_u128(other) {
-            scalar_mul(self, other);
-        } else {
-            let (hi, lo) = big_digit::from_doublebigdigit(other);
-            *self = mul3(&self.data, &[lo, hi]);
+        #[inline]
+        fn mul_assign(&mut self, other: u128) {
+            if let Some(other) = BigDigit::from_u128(other) {
+                scalar_mul(self, other);
+            } else {
+                let (hi, lo) = big_digit::from_doublebigdigit(other);
+                *self = mul3(&self.data, &[lo, hi]);
+            }
         }
-    }
+    );
 }
 
 impl CheckedMul for BigUint {
diff --git a/crates/num-bigint/src/biguint/power.rs b/crates/num-bigint/src/biguint/power.rs
index 621e1b1..fa1c492 100644
--- a/crates/num-bigint/src/biguint/power.rs
+++ b/crates/num-bigint/src/biguint/power.rs
@@ -14,7 +14,7 @@
         if self.is_one() || exp.is_zero() {
             BigUint::one()
         } else if self.is_zero() {
-            BigUint::zero()
+            Self::ZERO
         } else if let Some(exp) = exp.to_u64() {
             self.pow(exp)
         } else if let Some(exp) = exp.to_u128() {
@@ -44,7 +44,7 @@
         if self.is_one() || exp.is_zero() {
             BigUint::one()
         } else if self.is_zero() {
-            BigUint::zero()
+            BigUint::ZERO
         } else {
             self.clone().pow(exp)
         }
diff --git a/crates/num-bigint/src/biguint/serde.rs b/crates/num-bigint/src/biguint/serde.rs
index 3240f09..84bdf50 100644
--- a/crates/num-bigint/src/biguint/serde.rs
+++ b/crates/num-bigint/src/biguint/serde.rs
@@ -1,7 +1,9 @@
+#![cfg(feature = "serde")]
+#![cfg_attr(docsrs, doc(cfg(feature = "serde")))]
+
 use super::{biguint_from_vec, BigUint};
 
-use crate::std_alloc::Vec;
-
+use alloc::vec::Vec;
 use core::{cmp, fmt, mem};
 use serde::de::{SeqAccess, Visitor};
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
@@ -18,44 +20,44 @@
 }
 
 impl Serialize for BigUint {
-    #[cfg(not(u64_digit))]
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        // Note: do not change the serialization format, or it may break forward
-        // and backward compatibility of serialized data!  If we ever change the
-        // internal representation, we should still serialize in base-`u32`.
-        let data: &[u32] = &self.data;
-        data.serialize(serializer)
-    }
-
-    #[cfg(u64_digit)]
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        use serde::ser::SerializeSeq;
-
-        if let Some((&last, data)) = self.data.split_last() {
-            let last_lo = last as u32;
-            let last_hi = (last >> 32) as u32;
-            let u32_len = data.len() * 2 + 1 + (last_hi != 0) as usize;
-            let mut seq = serializer.serialize_seq(Some(u32_len))?;
-            for &x in data {
-                seq.serialize_element(&(x as u32))?;
-                seq.serialize_element(&((x >> 32) as u32))?;
-            }
-            seq.serialize_element(&last_lo)?;
-            if last_hi != 0 {
-                seq.serialize_element(&last_hi)?;
-            }
-            seq.end()
-        } else {
-            let data: &[u32] = &[];
+    cfg_digit!(
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            // Note: do not change the serialization format, or it may break forward
+            // and backward compatibility of serialized data!  If we ever change the
+            // internal representation, we should still serialize in base-`u32`.
+            let data: &[u32] = &self.data;
             data.serialize(serializer)
         }
-    }
+
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            use serde::ser::SerializeSeq;
+
+            if let Some((&last, data)) = self.data.split_last() {
+                let last_lo = last as u32;
+                let last_hi = (last >> 32) as u32;
+                let u32_len = data.len() * 2 + 1 + (last_hi != 0) as usize;
+                let mut seq = serializer.serialize_seq(Some(u32_len))?;
+                for &x in data {
+                    seq.serialize_element(&(x as u32))?;
+                    seq.serialize_element(&((x >> 32) as u32))?;
+                }
+                seq.serialize_element(&last_lo)?;
+                if last_hi != 0 {
+                    seq.serialize_element(&last_hi)?;
+                }
+                seq.end()
+            } else {
+                let data: &[u32] = &[];
+                data.serialize(serializer)
+            }
+        }
+    );
 }
 
 impl<'de> Deserialize<'de> for BigUint {
@@ -76,44 +78,44 @@
         formatter.write_str("a sequence of unsigned 32-bit numbers")
     }
 
-    #[cfg(not(u64_digit))]
-    fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
-    where
-        S: SeqAccess<'de>,
-    {
-        let len = cautious(seq.size_hint());
-        let mut data = Vec::with_capacity(len);
+    cfg_digit!(
+        fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
+        where
+            S: SeqAccess<'de>,
+        {
+            let len = cautious(seq.size_hint());
+            let mut data = Vec::with_capacity(len);
 
-        while let Some(value) = seq.next_element::<u32>()? {
-            data.push(value);
-        }
-
-        Ok(biguint_from_vec(data))
-    }
-
-    #[cfg(u64_digit)]
-    fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
-    where
-        S: SeqAccess<'de>,
-    {
-        use crate::big_digit::BigDigit;
-        use num_integer::Integer;
-
-        let u32_len = cautious(seq.size_hint());
-        let len = Integer::div_ceil(&u32_len, &2);
-        let mut data = Vec::with_capacity(len);
-
-        while let Some(lo) = seq.next_element::<u32>()? {
-            let mut value = BigDigit::from(lo);
-            if let Some(hi) = seq.next_element::<u32>()? {
-                value |= BigDigit::from(hi) << 32;
+            while let Some(value) = seq.next_element::<u32>()? {
                 data.push(value);
-            } else {
-                data.push(value);
-                break;
             }
+
+            Ok(biguint_from_vec(data))
         }
 
-        Ok(biguint_from_vec(data))
-    }
+        fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
+        where
+            S: SeqAccess<'de>,
+        {
+            use crate::big_digit::BigDigit;
+            use num_integer::Integer;
+
+            let u32_len = cautious(seq.size_hint());
+            let len = Integer::div_ceil(&u32_len, &2);
+            let mut data = Vec::with_capacity(len);
+
+            while let Some(lo) = seq.next_element::<u32>()? {
+                let mut value = BigDigit::from(lo);
+                if let Some(hi) = seq.next_element::<u32>()? {
+                    value |= BigDigit::from(hi) << 32;
+                    data.push(value);
+                } else {
+                    data.push(value);
+                    break;
+                }
+            }
+
+            Ok(biguint_from_vec(data))
+        }
+    );
 }
diff --git a/crates/num-bigint/src/biguint/shift.rs b/crates/num-bigint/src/biguint/shift.rs
index 00326bb..51483cd 100644
--- a/crates/num-bigint/src/biguint/shift.rs
+++ b/crates/num-bigint/src/biguint/shift.rs
@@ -1,8 +1,9 @@
 use super::{biguint_from_vec, BigUint};
 
 use crate::big_digit;
-use crate::std_alloc::{Cow, Vec};
 
+use alloc::borrow::Cow;
+use alloc::vec::Vec;
 use core::mem;
 use core::ops::{Shl, ShlAssign, Shr, ShrAssign};
 use num_traits::{PrimInt, Zero};
@@ -58,7 +59,7 @@
         return n.into_owned();
     }
     let bits = T::from(big_digit::BITS).unwrap();
-    let digits = (shift / bits).to_usize().unwrap_or(core::usize::MAX);
+    let digits = (shift / bits).to_usize().unwrap_or(usize::MAX);
     let shift = (shift % bits).to_u8().unwrap();
     biguint_shr2(n, digits, shift)
 }
@@ -135,7 +136,7 @@
         impl ShlAssign<$rhs> for BigUint {
             #[inline]
             fn shl_assign(&mut self, rhs: $rhs) {
-                let n = mem::replace(self, BigUint::zero());
+                let n = mem::replace(self, Self::ZERO);
                 *self = n << rhs;
             }
         }
@@ -160,7 +161,7 @@
         impl ShrAssign<$rhs> for BigUint {
             #[inline]
             fn shr_assign(&mut self, rhs: $rhs) {
-                let n = mem::replace(self, BigUint::zero());
+                let n = mem::replace(self, Self::ZERO);
                 *self = n >> rhs;
             }
         }
diff --git a/crates/num-bigint/src/biguint/subtraction.rs b/crates/num-bigint/src/biguint/subtraction.rs
index b7cf59d..47a5015 100644
--- a/crates/num-bigint/src/biguint/subtraction.rs
+++ b/crates/num-bigint/src/biguint/subtraction.rs
@@ -1,5 +1,3 @@
-#[cfg(not(u64_digit))]
-use super::u32_from_u128;
 use super::BigUint;
 
 use crate::big_digit::{self, BigDigit};
@@ -7,42 +5,44 @@
 
 use core::cmp::Ordering::{Equal, Greater, Less};
 use core::ops::{Sub, SubAssign};
-use num_traits::{CheckedSub, Zero};
+use num_traits::CheckedSub;
 
-#[cfg(all(use_addcarry, target_arch = "x86_64"))]
+#[cfg(target_arch = "x86_64")]
 use core::arch::x86_64 as arch;
 
-#[cfg(all(use_addcarry, target_arch = "x86"))]
+#[cfg(target_arch = "x86")]
 use core::arch::x86 as arch;
 
 // Subtract with borrow:
-#[cfg(all(use_addcarry, u64_digit))]
-#[inline]
-fn sbb(borrow: u8, a: u64, b: u64, out: &mut u64) -> u8 {
-    // Safety: There are absolutely no safety concerns with calling `_subborrow_u64`.
-    // It's just unsafe for API consistency with other intrinsics.
-    unsafe { arch::_subborrow_u64(borrow, a, b, out) }
-}
+#[cfg(target_arch = "x86_64")]
+cfg_64!(
+    #[inline]
+    fn sbb(borrow: u8, a: u64, b: u64, out: &mut u64) -> u8 {
+        // Safety: There are absolutely no safety concerns with calling `_subborrow_u64`.
+        // It's just unsafe for API consistency with other intrinsics.
+        unsafe { arch::_subborrow_u64(borrow, a, b, out) }
+    }
+);
 
-#[cfg(all(use_addcarry, not(u64_digit)))]
-#[inline]
-fn sbb(borrow: u8, a: u32, b: u32, out: &mut u32) -> u8 {
-    // Safety: There are absolutely no safety concerns with calling `_subborrow_u32`.
-    // It's just unsafe for API consistency with other intrinsics.
-    unsafe { arch::_subborrow_u32(borrow, a, b, out) }
-}
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+cfg_32!(
+    #[inline]
+    fn sbb(borrow: u8, a: u32, b: u32, out: &mut u32) -> u8 {
+        // Safety: There are absolutely no safety concerns with calling `_subborrow_u32`.
+        // It's just unsafe for API consistency with other intrinsics.
+        unsafe { arch::_subborrow_u32(borrow, a, b, out) }
+    }
+);
 
 // fallback for environments where we don't have a subborrow intrinsic
-#[cfg(not(use_addcarry))]
+// (copied from the standard library's `borrowing_sub`)
+#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
 #[inline]
-fn sbb(borrow: u8, a: BigDigit, b: BigDigit, out: &mut BigDigit) -> u8 {
-    use crate::big_digit::SignedDoubleBigDigit;
-
-    let difference = SignedDoubleBigDigit::from(a)
-        - SignedDoubleBigDigit::from(b)
-        - SignedDoubleBigDigit::from(borrow);
-    *out = difference as BigDigit;
-    u8::from(difference < 0)
+fn sbb(borrow: u8, lhs: BigDigit, rhs: BigDigit, out: &mut BigDigit) -> u8 {
+    let (a, b) = lhs.overflowing_sub(rhs);
+    let (c, d) = a.overflowing_sub(borrow as BigDigit);
+    *out = c;
+    u8::from(b || d)
 }
 
 pub(super) fn sub2(a: &mut [BigDigit], b: &[BigDigit]) {
@@ -167,27 +167,27 @@
 impl Sub<BigUint> for u32 {
     type Output = BigUint;
 
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn sub(self, mut other: BigUint) -> BigUint {
-        if other.data.len() == 0 {
-            other.data.push(self);
-        } else {
-            sub2rev(&[self], &mut other.data[..]);
+    cfg_digit!(
+        #[inline]
+        fn sub(self, mut other: BigUint) -> BigUint {
+            if other.data.len() == 0 {
+                other.data.push(self);
+            } else {
+                sub2rev(&[self], &mut other.data[..]);
+            }
+            other.normalized()
         }
-        other.normalized()
-    }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn sub(self, mut other: BigUint) -> BigUint {
-        if other.data.is_empty() {
-            other.data.push(self as BigDigit);
-        } else {
-            sub2rev(&[self as BigDigit], &mut other.data[..]);
+        #[inline]
+        fn sub(self, mut other: BigUint) -> BigUint {
+            if other.data.is_empty() {
+                other.data.push(self as BigDigit);
+            } else {
+                sub2rev(&[self as BigDigit], &mut other.data[..]);
+            }
+            other.normalized()
         }
-        other.normalized()
-    }
+    );
 }
 
 impl Sub<u64> for BigUint {
@@ -201,47 +201,47 @@
 }
 
 impl SubAssign<u64> for BigUint {
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn sub_assign(&mut self, other: u64) {
-        let (hi, lo) = big_digit::from_doublebigdigit(other);
-        sub2(&mut self.data[..], &[lo, hi]);
-        self.normalize();
-    }
+    cfg_digit!(
+        #[inline]
+        fn sub_assign(&mut self, other: u64) {
+            let (hi, lo) = big_digit::from_doublebigdigit(other);
+            sub2(&mut self.data[..], &[lo, hi]);
+            self.normalize();
+        }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn sub_assign(&mut self, other: u64) {
-        sub2(&mut self.data[..], &[other as BigDigit]);
-        self.normalize();
-    }
+        #[inline]
+        fn sub_assign(&mut self, other: u64) {
+            sub2(&mut self.data[..], &[other as BigDigit]);
+            self.normalize();
+        }
+    );
 }
 
 impl Sub<BigUint> for u64 {
     type Output = BigUint;
 
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn sub(self, mut other: BigUint) -> BigUint {
-        while other.data.len() < 2 {
-            other.data.push(0);
+    cfg_digit!(
+        #[inline]
+        fn sub(self, mut other: BigUint) -> BigUint {
+            while other.data.len() < 2 {
+                other.data.push(0);
+            }
+
+            let (hi, lo) = big_digit::from_doublebigdigit(self);
+            sub2rev(&[lo, hi], &mut other.data[..]);
+            other.normalized()
         }
 
-        let (hi, lo) = big_digit::from_doublebigdigit(self);
-        sub2rev(&[lo, hi], &mut other.data[..]);
-        other.normalized()
-    }
-
-    #[cfg(u64_digit)]
-    #[inline]
-    fn sub(self, mut other: BigUint) -> BigUint {
-        if other.data.is_empty() {
-            other.data.push(self);
-        } else {
-            sub2rev(&[self], &mut other.data[..]);
+        #[inline]
+        fn sub(self, mut other: BigUint) -> BigUint {
+            if other.data.is_empty() {
+                other.data.push(self);
+            } else {
+                sub2rev(&[self], &mut other.data[..]);
+            }
+            other.normalized()
         }
-        other.normalized()
-    }
+    );
 }
 
 impl Sub<u128> for BigUint {
@@ -255,49 +255,49 @@
 }
 
 impl SubAssign<u128> for BigUint {
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn sub_assign(&mut self, other: u128) {
-        let (a, b, c, d) = u32_from_u128(other);
-        sub2(&mut self.data[..], &[d, c, b, a]);
-        self.normalize();
-    }
+    cfg_digit!(
+        #[inline]
+        fn sub_assign(&mut self, other: u128) {
+            let (a, b, c, d) = super::u32_from_u128(other);
+            sub2(&mut self.data[..], &[d, c, b, a]);
+            self.normalize();
+        }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn sub_assign(&mut self, other: u128) {
-        let (hi, lo) = big_digit::from_doublebigdigit(other);
-        sub2(&mut self.data[..], &[lo, hi]);
-        self.normalize();
-    }
+        #[inline]
+        fn sub_assign(&mut self, other: u128) {
+            let (hi, lo) = big_digit::from_doublebigdigit(other);
+            sub2(&mut self.data[..], &[lo, hi]);
+            self.normalize();
+        }
+    );
 }
 
 impl Sub<BigUint> for u128 {
     type Output = BigUint;
 
-    #[cfg(not(u64_digit))]
-    #[inline]
-    fn sub(self, mut other: BigUint) -> BigUint {
-        while other.data.len() < 4 {
-            other.data.push(0);
+    cfg_digit!(
+        #[inline]
+        fn sub(self, mut other: BigUint) -> BigUint {
+            while other.data.len() < 4 {
+                other.data.push(0);
+            }
+
+            let (a, b, c, d) = super::u32_from_u128(self);
+            sub2rev(&[d, c, b, a], &mut other.data[..]);
+            other.normalized()
         }
 
-        let (a, b, c, d) = u32_from_u128(self);
-        sub2rev(&[d, c, b, a], &mut other.data[..]);
-        other.normalized()
-    }
+        #[inline]
+        fn sub(self, mut other: BigUint) -> BigUint {
+            while other.data.len() < 2 {
+                other.data.push(0);
+            }
 
-    #[cfg(u64_digit)]
-    #[inline]
-    fn sub(self, mut other: BigUint) -> BigUint {
-        while other.data.len() < 2 {
-            other.data.push(0);
+            let (hi, lo) = big_digit::from_doublebigdigit(self);
+            sub2rev(&[lo, hi], &mut other.data[..]);
+            other.normalized()
         }
-
-        let (hi, lo) = big_digit::from_doublebigdigit(self);
-        sub2rev(&[lo, hi], &mut other.data[..]);
-        other.normalized()
-    }
+    );
 }
 
 impl CheckedSub for BigUint {
@@ -305,7 +305,7 @@
     fn checked_sub(&self, v: &BigUint) -> Option<BigUint> {
         match self.cmp(v) {
             Less => None,
-            Equal => Some(Zero::zero()),
+            Equal => Some(Self::ZERO),
             Greater => Some(self.sub(v)),
         }
     }
diff --git a/crates/num-bigint/src/lib.rs b/crates/num-bigint/src/lib.rs
index 893b747..6e47479 100644
--- a/crates/num-bigint/src/lib.rs
+++ b/crates/num-bigint/src/lib.rs
@@ -21,12 +21,12 @@
 //! ```rust
 //! # fn main() {
 //! use num_bigint::BigUint;
-//! use num_traits::{Zero, One};
+//! use num_traits::One;
 //!
 //! // Calculate large fibonacci numbers.
 //! fn fib(n: usize) -> BigUint {
-//!     let mut f0: BigUint = Zero::zero();
-//!     let mut f1: BigUint = One::one();
+//!     let mut f0 = BigUint::ZERO;
+//!     let mut f1 = BigUint::one();
 //!     for _ in 0..n {
 //!         let f2 = f0 + &f1;
 //!         f0 = f1;
@@ -60,10 +60,10 @@
 //!
 //! ## Features
 //!
-//! The `std` crate feature is enabled by default, and is mandatory before Rust
-//! 1.36 and the stabilized `alloc` crate.  If you depend on `num-bigint` with
-//! `default-features = false`, you must manually enable the `std` feature yourself
-//! if your compiler is not new enough.
+//! The `std` crate feature is enabled by default, which enables [`std::error::Error`]
+//! implementations and some internal use of floating point approximations. This can be disabled by
+//! depending on `num-bigint` with `default-features = false`. Either way, the `alloc` crate is
+//! always required for heap allocation of the `BigInt`/`BigUint` digits.
 //!
 //! ### Random Generation
 //!
@@ -78,53 +78,42 @@
 //! Note that you must use the version of `rand` that `num-bigint` is compatible
 //! with: `0.8`.
 //!
+//! ### Arbitrary Big Integers
+//!
+//! `num-bigint` supports `arbitrary` and `quickcheck` features to implement
+//! [`arbitrary::Arbitrary`] and [`quickcheck::Arbitrary`], respectively, for both `BigInt` and
+//! `BigUint`. These are useful for fuzzing and other forms of randomized testing.
+//!
+//! ### Serialization
+//!
+//! The `serde` feature adds implementations of [`Serialize`][serde::Serialize] and
+//! [`Deserialize`][serde::Deserialize] for both `BigInt` and `BigUint`. Their serialized data is
+//! generated portably, regardless of platform differences like the internal digit size.
+//!
 //!
 //! ## Compatibility
 //!
-//! The `num-bigint` crate is tested for rustc 1.31 and greater.
+//! The `num-bigint` crate is tested for rustc 1.60 and greater.
 
+#![cfg_attr(docsrs, feature(doc_cfg))]
 #![doc(html_root_url = "https://docs.rs/num-bigint/0.4")]
 #![warn(rust_2018_idioms)]
 #![no_std]
 
-#[cfg(feature = "std")]
-#[macro_use]
-extern crate std;
-
-#[cfg(feature = "std")]
-mod std_alloc {
-    pub(crate) use std::borrow::Cow;
-    #[cfg(feature = "quickcheck")]
-    pub(crate) use std::boxed::Box;
-    pub(crate) use std::string::String;
-    pub(crate) use std::vec::Vec;
-}
-
-#[cfg(not(feature = "std"))]
 #[macro_use]
 extern crate alloc;
 
-#[cfg(not(feature = "std"))]
-mod std_alloc {
-    pub(crate) use alloc::borrow::Cow;
-    #[cfg(feature = "quickcheck")]
-    pub(crate) use alloc::boxed::Box;
-    pub(crate) use alloc::string::String;
-    pub(crate) use alloc::vec::Vec;
-}
+#[cfg(feature = "std")]
+extern crate std;
 
 use core::fmt;
-#[cfg(feature = "std")]
-use std::error::Error;
 
 #[macro_use]
 mod macros;
 
 mod bigint;
-mod biguint;
-
-#[cfg(feature = "rand")]
 mod bigrand;
+mod biguint;
 
 #[cfg(target_pointer_width = "32")]
 type UsizePromotion = u32;
@@ -176,20 +165,19 @@
 }
 
 #[cfg(feature = "std")]
-impl Error for ParseBigIntError {
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for ParseBigIntError {
     fn description(&self) -> &str {
         self.__description()
     }
 }
 
 /// The error type returned when a checked conversion regarding big integer fails.
-#[cfg(has_try_from)]
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub struct TryFromBigIntError<T> {
     original: T,
 }
 
-#[cfg(has_try_from)]
 impl<T> TryFromBigIntError<T> {
     fn new(original: T) -> Self {
         TryFromBigIntError { original }
@@ -206,7 +194,8 @@
     }
 }
 
-#[cfg(all(feature = "std", has_try_from))]
+#[cfg(feature = "std")]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
 impl<T> std::error::Error for TryFromBigIntError<T>
 where
     T: fmt::Debug,
@@ -216,7 +205,6 @@
     }
 }
 
-#[cfg(has_try_from)]
 impl<T> fmt::Display for TryFromBigIntError<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.__description().fmt(f)
@@ -233,39 +221,29 @@
 pub use crate::bigint::ToBigInt;
 
 #[cfg(feature = "rand")]
+#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
 pub use crate::bigrand::{RandBigInt, RandomBits, UniformBigInt, UniformBigUint};
 
 mod big_digit {
-    /// A [`BigDigit`] is a [`BigUint`]'s composing element.
-    #[cfg(not(u64_digit))]
-    pub(crate) type BigDigit = u32;
-    #[cfg(u64_digit)]
-    pub(crate) type BigDigit = u64;
+    // A [`BigDigit`] is a [`BigUint`]'s composing element.
+    cfg_digit!(
+        pub(crate) type BigDigit = u32;
+        pub(crate) type BigDigit = u64;
+    );
 
-    /// A [`DoubleBigDigit`] is the internal type used to do the computations.  Its
-    /// size is the double of the size of [`BigDigit`].
-    #[cfg(not(u64_digit))]
-    pub(crate) type DoubleBigDigit = u64;
-    #[cfg(u64_digit)]
-    pub(crate) type DoubleBigDigit = u128;
+    // A [`DoubleBigDigit`] is the internal type used to do the computations.  Its
+    // size is the double of the size of [`BigDigit`].
+    cfg_digit!(
+        pub(crate) type DoubleBigDigit = u64;
+        pub(crate) type DoubleBigDigit = u128;
+    );
 
-    /// A [`SignedDoubleBigDigit`] is the signed version of [`DoubleBigDigit`].
-    #[cfg(not(u64_digit))]
-    pub(crate) type SignedDoubleBigDigit = i64;
-    #[cfg(u64_digit)]
-    pub(crate) type SignedDoubleBigDigit = i128;
-
-    // [`DoubleBigDigit`] size dependent
-    #[cfg(not(u64_digit))]
-    pub(crate) const BITS: u8 = 32;
-    #[cfg(u64_digit)]
-    pub(crate) const BITS: u8 = 64;
-
+    pub(crate) const BITS: u8 = BigDigit::BITS as u8;
     pub(crate) const HALF_BITS: u8 = BITS / 2;
     pub(crate) const HALF: BigDigit = (1 << HALF_BITS) - 1;
 
-    const LO_MASK: DoubleBigDigit = (1 << BITS) - 1;
-    pub(crate) const MAX: BigDigit = LO_MASK as BigDigit;
+    pub(crate) const MAX: BigDigit = BigDigit::MAX;
+    const LO_MASK: DoubleBigDigit = MAX as DoubleBigDigit;
 
     #[inline]
     fn get_hi(n: DoubleBigDigit) -> BigDigit {
diff --git a/crates/num-bigint/src/macros.rs b/crates/num-bigint/src/macros.rs
index 1618616..f2e21e3 100644
--- a/crates/num-bigint/src/macros.rs
+++ b/crates/num-bigint/src/macros.rs
@@ -1,5 +1,37 @@
 #![allow(unused_macros)]
 
+macro_rules! cfg_32 {
+    ($($any:tt)+) => {
+        #[cfg(not(target_pointer_width = "64"))] $($any)+
+    }
+}
+
+macro_rules! cfg_32_or_test {
+    ($($any:tt)+) => {
+        #[cfg(any(not(target_pointer_width = "64"), test))] $($any)+
+    }
+}
+
+macro_rules! cfg_64 {
+    ($($any:tt)+) => {
+        #[cfg(target_pointer_width = "64")] $($any)+
+    }
+}
+
+macro_rules! cfg_digit {
+    ($item32:item $item64:item) => {
+        cfg_32!($item32);
+        cfg_64!($item64);
+    };
+}
+
+macro_rules! cfg_digit_expr {
+    ($expr32:expr, $expr64:expr) => {
+        cfg_32!($expr32);
+        cfg_64!($expr64);
+    };
+}
+
 macro_rules! forward_val_val_binop {
     (impl $imp:ident for $res:ty, $method:ident) => {
         impl $imp<$res> for $res {
@@ -418,7 +450,7 @@
             where
                 I: Iterator<Item = T>,
             {
-                iter.fold(Zero::zero(), <$res>::add)
+                iter.fold(Self::ZERO, <$res>::add)
             }
         }
     };
diff --git a/crates/num-bigint/tests/bigint.rs b/crates/num-bigint/tests/bigint.rs
index 75cf81e..fea5232 100644
--- a/crates/num-bigint/tests/bigint.rs
+++ b/crates/num-bigint/tests/bigint.rs
@@ -8,9 +8,6 @@
 use std::iter::repeat;
 use std::ops::Neg;
 use std::{f32, f64};
-use std::{i128, u128};
-use std::{i16, i32, i64, i8, isize};
-use std::{u16, u32, u64, u8, usize};
 
 use num_integer::Integer;
 use num_traits::{
diff --git a/crates/num-bigint/tests/bigint_bitwise.rs b/crates/num-bigint/tests/bigint_bitwise.rs
index 6c1e82f..94d92e1 100644
--- a/crates/num-bigint/tests/bigint_bitwise.rs
+++ b/crates/num-bigint/tests/bigint_bitwise.rs
@@ -1,6 +1,5 @@
 use num_bigint::{BigInt, Sign, ToBigInt};
 use num_traits::ToPrimitive;
-use std::{i32, i64, u32};
 
 enum ValueVec {
     N,
diff --git a/crates/num-bigint/tests/biguint.rs b/crates/num-bigint/tests/biguint.rs
index c027771..4e2c625 100644
--- a/crates/num-bigint/tests/biguint.rs
+++ b/crates/num-bigint/tests/biguint.rs
@@ -6,12 +6,9 @@
 use std::cmp::Ordering::{Equal, Greater, Less};
 use std::collections::hash_map::RandomState;
 use std::hash::{BuildHasher, Hash, Hasher};
-use std::i64;
 use std::iter::repeat;
 use std::str::FromStr;
 use std::{f32, f64};
-use std::{i128, u128};
-use std::{u16, u32, u64, u8, usize};
 
 use num_traits::{
     pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Euclid, FromBytes, FromPrimitive, Num,
diff --git a/crates/num-bigint/tests/consts/mod.rs b/crates/num-bigint/tests/consts/mod.rs
index 5d0555d..457fbd3 100644
--- a/crates/num-bigint/tests/consts/mod.rs
+++ b/crates/num-bigint/tests/consts/mod.rs
@@ -16,7 +16,7 @@
     (&[1, 2, 2, 1], &[N1, N2], &[0, 1, 3, 1]),
 ];
 
-pub const M: u32 = ::std::u32::MAX;
+pub const M: u32 = u32::MAX;
 pub const MUL_TRIPLES: &[(&[u32], &[u32], &[u32])] = &[
     (&[], &[], &[]),
     (&[], &[1], &[]),
diff --git a/crates/num-bigint/tests/roots.rs b/crates/num-bigint/tests/roots.rs
index cfef80c..7110adf 100644
--- a/crates/num-bigint/tests/roots.rs
+++ b/crates/num-bigint/tests/roots.rs
@@ -1,7 +1,6 @@
 mod biguint {
     use num_bigint::BigUint;
     use num_traits::{One, Zero};
-    use std::{i32, u32};
 
     fn check<T: Into<BigUint>>(x: T, n: u32) {
         let x: BigUint = x.into();
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 0315d99..6e7ff72 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -3473,11 +3473,10 @@
 
 [[package]]
 name = "num-bigint"
-version = "0.4.4"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
 dependencies = [
- "autocfg",
  "num-integer",
  "num-traits",
 ]
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index 3717891..0b5ce4f 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -210,7 +210,7 @@
 nix = "=0.28.0"
 no-panic = "=0.1.33"
 nom = "=7.1.3"
-num-bigint = "=0.4.4"
+num-bigint = "=0.4.6"
 num-complex = "=0.4.6"
 num-derive = "=0.4.2"
 num-integer = "=0.1.46"