diff --git a/.circleci/config.yml b/.circleci/config.yml index ff5bb17087..ddd1d20bf1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -72,7 +72,7 @@ workflows: matrix: parameters: # Run with MSRV and some modern stable Rust - rust-version: ["1.60.0", "1.68.2"] + rust-version: ["1.65.0", "1.68.2"] - benchmarking: requires: - package_vm @@ -85,7 +85,7 @@ workflows: - /^[0-9]+\.[0-9]+$/ # Add your branch here if benchmarking matters to your work - fix-benchmarking - - coverage + # - coverage # disabled temporarily because Rust version is too low deploy: jobs: - build_and_upload_devcontracts: @@ -117,7 +117,7 @@ jobs: command: | wget https://static.rust-lang.org/rustup/dist/aarch64-unknown-linux-gnu/rustup-init chmod +x rustup-init - ./rustup-init -y --default-toolchain 1.60.0 --profile minimal + ./rustup-init -y --default-toolchain 1.65.0 --profile minimal - run: name: Version information command: rustc --version; cargo --version; rustup --version; rustup target list --installed @@ -126,12 +126,12 @@ jobs: command: rustup target add wasm32-unknown-unknown && rustup target list --installed - restore_cache: keys: - - v4-arm64-workspace-rust:1.60.0-{{ checksum "Cargo.lock" }} - - v4-arm64-workspace-rust:1.60.0- + - v4-arm64-workspace-rust:1.65.0-{{ checksum "Cargo.lock" }} + - v4-arm64-workspace-rust:1.65.0- - restore_cache: keys: - - v4-arm64-contracts-rust:1.60.0-{{ checksum "contracts/crypto-verify/Cargo.lock" }}-{{ checksum "contracts/hackatom/Cargo.lock" }}-{{ checksum "contracts/queue/Cargo.lock" }}-{{ checksum "contracts/reflect/Cargo.lock" }}-{{ checksum "contracts/staking/Cargo.lock" }} - - v4-arm64-contracts-rust:1.60.0- + - v4-arm64-contracts-rust:1.65.0-{{ checksum "contracts/crypto-verify/Cargo.lock" }}-{{ checksum "contracts/hackatom/Cargo.lock" }}-{{ checksum "contracts/queue/Cargo.lock" }}-{{ checksum "contracts/reflect/Cargo.lock" }}-{{ checksum "contracts/staking/Cargo.lock" }} + - v4-arm64-contracts-rust:1.65.0- # Test a few contracts that do something potentially interesting in the VM # to test contract execution on ARM64. # No need to add all contracts here. @@ -169,14 +169,14 @@ jobs: # use all features command: cargo test --locked --features iterator,staking,stargate - save_cache: - key: v4-arm64-workspace-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: v4-arm64-workspace-rust:1.65.0-{{ checksum "Cargo.lock" }} paths: - ~/.cargo/registry - target/debug/.fingerprint - target/debug/build - target/debug/deps - save_cache: - key: v4-arm64-contracts-rust:1.60.0-{{ checksum "contracts/crypto-verify/Cargo.lock" }}-{{ checksum "contracts/hackatom/Cargo.lock" }}-{{ checksum "contracts/queue/Cargo.lock" }}-{{ checksum "contracts/reflect/Cargo.lock" }}-{{ checksum "contracts/staking/Cargo.lock" }} + key: v4-arm64-contracts-rust:1.65.0-{{ checksum "contracts/crypto-verify/Cargo.lock" }}-{{ checksum "contracts/hackatom/Cargo.lock" }}-{{ checksum "contracts/queue/Cargo.lock" }}-{{ checksum "contracts/reflect/Cargo.lock" }}-{{ checksum "contracts/staking/Cargo.lock" }} paths: - ~/.cargo/registry # crypto-verify @@ -217,7 +217,7 @@ jobs: package_crypto: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -225,7 +225,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_crypto-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_crypto-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/crypto @@ -240,11 +240,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_crypto-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_crypto-rust:1.65.0-{{ checksum "Cargo.lock" }} package_check: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -252,7 +252,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_check-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_check-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/check @@ -267,11 +267,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_check-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_check-rust:1.65.0-{{ checksum "Cargo.lock" }} package_schema: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -279,7 +279,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_schema-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_schema-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/schema @@ -294,11 +294,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_schema-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_schema-rust:1.65.0-{{ checksum "Cargo.lock" }} package_schema_derive: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -306,7 +306,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_schema_derive-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_schema_derive-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/schema-derive @@ -321,11 +321,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_schema_derive-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_schema_derive-rust:1.65.0-{{ checksum "Cargo.lock" }} package_std: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: # Limit the number of parallel jobs to avoid OOM crashes during doc testing RUST_TEST_THREADS: 8 @@ -336,7 +336,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_std-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_std-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Add wasm32 target command: rustup target add wasm32-unknown-unknown && rustup target list --installed @@ -370,11 +370,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_std-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_std-rust:1.65.0-{{ checksum "Cargo.lock" }} package_storage: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -382,7 +382,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_storage-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_storage-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build library for native target working_directory: ~/project/packages/storage @@ -401,11 +401,11 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_storage-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_storage-rust:1.65.0-{{ checksum "Cargo.lock" }} package_vm: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -413,7 +413,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-package_vm-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-package_vm-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Build working_directory: ~/project/packages/vm @@ -442,7 +442,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-package_vm-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-package_vm-rust:1.65.0-{{ checksum "Cargo.lock" }} package_vm_windows: executor: @@ -490,7 +490,7 @@ jobs: contract_burner: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/burner @@ -502,7 +502,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_burner-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_burner-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -513,11 +513,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_burner-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_burner-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_crypto_verify: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/crypto-verify @@ -529,7 +529,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_crypto_verify-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_crypto_verify-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -540,11 +540,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_crypto_verify-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_crypto_verify-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_cyberpunk: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/cyberpunk @@ -556,7 +556,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_cyberpunk-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_cyberpunk-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -567,11 +567,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_cyberpunk-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_cyberpunk-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_hackatom: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/hackatom @@ -583,7 +583,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_hackatom-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_hackatom-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -594,11 +594,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_hackatom-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_hackatom-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_ibc_reflect: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/ibc-reflect @@ -610,7 +610,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_ibc_reflect-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_ibc_reflect-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -621,11 +621,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_ibc_reflect-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_ibc_reflect-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_ibc_reflect_send: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/ibc-reflect-send @@ -637,7 +637,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_ibc_reflect_send-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_ibc_reflect_send-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -648,11 +648,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_ibc_reflect_send-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_ibc_reflect_send-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_floaty: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/floaty @@ -664,7 +664,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_floaty-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_floaty-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -675,11 +675,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_floaty-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_floaty-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_queue: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/queue @@ -691,7 +691,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_queue-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_queue-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -702,11 +702,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_queue-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_queue-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_reflect: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/reflect @@ -718,7 +718,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_reflect-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_reflect-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -729,11 +729,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_reflect-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_reflect-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_staking: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/staking @@ -745,7 +745,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_staking-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_staking-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -756,11 +756,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_staking-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_staking-rust:1.65.0-{{ checksum "Cargo.lock" }} contract_virus: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 working_directory: ~/cosmwasm/contracts/virus @@ -772,7 +772,7 @@ jobs: command: rustc --version; cargo --version; rustup --version - restore_cache: keys: - - cargocache-v2-contract_virus-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-contract_virus-rust:1.65.0-{{ checksum "Cargo.lock" }} - check_contract - save_cache: paths: @@ -783,11 +783,11 @@ jobs: - target/wasm32-unknown-unknown/release/.fingerprint - target/wasm32-unknown-unknown/release/build - target/wasm32-unknown-unknown/release/deps - key: cargocache-v2-contract_virus-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-contract_virus-rust:1.65.0-{{ checksum "Cargo.lock" }} fmt: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -795,7 +795,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-fmt-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-fmt-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Add rustfmt component command: rustup component add rustfmt @@ -808,7 +808,7 @@ jobs: - target/debug/.fingerprint - target/debug/build - target/debug/deps - key: cargocache-v2-fmt-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-fmt-rust:1.65.0-{{ checksum "Cargo.lock" }} fmt_extra: docker: @@ -830,7 +830,7 @@ jobs: deadlinks: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 steps: - checkout - run: @@ -838,7 +838,7 @@ jobs: command: rustc --version; cargo --version; rustup --version; rustup target list --installed - restore_cache: keys: - - cargocache-v2-deadlinks-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-deadlinks-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Generate docs command: cargo doc @@ -858,7 +858,7 @@ jobs: - target/debug/build - target/debug/deps - /root/.cache/pip - key: cargocache-v2-deadlinks-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-deadlinks-rust:1.65.0-{{ checksum "Cargo.lock" }} clippy: parameters: @@ -934,7 +934,7 @@ jobs: benchmarking: docker: - - image: rust:1.60.0 + - image: rust:1.65.0 environment: RUST_BACKTRACE: 1 steps: @@ -944,7 +944,7 @@ jobs: command: rustc --version && cargo --version - restore_cache: keys: - - cargocache-v2-benchmarking-rust:1.60.0-{{ checksum "Cargo.lock" }} + - cargocache-v2-benchmarking-rust:1.65.0-{{ checksum "Cargo.lock" }} - run: name: Run vm benchmarks (Singlepass) working_directory: ~/project/packages/vm @@ -962,7 +962,7 @@ jobs: - target/release/.fingerprint - target/release/build - target/release/deps - key: cargocache-v2-benchmarking-rust:1.60.0-{{ checksum "Cargo.lock" }} + key: cargocache-v2-benchmarking-rust:1.65.0-{{ checksum "Cargo.lock" }} coverage: # https://circleci.com/developer/images?imageType=machine @@ -1034,7 +1034,7 @@ jobs: name: Check development contracts command: | echo "Checking all contracts under ./artifacts" - docker run --volumes-from with_code rust:1.60.0 \ + docker run --volumes-from with_code rust:1.65.0 \ /bin/bash -e -c 'export GLOBIGNORE="artifacts/floaty.wasm"; cd ./code; cargo run --bin cosmwasm-check artifacts/*.wasm' docker cp with_code:/code/artifacts . - run: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d50ccbda5..3a83bedddb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: 1.60.0 + toolchain: 1.65.0 target: wasm32-unknown-unknown profile: minimal override: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aafbc2baf..41108579a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,12 +28,15 @@ and this project adheres to - cosmwasm-std: Add trait functions `Storage::range_keys` and `Storage::range_values`. The default implementations just use `Storage::range`. Later this can be implemented more efficiently. ([#1748]) +- cosmwasm-std: Add `Int64`, `Int128`, `Int256` and `Int512` signed integer + types. ([#1718]) [#1593]: https://github.com/CosmWasm/cosmwasm/pull/1593 [#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635 [#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647 [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687 +[#1718]: https://github.com/CosmWasm/cosmwasm/pull/1718 [#1727]: https://github.com/CosmWasm/cosmwasm/issues/1727 [#1747]: https://github.com/CosmWasm/cosmwasm/pull/1747 [#1748]: https://github.com/CosmWasm/cosmwasm/pull/1748 diff --git a/Cargo.lock b/Cargo.lock index 2a7dafafed..c76a0f514a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -350,6 +356,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "chrono", "cosmwasm-crypto", "cosmwasm-derive", @@ -364,7 +371,6 @@ dependencies = [ "serde_json", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -565,12 +571,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1684,12 +1684,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.8.0" @@ -1817,18 +1811,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-width" version = "0.1.9" diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index ffc5bc3faf..8ebe8fa1a9 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -215,6 +221,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -225,7 +232,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -381,12 +387,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1388,12 +1388,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1496,18 +1490,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 525807cef8..8193676a13 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -210,6 +216,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -220,7 +227,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -384,12 +390,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1446,12 +1446,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1554,18 +1548,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index b254a657ac..60fb94530e 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -114,6 +114,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -233,6 +239,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -243,7 +250,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -407,12 +413,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1438,12 +1438,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1546,18 +1540,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index 7903f0555d..b7eefeda60 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -214,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -378,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1398,12 +1398,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1506,18 +1500,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index 77eb0d0e6d..4a22c86347 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -214,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -378,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1399,12 +1399,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1507,18 +1501,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index 68f519d999..dfff7aa4e7 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -214,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -378,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1397,12 +1397,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1505,18 +1499,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index 06dcdd37bc..f14335aa9a 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -214,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -378,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1397,12 +1397,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1505,18 +1499,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index ec0086d567..baecd4b897 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -214,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -370,12 +376,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1388,12 +1388,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1496,18 +1490,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index 90a729bb0d..6539fc1a75 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -214,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -378,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1398,12 +1398,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1506,18 +1500,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index 7d8e834d27..ab6d49866e 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -214,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -378,12 +384,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1425,12 +1425,6 @@ dependencies = [ "snafu", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1533,18 +1527,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index a55dc46412..ba54a8d9e3 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -91,6 +91,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bnum" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "845141a4fade3f790628b7daaaa298a25b204fb28907eb54febe5142db6ce653" + [[package]] name = "bumpalo" version = "3.12.0" @@ -204,6 +210,7 @@ name = "cosmwasm-std" version = "1.2.7" dependencies = [ "base64", + "bnum", "cosmwasm-crypto", "cosmwasm-derive", "derivative", @@ -214,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -370,12 +376,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-bigint" version = "0.4.8" @@ -1377,12 +1377,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -1485,18 +1479,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "uint" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index af772d64e4..d9c9c37ac3 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -54,7 +54,7 @@ sha2 = "0.10.3" serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"] } serde-json-wasm = { version = "0.5.0" } thiserror = "1.0.26" -uint = "0.9.3" +bnum = "0.7.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } diff --git a/packages/std/src/errors/mod.rs b/packages/std/src/errors/mod.rs index 8c41d36540..54422007d3 100644 --- a/packages/std/src/errors/mod.rs +++ b/packages/std/src/errors/mod.rs @@ -6,8 +6,8 @@ mod verification_error; pub use recover_pubkey_error::RecoverPubkeyError; pub use std_error::{ CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError, - CoinFromStrError, CoinsError, ConversionOverflowError, DivideByZeroError, OverflowError, - OverflowOperation, RoundUpOverflowError, StdError, StdResult, + CoinFromStrError, CoinsError, ConversionOverflowError, DivideByZeroError, DivisionError, + OverflowError, OverflowOperation, RoundUpOverflowError, StdError, StdResult, }; pub use system_error::SystemError; pub use verification_error::VerificationError; diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index d30fec6a71..aecdb82cc6 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -556,6 +556,15 @@ impl DivideByZeroError { } } +#[derive(Error, Debug, PartialEq, Eq)] +pub enum DivisionError { + #[error("Divide by zero")] + DivideByZero, + + #[error("Overflow in division")] + Overflow, +} + #[derive(Error, Debug, PartialEq, Eq)] pub enum CheckedMultiplyFractionError { #[error("{0}")] diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index 28dfcc9ec1..13eeee2874 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -52,8 +52,8 @@ pub use crate::ibc::{ #[cfg(feature = "iterator")] pub use crate::iterator::{Order, Record}; pub use crate::math::{ - Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Isqrt, Uint128, - Uint256, Uint512, Uint64, + Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int128, Int256, + Int512, Int64, Isqrt, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs index 157e7397b7..927c10b616 100644 --- a/packages/std/src/math/decimal256.rs +++ b/packages/std/src/math/decimal256.rs @@ -2070,7 +2070,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn decimal256_rem_panics_for_zero() { let _ = Decimal256::percent(777) % Decimal256::zero(); } diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs new file mode 100644 index 0000000000..e30aea7914 --- /dev/null +++ b/packages/std/src/math/int128.rs @@ -0,0 +1,1104 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; + +/// An implementation of i128 that is using strings for JSON encoding/decoding, +/// such that the full i128 range can be used for clients that convert JSON numbers to floats, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances of this and `i128` to get the value out: +/// +/// ``` +/// # use cosmwasm_std::Int128; +/// let a = Int128::from(258i128); +/// assert_eq!(a.i128(), 258); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int128(#[schemars(with = "String")] i128); + +forward_ref_partial_eq!(Int128, Int128); + +impl Int128 { + pub const MAX: Int128 = Int128(i128::MAX); + pub const MIN: Int128 = Int128(i128::MIN); + + /// Creates a Int128(value). + /// + /// This method is less flexible than `from` but can be called in a const context. + #[inline] + pub const fn new(value: i128) -> Self { + Self(value) + } + + /// Creates a Int128(0) + #[inline] + pub const fn zero() -> Self { + Int128(0) + } + + /// Creates a Int128(1) + #[inline] + pub const fn one() -> Self { + Self(1) + } + + /// Returns a copy of the internal data + pub const fn i128(&self) -> i128 { + self.0 + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 16]) -> Self { + Self(i128::from_be_bytes(data)) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 16]) -> Self { + Self(i128::from_le_bytes(data)) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 16] { + self.0.to_be_bytes() + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 16] { + self.0.to_le_bytes() + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0 == 0 + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + Self(self.0.pow(exp)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 128 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 128 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint128 { + Uint128(self.0.abs_diff(other.0)) + } +} + +impl From for Int128 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int128 { + fn from(val: u64) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u32) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u16) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: u8) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: Int64) -> Self { + val.i64().into() + } +} + +impl From for Int128 { + fn from(val: i128) -> Self { + Int128(val) + } +} + +impl From for Int128 { + fn from(val: i64) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i32) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i16) -> Self { + Int128(val.into()) + } +} + +impl From for Int128 { + fn from(val: i8) -> Self { + Int128(val.into()) + } +} + +impl TryFrom<&str> for Int128 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int128 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int128: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int128) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int128 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Add for Int128 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int128(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int128, Int128); + +impl Sub for Int128 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int128(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int128, Int128); + +impl SubAssign for Int128 { + fn sub_assign(&mut self, rhs: Int128) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int128, Int128); + +impl Div for Int128 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int128, Int128); + +impl Rem for Int128 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero. + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for Int128, Int128); + +impl Not for Int128 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int128 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int128 { + fn rem_assign(&mut self, rhs: Int128) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int128, Int128); + +impl Mul for Int128 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int128, Int128); + +impl MulAssign for Int128 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int128, Int128); + +impl Shr for Int128 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int128", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int128, u32); + +impl Shl for Int128 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int128", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int128, u32); + +impl AddAssign for Int128 { + fn add_assign(&mut self, rhs: Int128) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int128, Int128); + +impl DivAssign for Int128 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int128, Int128); + +impl ShrAssign for Int128 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int128, u32); + +impl ShlAssign for Int128 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int128, u32); + +impl Serialize for Int128 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int128 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int128Visitor) + } +} + +struct Int128Visitor; + +impl<'de> de::Visitor<'de> for Int128Visitor { + type Value = Int128; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int128::try_from(v).map_err(|e| E::custom(format!("invalid Int128 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int128 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 16); + } + + #[test] + fn int128_from_be_bytes_works() { + let num = Int128::from_be_bytes([1; 16]); + let a: [u8; 16] = num.to_be_bytes(); + assert_eq!(a, [1; 16]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int128::from_be_bytes(be_bytes); + let resulting_bytes: [u8; 16] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int128_new_works() { + let num = Int128::new(222); + assert_eq!(num.i128(), 222); + + let num = Int128::new(-222); + assert_eq!(num.i128(), -222); + + let num = Int128::new(i128::MAX); + assert_eq!(num.i128(), i128::MAX); + + let num = Int128::new(i128::MIN); + assert_eq!(num.i128(), i128::MIN); + } + + #[test] + fn int128_zero_works() { + let zero = Int128::zero(); + assert_eq!(zero.to_be_bytes(), [0; 16]); + } + + #[test] + fn uint128_one_works() { + let one = Int128::one(); + let mut one_be = [0; 16]; + one_be[15] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int128_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int128::from_be_bytes(be_bytes); + let num2 = Int128::from_le_bytes(le_bytes); + assert_eq!(num1, Int128::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + } + + #[test] + fn int128_convert_from() { + let a = Int128::from(5i128); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u64); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u32); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u16); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(5u8); + assert_eq!(a.0, i128::from(5u32)); + + let a = Int128::from(-5i128); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i64); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i32); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i16); + assert_eq!(a.0, i128::from(-5i32)); + + let a = Int128::from(-5i8); + assert_eq!(a.0, i128::from(-5i32)); + + let result = Int128::try_from("34567"); + assert_eq!(result.unwrap().0, "34567".parse::().unwrap()); + + let result = Int128::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int128_implements_display() { + let a = Int128::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int128::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int128::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int128_display_padding_works() { + let a = Int128::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int128::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int128_to_be_bytes_works() { + assert_eq!(Int128::zero().to_be_bytes(), [0; 16]); + + let mut max = [0xff; 16]; + max[0] = 0x7f; + assert_eq!(Int128::MAX.to_be_bytes(), max); + + let mut one = [0; 16]; + one[15] = 1; + assert_eq!(Int128::from(1i128).to_be_bytes(), one); + // Python: `[b for b in (70141183460469231731687303715884018880).to_bytes(16, "big")]` + assert_eq!( + Int128::from(70141183460469231731687303715884018880i128).to_be_bytes(), + [52, 196, 179, 87, 165, 121, 59, 133, 246, 117, 221, 191, 255, 254, 172, 192] + ); + assert_eq!( + Int128::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78]) + .to_be_bytes(), + [17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78] + ); + } + + #[test] + fn int128_to_le_bytes_works() { + assert_eq!(Int128::zero().to_le_bytes(), [0; 16]); + + let mut max = [0xff; 16]; + max[15] = 0x7f; + assert_eq!(Int128::MAX.to_le_bytes(), max); + + let mut one = [0; 16]; + one[0] = 1; + assert_eq!(Int128::from(1i128).to_le_bytes(), one); + // Python: `[b for b in (70141183460469231731687303715884018880).to_bytes(16, "little")]` + assert_eq!( + Int128::from(70141183460469231731687303715884018880i128).to_le_bytes(), + [192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 52] + ); + assert_eq!( + Int128::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78]) + .to_le_bytes(), + [78, 67, 21, 33, 38, 0, 91, 58, 200, 123, 67, 87, 32, 23, 4, 17] + ); + } + + #[test] + fn int128_is_zero_works() { + assert!(Int128::zero().is_zero()); + assert!(Int128(i128::from(0u32)).is_zero()); + + assert!(!Int128::from(1u32).is_zero()); + assert!(!Int128::from(123u32).is_zero()); + assert!(!Int128::from(-123i32).is_zero()); + } + + #[test] + fn int128_wrapping_methods() { + // wrapping_add + assert_eq!( + Int128::from(2u32).wrapping_add(Int128::from(2u32)), + Int128::from(4u32) + ); // non-wrapping + assert_eq!(Int128::MAX.wrapping_add(Int128::from(1u32)), Int128::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int128::from(7u32).wrapping_sub(Int128::from(5u32)), + Int128::from(2u32) + ); // non-wrapping + assert_eq!(Int128::MIN.wrapping_sub(Int128::from(1u32)), Int128::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int128::from(3u32).wrapping_mul(Int128::from(2u32)), + Int128::from(6u32) + ); // non-wrapping + assert_eq!( + Int128::MAX.wrapping_mul(Int128::from(2u32)), + Int128::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int128::from(2u32).wrapping_pow(3), Int128::from(8u32)); // non-wrapping + assert_eq!(Int128::MAX.wrapping_pow(2), Int128::from(1u32)); // wrapping + } + + #[test] + fn int128_json() { + let orig = Int128::from(1234567890987654321i128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int128 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int128_compare() { + let a = Int128::from(12345u32); + let b = Int128::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int128::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_math() { + let a = Int128::from(-12345i32); + let b = Int128::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int128::from(11111u32)); + assert_eq!(a + &b, Int128::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int128::from(35801u32)); + assert_eq!(b - &a, Int128::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int128::from(300000u32); + c += b; + assert_eq!(c, Int128::from(323456u32)); + let mut d = Int128::from(300000u32); + d += &b; + assert_eq!(d, Int128::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int128::from(300000u32); + c -= b; + assert_eq!(c, Int128::from(276544u32)); + let mut d = Int128::from(300000u32); + d -= &b; + assert_eq!(d, Int128::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int128::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int128_add_overflow_panics() { + let _ = Int128::MAX + Int128::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_sub_works() { + assert_eq!(Int128::from(2u32) - Int128::from(1u32), Int128::from(1u32)); + assert_eq!(Int128::from(2u32) - Int128::from(0u32), Int128::from(2u32)); + assert_eq!(Int128::from(2u32) - Int128::from(2u32), Int128::from(0u32)); + assert_eq!(Int128::from(2u32) - Int128::from(3u32), Int128::from(-1i32)); + + // works for refs + let a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int128_sub_overflow_panics() { + let _ = Int128::MIN + Int128::one() - Int128::from(2u32); + } + + #[test] + fn int128_sub_assign_works() { + let mut a = Int128::from(14u32); + a -= Int128::from(2u32); + assert_eq!(a, Int128::from(12u32)); + + // works for refs + let mut a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_mul_works() { + assert_eq!(Int128::from(2u32) * Int128::from(3u32), Int128::from(6u32)); + assert_eq!(Int128::from(2u32) * Int128::zero(), Int128::zero()); + + // works for refs + let a = Int128::from(11u32); + let b = Int128::from(3u32); + let expected = Int128::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int128_mul_assign_works() { + let mut a = Int128::from(14u32); + a *= Int128::from(2u32); + assert_eq!(a, Int128::from(28u32)); + + // works for refs + let mut a = Int128::from(10u32); + let b = Int128::from(3u32); + a *= &b; + assert_eq!(a, Int128::from(30u32)); + } + + #[test] + fn int128_pow_works() { + assert_eq!(Int128::from(2u32).pow(2), Int128::from(4u32)); + assert_eq!(Int128::from(2u32).pow(10), Int128::from(1024u32)); + } + + #[test] + #[should_panic] + fn int128_pow_overflow_panics() { + _ = Int128::MAX.pow(2u32); + } + + #[test] + fn int128_shr_works() { + let original = Int128::from_be_bytes([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int128::from_be_bytes([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int128_shr_overflow_panics() { + let _ = Int128::from(1u32) >> 128u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int128::from(17u32), + Int128::from(123u32), + Int128::from(540u32), + Int128::from(82u32), + ]; + let expected = Int128::from(762u32); + + let sum_as_ref: Int128 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int128 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int128_methods() { + // checked_* + assert!(matches!( + Int128::MAX.checked_add(Int128::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(1u32).checked_add(Int128::from(1u32)), + Ok(Int128::from(2u32)), + ); + assert!(matches!( + Int128::MIN.checked_sub(Int128::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(2u32).checked_sub(Int128::from(1u32)), + Ok(Int128::from(1u32)), + ); + assert!(matches!( + Int128::MAX.checked_mul(Int128::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int128::from(2u32).checked_mul(Int128::from(2u32)), + Ok(Int128::from(4u32)), + ); + assert!(matches!( + Int128::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int128::from(2u32).checked_pow(3u32), Ok(Int128::from(8u32)),); + assert_eq!( + Int128::MAX.checked_div(Int128::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int128::from(6u32).checked_div(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert_eq!( + Int128::MAX.checked_div_euclid(Int128::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int128::from(6u32).checked_div_euclid(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert_eq!( + Int128::from(7u32).checked_div_euclid(Int128::from(2u32)), + Ok(Int128::from(3u32)), + ); + assert!(matches!( + Int128::MAX.checked_rem(Int128::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int128::from(-12i32).checked_div(Int128::from(10i32)), + Ok(Int128::from(-1i32)), + ); + assert_eq!( + Int128::from(-2i32).checked_pow(3u32), + Ok(Int128::from(-8i32)), + ); + assert_eq!( + Int128::from(-6i32).checked_mul(Int128::from(-7i32)), + Ok(Int128::from(42i32)), + ); + assert_eq!( + Int128::from(-2i32).checked_add(Int128::from(3i32)), + Ok(Int128::from(1i32)), + ); + assert_eq!( + Int128::from(-1i32).checked_div_euclid(Int128::from(-2i32)), + Ok(Int128::from(1u32)), + ); + + // saturating_* + assert_eq!(Int128::MAX.saturating_add(Int128::from(1u32)), Int128::MAX); + assert_eq!(Int128::MIN.saturating_sub(Int128::from(1u32)), Int128::MIN); + assert_eq!(Int128::MAX.saturating_mul(Int128::from(2u32)), Int128::MAX); + assert_eq!(Int128::from(4u32).saturating_pow(2u32), Int128::from(16u32)); + assert_eq!(Int128::MAX.saturating_pow(2u32), Int128::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int128_implements_rem() { + let a = Int128::from(10u32); + assert_eq!(a % Int128::from(10u32), Int128::zero()); + assert_eq!(a % Int128::from(2u32), Int128::zero()); + assert_eq!(a % Int128::from(1u32), Int128::zero()); + assert_eq!(a % Int128::from(3u32), Int128::from(1u32)); + assert_eq!(a % Int128::from(4u32), Int128::from(2u32)); + + assert_eq!( + Int128::from(-12i32) % Int128::from(10i32), + Int128::from(-2i32) + ); + assert_eq!( + Int128::from(12i32) % Int128::from(-10i32), + Int128::from(2i32) + ); + assert_eq!( + Int128::from(-12i32) % Int128::from(-10i32), + Int128::from(-2i32) + ); + + // works for refs + let a = Int128::from(10u32); + let b = Int128::from(3u32); + let expected = Int128::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int128_rem_panics_for_zero() { + let _ = Int128::from(10u32) % Int128::zero(); + } + + #[test] + fn int128_rem_assign_works() { + let mut a = Int128::from(30u32); + a %= Int128::from(4u32); + assert_eq!(a, Int128::from(2u32)); + + // works for refs + let mut a = Int128::from(25u32); + let b = Int128::from(6u32); + a %= &b; + assert_eq!(a, Int128::from(1u32)); + } + + #[test] + fn int128_shr() { + let x: Int128 = 0x4000_0000_0000_0000_0000_0000_0000_0000i128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int128::from(0x2000_0000_0000_0000_0000_0000_0000_0000i128) + ); + assert_eq!( + x >> 4, + Int128::from(0x0400_0000_0000_0000_0000_0000_0000_0000i128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int128::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int128::one() + ); + } + + #[test] + fn int128_shl() { + let x: Int128 = 0x0800_0000_0000_0000_0000_0000_0000_0000i128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int128::from(0x1000_0000_0000_0000_0000_0000_0000_0000i128) + ); + assert_eq!( + x << 4, + Int128::from(0x0800_0000_0000_0000_0000_0000_0000_0000i128 << 4) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int128::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int128::MIN + ); + } + + #[test] + fn int128_abs_diff_works() { + let a = Int128::from(42u32); + let b = Int128::from(5u32); + let expected = Uint128::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int128::from(-5i32); + assert_eq!(b.abs_diff(c), Uint128::from(10u32)); + assert_eq!(c.abs_diff(b), Uint128::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int128_neg_min_panics() { + _ = -Int128::MIN; + } + + #[test] + fn int128_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int128::from(lhs), Int128::from(rhs), expected) + }); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } +} diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs new file mode 100644 index 0000000000..937cf8df80 --- /dev/null +++ b/packages/std/src/math/int256.rs @@ -0,0 +1,1189 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Int128, Int64, Uint128, Uint256, Uint64}; + +/// Used internally - we don't want to leak this type since we might change +/// the implementation in the future. +use bnum::types::{I256, U256}; + +/// An implementation of i256 that is using strings for JSON encoding/decoding, +/// such that the full i256 range can be used for clients that convert JSON numbers to floats, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int256; +/// let a = Int256::from(258u128); +/// let b = Int256::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int256(#[schemars(with = "String")] I256); + +forward_ref_partial_eq!(Int256, Int256); + +impl Int256 { + pub const MAX: Int256 = Int256(I256::MAX); + pub const MIN: Int256 = Int256(I256::MIN); + + /// Creates a Int256(value) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 32]) -> Self { + Self::from_be_bytes(value) + } + + /// Creates a Int256(0) + #[inline] + pub const fn zero() -> Self { + Int256(I256::ZERO) + } + + /// Creates a Int256(1) + #[inline] + pub const fn one() -> Self { + Self(I256::ONE) + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 32]) -> Self { + let words: [u64; 4] = [ + u64::from_le_bytes([ + data[31], data[30], data[29], data[28], data[27], data[26], data[25], data[24], + ]), + u64::from_le_bytes([ + data[23], data[22], data[21], data[20], data[19], data[18], data[17], data[16], + ]), + u64::from_le_bytes([ + data[15], data[14], data[13], data[12], data[11], data[10], data[9], data[8], + ]), + u64::from_le_bytes([ + data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], + ]), + ]; + Self(I256::from_bits(U256::from_digits(words))) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 32]) -> Self { + let words: [u64; 4] = [ + u64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]), + u64::from_le_bytes([ + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], + ]), + u64::from_le_bytes([ + data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], + ]), + u64::from_le_bytes([ + data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], + ]), + ]; + Self(I256::from_bits(U256::from_digits(words))) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 32] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 32] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + Self(self.0.pow(exp)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 256 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 256 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint256 { + Uint256(self.0.abs_diff(other.0)) + } +} + +impl From for Int256 { + fn from(val: Uint128) -> Self { + val.u128().into() + } +} + +impl From for Int256 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int256 { + fn from(val: u128) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u64) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u32) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u16) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: u8) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: Int128) -> Self { + val.i128().into() + } +} + +impl From for Int256 { + fn from(val: Int64) -> Self { + val.i64().into() + } +} + +impl From for Int256 { + fn from(val: i128) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i64) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i32) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i16) -> Self { + Int256(val.into()) + } +} + +impl From for Int256 { + fn from(val: i8) -> Self { + Int256(val.into()) + } +} + +impl TryFrom<&str> for Int256 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int256 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match I256::from_str_radix(s, 10) { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int256: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int256) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The inner type doesn't work as expected with padding, so we + // work around that. Remove this code when the upstream padding is fixed. + let unpadded = self.0.to_string(); + let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); + + f.pad_integral(self >= &Self::zero(), "", numeric) + } +} + +impl Add for Int256 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int256(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int256, Int256); + +impl Sub for Int256 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int256(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int256, Int256); + +impl SubAssign for Int256 { + fn sub_assign(&mut self, rhs: Int256) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int256, Int256); + +impl Div for Int256 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int256, Int256); + +impl Rem for Int256 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero. + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for Int256, Int256); + +impl Not for Int256 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int256 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int256 { + fn rem_assign(&mut self, rhs: Int256) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int256, Int256); + +impl Mul for Int256 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int256, Int256); + +impl MulAssign for Int256 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int256, Int256); + +impl Shr for Int256 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int256", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int256, u32); + +impl Shl for Int256 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int256", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int256, u32); + +impl AddAssign for Int256 { + fn add_assign(&mut self, rhs: Int256) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int256, Int256); + +impl DivAssign for Int256 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int256, Int256); + +impl ShrAssign for Int256 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int256, u32); + +impl ShlAssign for Int256 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int256, u32); + +impl Serialize for Int256 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int256 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int256Visitor) + } +} + +struct Int256Visitor; + +impl<'de> de::Visitor<'de> for Int256Visitor { + type Value = Int256; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int256::try_from(v).map_err(|e| E::custom(format!("invalid Int256 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int256 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 32); + } + + #[test] + fn int256_new_works() { + let num = Int256::new([1; 32]); + let a: [u8; 32] = num.to_be_bytes(); + assert_eq!(a, [1; 32]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int256::new(be_bytes); + let resulting_bytes: [u8; 32] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int256_zero_works() { + let zero = Int256::zero(); + assert_eq!(zero.to_be_bytes(), [0; 32]); + } + + #[test] + fn uint256_one_works() { + let one = Int256::one(); + let mut one_be = [0; 32]; + one_be[31] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int256_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int256::new(be_bytes); + let num2 = Int256::from_be_bytes(be_bytes); + let num3 = Int256::from_le_bytes(le_bytes); + assert_eq!(num1, Int256::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[test] + fn int256_convert_from() { + let a = Int256::from(5u128); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u64); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u32); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u16); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(5u8); + assert_eq!(a.0, I256::from(5u32)); + + let a = Int256::from(-5i128); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i64); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i32); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i16); + assert_eq!(a.0, I256::from(-5i32)); + + let a = Int256::from(-5i8); + assert_eq!(a.0, I256::from(-5i32)); + + let result = Int256::try_from("34567"); + assert_eq!( + result.unwrap().0, + I256::from_str_radix("34567", 10).unwrap() + ); + + let result = Int256::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int256_implements_display() { + let a = Int256::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int256::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int256::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int256_display_padding_works() { + let a = Int256::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int256::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int256_to_be_bytes_works() { + assert_eq!(Int256::zero().to_be_bytes(), [0; 32]); + + let mut max = [0xff; 32]; + max[0] = 0x7f; + assert_eq!(Int256::MAX.to_be_bytes(), max); + + let mut one = [0; 32]; + one[31] = 1; + assert_eq!(Int256::from(1u128).to_be_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(32, "big")]` + assert_eq!( + Int256::from(240282366920938463463374607431768124608u128).to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 196, 179, 87, 165, 121, 59, + 133, 246, 117, 221, 191, 255, 254, 172, 192 + ] + ); + assert_eq!( + Int256::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ]) + .to_be_bytes(), + [ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ] + ); + } + + #[test] + fn int256_to_le_bytes_works() { + assert_eq!(Int256::zero().to_le_bytes(), [0; 32]); + + let mut max = [0xff; 32]; + max[31] = 0x7f; + assert_eq!(Int256::MAX.to_le_bytes(), max); + + let mut one = [0; 32]; + one[0] = 1; + assert_eq!(Int256::from(1u128).to_le_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "little")]` + assert_eq!( + Int256::from(240282366920938463463374607431768124608u128).to_le_bytes(), + [ + 192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); + assert_eq!( + Int256::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240 + ]) + .to_le_bytes(), + [ + 240, 150, 115, 200, 240, 2, 233, 42, 7, 192, 201, 211, 54, 65, 76, 87, 78, 67, 21, + 33, 38, 0, 91, 58, 200, 123, 67, 87, 32, 23, 4, 17 + ] + ); + } + + #[test] + fn int256_is_zero_works() { + assert!(Int256::zero().is_zero()); + assert!(Int256(I256::from(0u32)).is_zero()); + + assert!(!Int256::from(1u32).is_zero()); + assert!(!Int256::from(123u32).is_zero()); + assert!(!Int256::from(-123i32).is_zero()); + } + + #[test] + fn int256_wrapping_methods() { + // wrapping_add + assert_eq!( + Int256::from(2u32).wrapping_add(Int256::from(2u32)), + Int256::from(4u32) + ); // non-wrapping + assert_eq!(Int256::MAX.wrapping_add(Int256::from(1u32)), Int256::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int256::from(7u32).wrapping_sub(Int256::from(5u32)), + Int256::from(2u32) + ); // non-wrapping + assert_eq!(Int256::MIN.wrapping_sub(Int256::from(1u32)), Int256::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int256::from(3u32).wrapping_mul(Int256::from(2u32)), + Int256::from(6u32) + ); // non-wrapping + assert_eq!( + Int256::MAX.wrapping_mul(Int256::from(2u32)), + Int256::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int256::from(2u32).wrapping_pow(3), Int256::from(8u32)); // non-wrapping + assert_eq!(Int256::MAX.wrapping_pow(2), Int256::from(1u32)); // wrapping + } + + #[test] + fn int256_json() { + let orig = Int256::from(1234567890987654321u128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int256 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int256_compare() { + let a = Int256::from(12345u32); + let b = Int256::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int256::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_math() { + let a = Int256::from(-12345i32); + let b = Int256::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int256::from(11111u32)); + assert_eq!(a + &b, Int256::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int256::from(35801u32)); + assert_eq!(b - &a, Int256::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int256::from(300000u32); + c += b; + assert_eq!(c, Int256::from(323456u32)); + let mut d = Int256::from(300000u32); + d += &b; + assert_eq!(d, Int256::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int256::from(300000u32); + c -= b; + assert_eq!(c, Int256::from(276544u32)); + let mut d = Int256::from(300000u32); + d -= &b; + assert_eq!(d, Int256::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int256::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int256_add_overflow_panics() { + let _ = Int256::MAX + Int256::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_sub_works() { + assert_eq!(Int256::from(2u32) - Int256::from(1u32), Int256::from(1u32)); + assert_eq!(Int256::from(2u32) - Int256::from(0u32), Int256::from(2u32)); + assert_eq!(Int256::from(2u32) - Int256::from(2u32), Int256::from(0u32)); + assert_eq!(Int256::from(2u32) - Int256::from(3u32), Int256::from(-1i32)); + + // works for refs + let a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int256_sub_overflow_panics() { + let _ = Int256::MIN + Int256::one() - Int256::from(2u32); + } + + #[test] + fn int256_sub_assign_works() { + let mut a = Int256::from(14u32); + a -= Int256::from(2u32); + assert_eq!(a, Int256::from(12u32)); + + // works for refs + let mut a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_mul_works() { + assert_eq!(Int256::from(2u32) * Int256::from(3u32), Int256::from(6u32)); + assert_eq!(Int256::from(2u32) * Int256::zero(), Int256::zero()); + + // works for refs + let a = Int256::from(11u32); + let b = Int256::from(3u32); + let expected = Int256::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int256_mul_assign_works() { + let mut a = Int256::from(14u32); + a *= Int256::from(2u32); + assert_eq!(a, Int256::from(28u32)); + + // works for refs + let mut a = Int256::from(10u32); + let b = Int256::from(3u32); + a *= &b; + assert_eq!(a, Int256::from(30u32)); + } + + #[test] + fn int256_pow_works() { + assert_eq!(Int256::from(2u32).pow(2), Int256::from(4u32)); + assert_eq!(Int256::from(2u32).pow(10), Int256::from(1024u32)); + } + + #[test] + #[should_panic] + fn int256_pow_overflow_panics() { + _ = Int256::MAX.pow(2u32); + } + + #[test] + fn int256_shr_works() { + let original = Int256::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int256::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int256_shr_overflow_panics() { + let _ = Int256::from(1u32) >> 256u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int256::from(17u32), + Int256::from(123u32), + Int256::from(540u32), + Int256::from(82u32), + ]; + let expected = Int256::from(762u32); + + let sum_as_ref: Int256 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int256 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int256_methods() { + // checked_* + assert!(matches!( + Int256::MAX.checked_add(Int256::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(1u32).checked_add(Int256::from(1u32)), + Ok(Int256::from(2u32)), + ); + assert!(matches!( + Int256::MIN.checked_sub(Int256::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(2u32).checked_sub(Int256::from(1u32)), + Ok(Int256::from(1u32)), + ); + assert!(matches!( + Int256::MAX.checked_mul(Int256::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int256::from(2u32).checked_mul(Int256::from(2u32)), + Ok(Int256::from(4u32)), + ); + assert!(matches!( + Int256::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int256::from(2u32).checked_pow(3u32), Ok(Int256::from(8u32)),); + assert_eq!( + Int256::MAX.checked_div(Int256::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int256::from(6u32).checked_div(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert_eq!( + Int256::MAX.checked_div_euclid(Int256::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int256::from(6u32).checked_div_euclid(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert_eq!( + Int256::from(7u32).checked_div_euclid(Int256::from(2u32)), + Ok(Int256::from(3u32)), + ); + assert!(matches!( + Int256::MAX.checked_rem(Int256::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int256::from(-12i32).checked_div(Int256::from(10i32)), + Ok(Int256::from(-1i32)), + ); + assert_eq!( + Int256::from(-2i32).checked_pow(3u32), + Ok(Int256::from(-8i32)), + ); + assert_eq!( + Int256::from(-6i32).checked_mul(Int256::from(-7i32)), + Ok(Int256::from(42i32)), + ); + assert_eq!( + Int256::from(-2i32).checked_add(Int256::from(3i32)), + Ok(Int256::from(1i32)), + ); + assert_eq!( + Int256::from(-1i32).checked_div_euclid(Int256::from(-2i32)), + Ok(Int256::from(1u32)), + ); + + // saturating_* + assert_eq!(Int256::MAX.saturating_add(Int256::from(1u32)), Int256::MAX); + assert_eq!(Int256::MIN.saturating_sub(Int256::from(1u32)), Int256::MIN); + assert_eq!(Int256::MAX.saturating_mul(Int256::from(2u32)), Int256::MAX); + assert_eq!(Int256::from(4u32).saturating_pow(2u32), Int256::from(16u32)); + assert_eq!(Int256::MAX.saturating_pow(2u32), Int256::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int256_implements_rem() { + let a = Int256::from(10u32); + assert_eq!(a % Int256::from(10u32), Int256::zero()); + assert_eq!(a % Int256::from(2u32), Int256::zero()); + assert_eq!(a % Int256::from(1u32), Int256::zero()); + assert_eq!(a % Int256::from(3u32), Int256::from(1u32)); + assert_eq!(a % Int256::from(4u32), Int256::from(2u32)); + + assert_eq!( + Int256::from(-12i32) % Int256::from(10i32), + Int256::from(-2i32) + ); + assert_eq!( + Int256::from(12i32) % Int256::from(-10i32), + Int256::from(2i32) + ); + assert_eq!( + Int256::from(-12i32) % Int256::from(-10i32), + Int256::from(-2i32) + ); + + // works for refs + let a = Int256::from(10u32); + let b = Int256::from(3u32); + let expected = Int256::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int256_rem_panics_for_zero() { + let _ = Int256::from(10u32) % Int256::zero(); + } + + #[test] + fn int256_rem_assign_works() { + let mut a = Int256::from(30u32); + a %= Int256::from(4u32); + assert_eq!(a, Int256::from(2u32)); + + // works for refs + let mut a = Int256::from(25u32); + let b = Int256::from(6u32); + a %= &b; + assert_eq!(a, Int256::from(1u32)); + } + + #[test] + fn int256_shr() { + let x: Int256 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int256::from(0x4000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x >> 4, + Int256::from(0x0800_0000_0000_0000_0000_0000_0000_0000u128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int256::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int256::one() + ); + } + + #[test] + fn int256_shl() { + let x: Int256 = 0x0800_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int256::from(0x1000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x << 4, + Int256::from(0x8000_0000_0000_0000_0000_0000_0000_0000u128) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int256::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int256::MIN + ); + } + + #[test] + fn int256_abs_diff_works() { + let a = Int256::from(42u32); + let b = Int256::from(5u32); + let expected = Uint256::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int256::from(-5i32); + assert_eq!(b.abs_diff(c), Uint256::from(10u32)); + assert_eq!(c.abs_diff(b), Uint256::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int256_neg_min_panics() { + _ = -Int256::MIN; + } + + #[test] + fn int256_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int256::from(lhs), Int256::from(rhs), expected) + }); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } +} diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs new file mode 100644 index 0000000000..980b81ee5c --- /dev/null +++ b/packages/std/src/math/int512.rs @@ -0,0 +1,1242 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint512, Uint64}; + +/// Used internally - we don't want to leak this type since we might change +/// the implementation in the future. +use bnum::types::{I512, U512}; + +/// An implementation of i512 that is using strings for JSON encoding/decoding, +/// such that the full i512 range can be used for clients that convert JSON numbers to floats, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int512; +/// let a = Int512::from(258u128); +/// let b = Int512::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int512(#[schemars(with = "String")] I512); + +forward_ref_partial_eq!(Int512, Int512); + +impl Int512 { + pub const MAX: Int512 = Int512(I512::MAX); + pub const MIN: Int512 = Int512(I512::MIN); + + /// Creates a Int512(value) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 64]) -> Self { + Self::from_be_bytes(value) + } + + /// Creates a Int512(0) + #[inline] + pub const fn zero() -> Self { + Int512(I512::ZERO) + } + + /// Creates a Int512(1) + #[inline] + pub const fn one() -> Self { + Self(I512::ONE) + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 64]) -> Self { + let words: [u64; 8] = [ + u64::from_le_bytes([ + data[63], data[62], data[61], data[60], data[59], data[58], data[57], data[56], + ]), + u64::from_le_bytes([ + data[55], data[54], data[53], data[52], data[51], data[50], data[49], data[48], + ]), + u64::from_le_bytes([ + data[47], data[46], data[45], data[44], data[43], data[42], data[41], data[40], + ]), + u64::from_le_bytes([ + data[39], data[38], data[37], data[36], data[35], data[34], data[33], data[32], + ]), + u64::from_le_bytes([ + data[31], data[30], data[29], data[28], data[27], data[26], data[25], data[24], + ]), + u64::from_le_bytes([ + data[23], data[22], data[21], data[20], data[19], data[18], data[17], data[16], + ]), + u64::from_le_bytes([ + data[15], data[14], data[13], data[12], data[11], data[10], data[9], data[8], + ]), + u64::from_le_bytes([ + data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], + ]), + ]; + Self(I512::from_bits(U512::from_digits(words))) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 64]) -> Self { + let words: [u64; 8] = [ + u64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]), + u64::from_le_bytes([ + data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], + ]), + u64::from_le_bytes([ + data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23], + ]), + u64::from_le_bytes([ + data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], + ]), + u64::from_le_bytes([ + data[32], data[33], data[34], data[35], data[36], data[37], data[38], data[39], + ]), + u64::from_le_bytes([ + data[40], data[41], data[42], data[43], data[44], data[45], data[46], data[47], + ]), + u64::from_le_bytes([ + data[48], data[49], data[50], data[51], data[52], data[53], data[54], data[55], + ]), + u64::from_le_bytes([ + data[56], data[57], data[58], data[59], data[60], data[61], data[62], data[63], + ]), + ]; + Self(I512::from_bits(U512::from_digits(words))) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 64] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[7].to_be_bytes(), + words[6].to_be_bytes(), + words[5].to_be_bytes(), + words[4].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 64] { + let bits = self.0.to_bits(); + let words = bits.digits(); + let words = [ + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + words[4].to_le_bytes(), + words[5].to_le_bytes(), + words[6].to_le_bytes(), + words[7].to_le_bytes(), + ]; + unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0.is_zero() + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + Self(self.0.pow(exp)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint512 { + Uint512(self.0.abs_diff(other.0)) + } +} + +impl From for Int512 { + fn from(val: Uint256) -> Self { + let mut bytes = [0u8; 64]; + bytes[32..].copy_from_slice(&val.to_be_bytes()); + + Self::from_be_bytes(bytes) + } +} + +impl From for Int512 { + fn from(val: Uint128) -> Self { + val.u128().into() + } +} + +impl From for Int512 { + fn from(val: Uint64) -> Self { + val.u64().into() + } +} + +impl From for Int512 { + fn from(val: u128) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u64) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u32) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u16) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: u8) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i128) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i64) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i32) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i16) -> Self { + Int512(val.into()) + } +} + +impl From for Int512 { + fn from(val: i8) -> Self { + Int512(val.into()) + } +} + +impl TryFrom<&str> for Int512 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int512 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match I512::from_str_radix(s, 10) { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int512: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int512) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int512 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // The inner type doesn't work as expected with padding, so we + // work around that. Remove this code when the upstream padding is fixed. + let unpadded = self.0.to_string(); + let numeric = unpadded.strip_prefix('-').unwrap_or(&unpadded); + + f.pad_integral(self >= &Self::zero(), "", numeric) + } +} + +impl Add for Int512 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int512(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int512, Int512); + +impl Sub for Int512 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int512(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int512, Int512); + +impl SubAssign for Int512 { + fn sub_assign(&mut self, rhs: Int512) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int512, Int512); + +impl Div for Int512 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int512, Int512); + +impl Rem for Int512 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero. + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for Int512, Int512); + +impl Not for Int512 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int512 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int512 { + fn rem_assign(&mut self, rhs: Int512) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int512, Int512); + +impl Mul for Int512 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int512, Int512); + +impl MulAssign for Int512 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int512, Int512); + +impl Shr for Int512 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int512", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int512, u32); + +impl Shl for Int512 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int512", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int512, u32); + +impl AddAssign for Int512 { + fn add_assign(&mut self, rhs: Int512) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int512, Int512); + +impl DivAssign for Int512 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int512, Int512); + +impl ShrAssign for Int512 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int512, u32); + +impl ShlAssign for Int512 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int512, u32); + +impl Serialize for Int512 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int512 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int512Visitor) + } +} + +struct Int512Visitor; + +impl<'de> de::Visitor<'de> for Int512Visitor { + type Value = Int512; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int512::try_from(v).map_err(|e| E::custom(format!("invalid Int512 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int512 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 64); + } + + #[test] + fn int512_new_works() { + let num = Int512::new([1; 64]); + let a: [u8; 64] = num.to_be_bytes(); + assert_eq!(a, [1; 64]); + + let be_bytes = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int512::new(be_bytes); + let resulting_bytes: [u8; 64] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int512_zero_works() { + let zero = Int512::zero(); + assert_eq!(zero.to_be_bytes(), [0; 64]); + } + + #[test] + fn uint512_one_works() { + let one = Int512::one(); + let mut one_be = [0; 64]; + one_be[63] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int512_endianness() { + let be_bytes = [ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let le_bytes = [ + 3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]; + + // These should all be the same. + let num1 = Int512::new(be_bytes); + let num2 = Int512::from_be_bytes(be_bytes); + let num3 = Int512::from_le_bytes(le_bytes); + assert_eq!(num1, Int512::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[test] + fn int512_convert_from() { + let a = Int512::from(5u128); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u64); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u32); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u16); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(5u8); + assert_eq!(a.0, I512::from(5u32)); + + let a = Int512::from(-5i128); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i64); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i32); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i16); + assert_eq!(a.0, I512::from(-5i32)); + + let a = Int512::from(-5i8); + assert_eq!(a.0, I512::from(-5i32)); + + let result = Int512::try_from("34567"); + assert_eq!( + result.unwrap().0, + I512::from_str_radix("34567", 10).unwrap() + ); + + let result = Int512::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int512_implements_display() { + let a = Int512::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int512::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int512::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int512_display_padding_works() { + let a = Int512::from(123u64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int512::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int512_to_be_bytes_works() { + assert_eq!(Int512::zero().to_be_bytes(), [0; 64]); + + let mut max = [0xff; 64]; + max[0] = 0x7f; + assert_eq!(Int512::MAX.to_be_bytes(), max); + + let mut one = [0; 64]; + one[63] = 1; + assert_eq!(Int512::from(1u128).to_be_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "big")]` + assert_eq!( + Int512::from(240282366920938463463374607431768124608u128).to_be_bytes(), + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 196, 179, 87, 165, + 121, 59, 133, 246, 117, 221, 191, 255, 254, 172, 192 + ] + ); + assert_eq!( + Int512::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ]) + .to_be_bytes(), + [ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ] + ); + } + + #[test] + fn int512_to_le_bytes_works() { + assert_eq!(Int512::zero().to_le_bytes(), [0; 64]); + + let mut max = [0xff; 64]; + max[63] = 0x7f; + assert_eq!(Int512::MAX.to_le_bytes(), max); + + let mut one = [0; 64]; + one[0] = 1; + assert_eq!(Int512::from(1u128).to_le_bytes(), one); + // Python: `[b for b in (240282366920938463463374607431768124608).to_bytes(64, "little")]` + assert_eq!( + Int512::from(240282366920938463463374607431768124608u128).to_le_bytes(), + [ + 192, 172, 254, 255, 191, 221, 117, 246, 133, 59, 121, 165, 87, 179, 196, 180, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + assert_eq!( + Int512::from_be_bytes([ + 17, 4, 23, 32, 87, 67, 123, 200, 58, 91, 0, 38, 33, 21, 67, 78, 87, 76, 65, 54, + 211, 201, 192, 7, 42, 233, 2, 240, 200, 115, 150, 240, 218, 88, 106, 45, 208, 134, + 238, 119, 85, 22, 14, 88, 166, 195, 154, 73, 64, 10, 44, 59, 13, 22, 47, 12, 99, 8, + 252, 96, 230, 187, 38, 29 + ]) + .to_le_bytes(), + [ + 29, 38, 187, 230, 96, 252, 8, 99, 12, 47, 22, 13, 59, 44, 10, 64, 73, 154, 195, + 166, 88, 14, 22, 85, 119, 238, 134, 208, 45, 106, 88, 218, 240, 150, 115, 200, 240, + 2, 233, 42, 7, 192, 201, 211, 54, 65, 76, 87, 78, 67, 21, 33, 38, 0, 91, 58, 200, + 123, 67, 87, 32, 23, 4, 17 + ] + ); + } + + #[test] + fn int512_is_zero_works() { + assert!(Int512::zero().is_zero()); + assert!(Int512(I512::from(0u32)).is_zero()); + + assert!(!Int512::from(1u32).is_zero()); + assert!(!Int512::from(123u32).is_zero()); + assert!(!Int512::from(-123i32).is_zero()); + } + + #[test] + fn int512_wrapping_methods() { + // wrapping_add + assert_eq!( + Int512::from(2u32).wrapping_add(Int512::from(2u32)), + Int512::from(4u32) + ); // non-wrapping + assert_eq!(Int512::MAX.wrapping_add(Int512::from(1u32)), Int512::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int512::from(7u32).wrapping_sub(Int512::from(5u32)), + Int512::from(2u32) + ); // non-wrapping + assert_eq!(Int512::MIN.wrapping_sub(Int512::from(1u32)), Int512::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int512::from(3u32).wrapping_mul(Int512::from(2u32)), + Int512::from(6u32) + ); // non-wrapping + assert_eq!( + Int512::MAX.wrapping_mul(Int512::from(2u32)), + Int512::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int512::from(2u32).wrapping_pow(3), Int512::from(8u32)); // non-wrapping + assert_eq!(Int512::MAX.wrapping_pow(2), Int512::from(1u32)); // wrapping + } + + #[test] + fn int512_json() { + let orig = Int512::from(1234567890987654321u128); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int512 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int512_compare() { + let a = Int512::from(12345u32); + let b = Int512::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int512::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_math() { + let a = Int512::from(-12345i32); + let b = Int512::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int512::from(11111u32)); + assert_eq!(a + &b, Int512::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int512::from(35801u32)); + assert_eq!(b - &a, Int512::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int512::from(300000u32); + c += b; + assert_eq!(c, Int512::from(323456u32)); + let mut d = Int512::from(300000u32); + d += &b; + assert_eq!(d, Int512::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int512::from(300000u32); + c -= b; + assert_eq!(c, Int512::from(276544u32)); + let mut d = Int512::from(300000u32); + d -= &b; + assert_eq!(d, Int512::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int512::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int512_add_overflow_panics() { + let _ = Int512::MAX + Int512::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_sub_works() { + assert_eq!(Int512::from(2u32) - Int512::from(1u32), Int512::from(1u32)); + assert_eq!(Int512::from(2u32) - Int512::from(0u32), Int512::from(2u32)); + assert_eq!(Int512::from(2u32) - Int512::from(2u32), Int512::from(0u32)); + assert_eq!(Int512::from(2u32) - Int512::from(3u32), Int512::from(-1i32)); + + // works for refs + let a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int512_sub_overflow_panics() { + let _ = Int512::MIN + Int512::one() - Int512::from(2u32); + } + + #[test] + fn int512_sub_assign_works() { + let mut a = Int512::from(14u32); + a -= Int512::from(2u32); + assert_eq!(a, Int512::from(12u32)); + + // works for refs + let mut a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_mul_works() { + assert_eq!(Int512::from(2u32) * Int512::from(3u32), Int512::from(6u32)); + assert_eq!(Int512::from(2u32) * Int512::zero(), Int512::zero()); + + // works for refs + let a = Int512::from(11u32); + let b = Int512::from(3u32); + let expected = Int512::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int512_mul_assign_works() { + let mut a = Int512::from(14u32); + a *= Int512::from(2u32); + assert_eq!(a, Int512::from(28u32)); + + // works for refs + let mut a = Int512::from(10u32); + let b = Int512::from(3u32); + a *= &b; + assert_eq!(a, Int512::from(30u32)); + } + + #[test] + fn int512_pow_works() { + assert_eq!(Int512::from(2u32).pow(2), Int512::from(4u32)); + assert_eq!(Int512::from(2u32).pow(10), Int512::from(1024u32)); + } + + #[test] + #[should_panic] + fn int512_pow_overflow_panics() { + _ = Int512::MAX.pow(2u32); + } + + #[test] + fn int512_shr_works() { + let original = Int512::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int512::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int512_shr_overflow_panics() { + let _ = Int512::from(1u32) >> 512u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int512::from(17u32), + Int512::from(123u32), + Int512::from(540u32), + Int512::from(82u32), + ]; + let expected = Int512::from(762u32); + + let sum_as_ref: Int512 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int512 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int512_methods() { + // checked_* + assert!(matches!( + Int512::MAX.checked_add(Int512::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(1u32).checked_add(Int512::from(1u32)), + Ok(Int512::from(2u32)), + ); + assert!(matches!( + Int512::MIN.checked_sub(Int512::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(2u32).checked_sub(Int512::from(1u32)), + Ok(Int512::from(1u32)), + ); + assert!(matches!( + Int512::MAX.checked_mul(Int512::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int512::from(2u32).checked_mul(Int512::from(2u32)), + Ok(Int512::from(4u32)), + ); + assert!(matches!( + Int512::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int512::from(2u32).checked_pow(3u32), Ok(Int512::from(8u32)),); + assert_eq!( + Int512::MAX.checked_div(Int512::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int512::from(6u32).checked_div(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert_eq!( + Int512::MAX.checked_div_euclid(Int512::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int512::from(6u32).checked_div_euclid(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert_eq!( + Int512::from(7u32).checked_div_euclid(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert!(matches!( + Int512::MAX.checked_rem(Int512::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int512::from(-12i32).checked_div(Int512::from(10i32)), + Ok(Int512::from(-1i32)), + ); + assert_eq!( + Int512::from(-2i32).checked_pow(3u32), + Ok(Int512::from(-8i32)), + ); + assert_eq!( + Int512::from(-6i32).checked_mul(Int512::from(-7i32)), + Ok(Int512::from(42i32)), + ); + assert_eq!( + Int512::from(-2i32).checked_add(Int512::from(3i32)), + Ok(Int512::from(1i32)), + ); + assert_eq!( + Int512::from(-1i32).checked_div_euclid(Int512::from(-2i32)), + Ok(Int512::from(1u32)), + ); + + // saturating_* + assert_eq!(Int512::MAX.saturating_add(Int512::from(1u32)), Int512::MAX); + assert_eq!(Int512::MIN.saturating_sub(Int512::from(1u32)), Int512::MIN); + assert_eq!(Int512::MAX.saturating_mul(Int512::from(2u32)), Int512::MAX); + assert_eq!(Int512::from(4u32).saturating_pow(2u32), Int512::from(16u32)); + assert_eq!(Int512::MAX.saturating_pow(2u32), Int512::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int512_implements_rem() { + let a = Int512::from(10u32); + assert_eq!(a % Int512::from(10u32), Int512::zero()); + assert_eq!(a % Int512::from(2u32), Int512::zero()); + assert_eq!(a % Int512::from(1u32), Int512::zero()); + assert_eq!(a % Int512::from(3u32), Int512::from(1u32)); + assert_eq!(a % Int512::from(4u32), Int512::from(2u32)); + + assert_eq!( + Int512::from(-12i32) % Int512::from(10i32), + Int512::from(-2i32) + ); + assert_eq!( + Int512::from(12i32) % Int512::from(-10i32), + Int512::from(2i32) + ); + assert_eq!( + Int512::from(-12i32) % Int512::from(-10i32), + Int512::from(-2i32) + ); + + // works for refs + let a = Int512::from(10u32); + let b = Int512::from(3u32); + let expected = Int512::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int512_rem_panics_for_zero() { + let _ = Int512::from(10u32) % Int512::zero(); + } + + #[test] + fn int512_rem_assign_works() { + let mut a = Int512::from(30u32); + a %= Int512::from(4u32); + assert_eq!(a, Int512::from(2u32)); + + // works for refs + let mut a = Int512::from(25u32); + let b = Int512::from(6u32); + a %= &b; + assert_eq!(a, Int512::from(1u32)); + } + + #[test] + fn int512_shr() { + let x: Int512 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!( + x >> 1, + Int512::from(0x4000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x >> 4, + Int512::from(0x0800_0000_0000_0000_0000_0000_0000_0000u128) + ); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int512::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int512::one() + ); + } + + #[test] + fn int512_shl() { + let x: Int512 = 0x0800_0000_0000_0000_0000_0000_0000_0000u128.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!( + x << 1, + Int512::from(0x1000_0000_0000_0000_0000_0000_0000_0000u128) + ); + assert_eq!( + x << 4, + Int512::from(0x8000_0000_0000_0000_0000_0000_0000_0000u128) + ); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int512::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int512::MIN + ); + } + + #[test] + fn int512_abs_diff_works() { + let a = Int512::from(42u32); + let b = Int512::from(5u32); + let expected = Uint512::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int512::from(-5i32); + assert_eq!(b.abs_diff(c), Uint512::from(10u32)); + assert_eq!(c.abs_diff(b), Uint512::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int512_neg_min_panics() { + _ = -Int512::MIN; + } + + #[test] + fn int512_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (u64, u64, bool)| { + (Int512::from(lhs), Int512::from(rhs), expected) + }); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } +} diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs new file mode 100644 index 0000000000..8cfbc756c6 --- /dev/null +++ b/packages/std/src/math/int64.rs @@ -0,0 +1,1047 @@ +use forward_ref::{forward_ref_binop, forward_ref_op_assign}; +use schemars::JsonSchema; +use serde::{de, ser, Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; +use crate::{forward_ref_partial_eq, Uint64}; + +/// An implementation of i64 that is using strings for JSON encoding/decoding, +/// such that the full i64 range can be used for clients that convert JSON numbers to floats, +/// like JavaScript and jq. +/// +/// # Examples +/// +/// Use `from` to create instances of this and `i64` to get the value out: +/// +/// ``` +/// # use cosmwasm_std::Int64; +/// let a = Int64::from(258i64); +/// assert_eq!(a.i64(), 258); +/// ``` +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] +pub struct Int64(#[schemars(with = "String")] i64); + +forward_ref_partial_eq!(Int64, Int64); + +impl Int64 { + pub const MAX: Int64 = Int64(i64::MAX); + pub const MIN: Int64 = Int64(i64::MIN); + + /// Creates a Int64(value). + /// + /// This method is less flexible than `from` but can be called in a const context. + #[inline] + pub const fn new(value: i64) -> Self { + Self(value) + } + + /// Creates a Int64(0) + #[inline] + pub const fn zero() -> Self { + Int64(0) + } + + /// Creates a Int64(1) + #[inline] + pub const fn one() -> Self { + Self(1) + } + + /// Returns a copy of the internal data + pub const fn i64(&self) -> i64 { + self.0 + } + + #[must_use] + pub const fn from_be_bytes(data: [u8; 8]) -> Self { + Self(i64::from_be_bytes(data)) + } + + #[must_use] + pub const fn from_le_bytes(data: [u8; 8]) -> Self { + Self(i64::from_le_bytes(data)) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; 8] { + self.0.to_be_bytes() + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; 8] { + self.0.to_le_bytes() + } + + #[must_use] + pub const fn is_zero(&self) -> bool { + self.0 == 0 + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn pow(self, exp: u32) -> Self { + Self(self.0.pow(exp)) + } + + pub fn checked_add(self, other: Self) -> Result { + self.0 + .checked_add(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Add, self, other)) + } + + pub fn checked_sub(self, other: Self) -> Result { + self.0 + .checked_sub(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Sub, self, other)) + } + + pub fn checked_mul(self, other: Self) -> Result { + self.0 + .checked_mul(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Mul, self, other)) + } + + pub fn checked_pow(self, exp: u32) -> Result { + self.0 + .checked_pow(exp) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) + } + + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivisionError::DivideByZero); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or(DivisionError::Overflow) + } + + pub fn checked_rem(self, other: Self) -> Result { + self.0 + .checked_rem(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_shr(self, other: u32) -> Result { + if other >= 64 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 64 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_add(self, other: Self) -> Self { + Self(self.0.wrapping_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_sub(self, other: Self) -> Self { + Self(self.0.wrapping_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_mul(self, other: Self) -> Self { + Self(self.0.wrapping_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] + pub fn wrapping_pow(self, other: u32) -> Self { + Self(self.0.wrapping_pow(other)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_add(self, other: Self) -> Self { + Self(self.0.saturating_add(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_sub(self, other: Self) -> Self { + Self(self.0.saturating_sub(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_mul(self, other: Self) -> Self { + Self(self.0.saturating_mul(other.0)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn saturating_pow(self, exp: u32) -> Self { + Self(self.0.saturating_pow(exp)) + } + + #[must_use = "this returns the result of the operation, without modifying the original"] + pub fn abs_diff(self, other: Self) -> Uint64 { + Uint64(self.0.abs_diff(other.0)) + } +} + +impl From for Int64 { + fn from(val: u32) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: u16) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: u8) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i64) -> Self { + Int64(val) + } +} + +impl From for Int64 { + fn from(val: i32) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i16) -> Self { + Int64(val.into()) + } +} + +impl From for Int64 { + fn from(val: i8) -> Self { + Int64(val.into()) + } +} + +impl TryFrom<&str> for Int64 { + type Error = StdError; + + fn try_from(val: &str) -> Result { + Self::from_str(val) + } +} + +impl FromStr for Int64 { + type Err = StdError; + + fn from_str(s: &str) -> Result { + match s.parse::() { + Ok(u) => Ok(Self(u)), + Err(e) => Err(StdError::generic_err(format!("Parsing Int64: {}", e))), + } + } +} + +impl From for String { + fn from(original: Int64) -> Self { + original.to_string() + } +} + +impl fmt::Display for Int64 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Add for Int64 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int64(self.0.checked_add(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Add, add for Int64, Int64); + +impl Sub for Int64 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + Int64(self.0.checked_sub(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Sub, sub for Int64, Int64); + +impl SubAssign for Int64 { + fn sub_assign(&mut self, rhs: Int64) { + self.0 = self.0.checked_sub(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl SubAssign, sub_assign for Int64, Int64); + +impl Div for Int64 { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Div, div for Int64, Int64); + +impl Rem for Int64 { + type Output = Self; + + /// # Panics + /// + /// This operation will panic if `rhs` is zero. + #[inline] + fn rem(self, rhs: Self) -> Self { + Self(self.0.rem(rhs.0)) + } +} +forward_ref_binop!(impl Rem, rem for Int64, Int64); + +impl Not for Int64 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +impl Neg for Int64 { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl RemAssign for Int64 { + fn rem_assign(&mut self, rhs: Int64) { + *self = *self % rhs; + } +} +forward_ref_op_assign!(impl RemAssign, rem_assign for Int64, Int64); + +impl Mul for Int64 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self(self.0.checked_mul(rhs.0).unwrap()) + } +} +forward_ref_binop!(impl Mul, mul for Int64, Int64); + +impl MulAssign for Int64 { + fn mul_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_mul(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl MulAssign, mul_assign for Int64, Int64); + +impl Shr for Int64 { + type Output = Self; + + fn shr(self, rhs: u32) -> Self::Output { + self.checked_shr(rhs).unwrap_or_else(|_| { + panic!( + "right shift error: {} is larger or equal than the number of bits in Int64", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shr, shr for Int64, u32); + +impl Shl for Int64 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs).unwrap_or_else(|_| { + panic!( + "left shift error: {} is larger or equal than the number of bits in Int64", + rhs, + ) + }) + } +} +forward_ref_binop!(impl Shl, shl for Int64, u32); + +impl AddAssign for Int64 { + fn add_assign(&mut self, rhs: Int64) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl AddAssign, add_assign for Int64, Int64); + +impl DivAssign for Int64 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} +forward_ref_op_assign!(impl DivAssign, div_assign for Int64, Int64); + +impl ShrAssign for Int64 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} +forward_ref_op_assign!(impl ShrAssign, shr_assign for Int64, u32); + +impl ShlAssign for Int64 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} +forward_ref_op_assign!(impl ShlAssign, shl_assign for Int64, u32); + +impl Serialize for Int64 { + /// Serializes as an integer string using base 10 + fn serialize(&self, serializer: S) -> Result + where + S: ser::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Int64 { + /// Deserialized from an integer string using base 10 + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(Int64Visitor) + } +} + +struct Int64Visitor; + +impl<'de> de::Visitor<'de> for Int64Visitor { + type Value = Int64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("string-encoded integer") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Int64::try_from(v).map_err(|e| E::custom(format!("invalid Int64 '{}' - {}", v, e))) + } +} + +impl std::iter::Sum for Int64 +where + Self: Add, +{ + fn sum>(iter: I) -> Self { + iter.fold(Self::zero(), Add::add) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{from_slice, to_vec}; + + #[test] + fn size_of_works() { + assert_eq!(std::mem::size_of::(), 8); + } + + #[test] + fn int64_from_be_bytes_works() { + let num = Int64::from_be_bytes([1; 8]); + let a: [u8; 8] = num.to_be_bytes(); + assert_eq!(a, [1; 8]); + + let be_bytes = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let num = Int64::from_be_bytes(be_bytes); + let resulting_bytes: [u8; 8] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[test] + fn int64_new_works() { + let num = Int64::new(222); + assert_eq!(num.i64(), 222); + + let num = Int64::new(-222); + assert_eq!(num.i64(), -222); + + let num = Int64::new(i64::MAX); + assert_eq!(num.i64(), i64::MAX); + + let num = Int64::new(i64::MIN); + assert_eq!(num.i64(), i64::MIN); + } + + #[test] + fn int64_zero_works() { + let zero = Int64::zero(); + assert_eq!(zero.to_be_bytes(), [0; 8]); + } + + #[test] + fn uint64_one_works() { + let one = Int64::one(); + let mut one_be = [0; 8]; + one_be[7] = 1; + + assert_eq!(one.to_be_bytes(), one_be); + } + + #[test] + fn int64_endianness() { + let be_bytes = [0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let le_bytes = [3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8]; + + // These should all be the same. + let num1 = Int64::from_be_bytes(be_bytes); + let num2 = Int64::from_le_bytes(le_bytes); + assert_eq!(num1, Int64::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + } + + #[test] + fn int64_convert_from() { + let a = Int64::from(5i64); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5i64); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u32); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u16); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(5u8); + assert_eq!(a.0, i64::from(5u32)); + + let a = Int64::from(-5i64); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i64); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i32); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i16); + assert_eq!(a.0, i64::from(-5i32)); + + let a = Int64::from(-5i8); + assert_eq!(a.0, i64::from(-5i32)); + + let result = Int64::try_from("34567"); + assert_eq!(result.unwrap().0, "34567".parse::().unwrap()); + + let result = Int64::try_from("1.23"); + assert!(result.is_err()); + } + + #[test] + fn int64_implements_display() { + let a = Int64::from(12345u32); + assert_eq!(format!("Embedded: {}", a), "Embedded: 12345"); + assert_eq!(a.to_string(), "12345"); + + let a = Int64::from(-12345i32); + assert_eq!(format!("Embedded: {}", a), "Embedded: -12345"); + assert_eq!(a.to_string(), "-12345"); + + let a = Int64::zero(); + assert_eq!(format!("Embedded: {}", a), "Embedded: 0"); + assert_eq!(a.to_string(), "0"); + } + + #[test] + fn int64_display_padding_works() { + let a = Int64::from(123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: 00123"); + + let a = Int64::from(-123i64); + assert_eq!(format!("Embedded: {:05}", a), "Embedded: -0123"); + } + + #[test] + fn int64_to_be_bytes_works() { + assert_eq!(Int64::zero().to_be_bytes(), [0; 8]); + + let mut max = [0xff; 8]; + max[0] = 0x7f; + assert_eq!(Int64::MAX.to_be_bytes(), max); + + let mut one = [0; 8]; + one[7] = 1; + assert_eq!(Int64::from(1i64).to_be_bytes(), one); + // Python: `[b for b in (8535972485454015680).to_bytes(8, "big")]` + assert_eq!( + Int64::from(8535972485454015680i64).to_be_bytes(), + [118, 117, 221, 191, 255, 254, 172, 192] + ); + assert_eq!( + Int64::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200]).to_be_bytes(), + [17, 4, 23, 32, 87, 67, 123, 200] + ); + } + + #[test] + fn int64_to_le_bytes_works() { + assert_eq!(Int64::zero().to_le_bytes(), [0; 8]); + + let mut max = [0xff; 8]; + max[7] = 0x7f; + assert_eq!(Int64::MAX.to_le_bytes(), max); + + let mut one = [0; 8]; + one[0] = 1; + assert_eq!(Int64::from(1i64).to_le_bytes(), one); + // Python: `[b for b in (8535972485454015680).to_bytes(8, "little")]` + assert_eq!( + Int64::from(8535972485454015680i64).to_le_bytes(), + [192, 172, 254, 255, 191, 221, 117, 118] + ); + assert_eq!( + Int64::from_be_bytes([17, 4, 23, 32, 87, 67, 123, 200]).to_le_bytes(), + [200, 123, 67, 87, 32, 23, 4, 17] + ); + } + + #[test] + fn int64_is_zero_works() { + assert!(Int64::zero().is_zero()); + assert!(Int64(i64::from(0u32)).is_zero()); + + assert!(!Int64::from(1u32).is_zero()); + assert!(!Int64::from(123u32).is_zero()); + assert!(!Int64::from(-123i32).is_zero()); + } + + #[test] + fn int64_wrapping_methods() { + // wrapping_add + assert_eq!( + Int64::from(2u32).wrapping_add(Int64::from(2u32)), + Int64::from(4u32) + ); // non-wrapping + assert_eq!(Int64::MAX.wrapping_add(Int64::from(1u32)), Int64::MIN); // wrapping + + // wrapping_sub + assert_eq!( + Int64::from(7u32).wrapping_sub(Int64::from(5u32)), + Int64::from(2u32) + ); // non-wrapping + assert_eq!(Int64::MIN.wrapping_sub(Int64::from(1u32)), Int64::MAX); // wrapping + + // wrapping_mul + assert_eq!( + Int64::from(3u32).wrapping_mul(Int64::from(2u32)), + Int64::from(6u32) + ); // non-wrapping + assert_eq!( + Int64::MAX.wrapping_mul(Int64::from(2u32)), + Int64::from(-2i32) + ); // wrapping + + // wrapping_pow + assert_eq!(Int64::from(2u32).wrapping_pow(3), Int64::from(8u32)); // non-wrapping + assert_eq!(Int64::MAX.wrapping_pow(2), Int64::from(1u32)); // wrapping + } + + #[test] + fn int64_json() { + let orig = Int64::from(1234567890987654321i64); + let serialized = to_vec(&orig).unwrap(); + assert_eq!(serialized.as_slice(), b"\"1234567890987654321\""); + let parsed: Int64 = from_slice(&serialized).unwrap(); + assert_eq!(parsed, orig); + } + + #[test] + fn int64_compare() { + let a = Int64::from(12345u32); + let b = Int64::from(23456u32); + + assert!(a < b); + assert!(b > a); + assert_eq!(a, Int64::from(12345u32)); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_math() { + let a = Int64::from(-12345i32); + let b = Int64::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int64::from(11111u32)); + assert_eq!(a + &b, Int64::from(11111u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int64::from(35801u32)); + assert_eq!(b - &a, Int64::from(35801u32)); + + // test += with owned and reference right hand side + let mut c = Int64::from(300000u32); + c += b; + assert_eq!(c, Int64::from(323456u32)); + let mut d = Int64::from(300000u32); + d += &b; + assert_eq!(d, Int64::from(323456u32)); + + // test -= with owned and reference right hand side + let mut c = Int64::from(300000u32); + c -= b; + assert_eq!(c, Int64::from(276544u32)); + let mut d = Int64::from(300000u32); + d -= &b; + assert_eq!(d, Int64::from(276544u32)); + + // test - with negative result + assert_eq!(a - b, Int64::from(-35801i32)); + } + + #[test] + #[should_panic] + fn int64_add_overflow_panics() { + let _ = Int64::MAX + Int64::from(12u32); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_sub_works() { + assert_eq!(Int64::from(2u32) - Int64::from(1u32), Int64::from(1u32)); + assert_eq!(Int64::from(2u32) - Int64::from(0u32), Int64::from(2u32)); + assert_eq!(Int64::from(2u32) - Int64::from(2u32), Int64::from(0u32)); + assert_eq!(Int64::from(2u32) - Int64::from(3u32), Int64::from(-1i32)); + + // works for refs + let a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(7u32); + assert_eq!(a - b, expected); + assert_eq!(a - &b, expected); + assert_eq!(&a - b, expected); + assert_eq!(&a - &b, expected); + } + + #[test] + #[should_panic] + fn int64_sub_overflow_panics() { + let _ = Int64::MIN + Int64::one() - Int64::from(2u32); + } + + #[test] + fn int64_sub_assign_works() { + let mut a = Int64::from(14u32); + a -= Int64::from(2u32); + assert_eq!(a, Int64::from(12u32)); + + // works for refs + let mut a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(7u32); + a -= &b; + assert_eq!(a, expected); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_mul_works() { + assert_eq!(Int64::from(2u32) * Int64::from(3u32), Int64::from(6u32)); + assert_eq!(Int64::from(2u32) * Int64::zero(), Int64::zero()); + + // works for refs + let a = Int64::from(11u32); + let b = Int64::from(3u32); + let expected = Int64::from(33u32); + assert_eq!(a * b, expected); + assert_eq!(a * &b, expected); + assert_eq!(&a * b, expected); + assert_eq!(&a * &b, expected); + } + + #[test] + fn int64_mul_assign_works() { + let mut a = Int64::from(14u32); + a *= Int64::from(2u32); + assert_eq!(a, Int64::from(28u32)); + + // works for refs + let mut a = Int64::from(10u32); + let b = Int64::from(3u32); + a *= &b; + assert_eq!(a, Int64::from(30u32)); + } + + #[test] + fn int64_pow_works() { + assert_eq!(Int64::from(2u32).pow(2), Int64::from(4u32)); + assert_eq!(Int64::from(2u32).pow(10), Int64::from(1024u32)); + } + + #[test] + #[should_panic] + fn int64_pow_overflow_panics() { + _ = Int64::MAX.pow(2u32); + } + + #[test] + fn int64_shr_works() { + let original = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); + + let shifted = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8]); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn int64_shr_overflow_panics() { + let _ = Int64::from(1u32) >> 64u32; + } + + #[test] + fn sum_works() { + let nums = vec![ + Int64::from(17u32), + Int64::from(123u32), + Int64::from(540u32), + Int64::from(82u32), + ]; + let expected = Int64::from(762u32); + + let sum_as_ref: Int64 = nums.iter().sum(); + assert_eq!(expected, sum_as_ref); + + let sum_as_owned: Int64 = nums.into_iter().sum(); + assert_eq!(expected, sum_as_owned); + } + + #[test] + fn int64_methods() { + // checked_* + assert!(matches!( + Int64::MAX.checked_add(Int64::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(1u32).checked_add(Int64::from(1u32)), + Ok(Int64::from(2u32)), + ); + assert!(matches!( + Int64::MIN.checked_sub(Int64::from(1u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(2u32).checked_sub(Int64::from(1u32)), + Ok(Int64::from(1u32)), + ); + assert!(matches!( + Int64::MAX.checked_mul(Int64::from(2u32)), + Err(OverflowError { .. }) + )); + assert_eq!( + Int64::from(2u32).checked_mul(Int64::from(2u32)), + Ok(Int64::from(4u32)), + ); + assert!(matches!( + Int64::MAX.checked_pow(2u32), + Err(OverflowError { .. }) + )); + assert_eq!(Int64::from(2u32).checked_pow(3u32), Ok(Int64::from(8u32)),); + assert_eq!( + Int64::MAX.checked_div(Int64::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int64::from(6u32).checked_div(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert_eq!( + Int64::MAX.checked_div_euclid(Int64::from(0u32)), + Err(DivisionError::DivideByZero) + ); + assert_eq!( + Int64::from(6u32).checked_div_euclid(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert_eq!( + Int64::from(7u32).checked_div_euclid(Int64::from(2u32)), + Ok(Int64::from(3u32)), + ); + assert!(matches!( + Int64::MAX.checked_rem(Int64::from(0u32)), + Err(DivideByZeroError { .. }) + )); + // checked_* with negative numbers + assert_eq!( + Int64::from(-12i32).checked_div(Int64::from(10i32)), + Ok(Int64::from(-1i32)), + ); + assert_eq!(Int64::from(-2i32).checked_pow(3u32), Ok(Int64::from(-8i32)),); + assert_eq!( + Int64::from(-6i32).checked_mul(Int64::from(-7i32)), + Ok(Int64::from(42i32)), + ); + assert_eq!( + Int64::from(-2i32).checked_add(Int64::from(3i32)), + Ok(Int64::from(1i32)), + ); + assert_eq!( + Int64::from(-1i32).checked_div_euclid(Int64::from(-2i32)), + Ok(Int64::from(1u32)), + ); + + // saturating_* + assert_eq!(Int64::MAX.saturating_add(Int64::from(1u32)), Int64::MAX); + assert_eq!(Int64::MIN.saturating_sub(Int64::from(1u32)), Int64::MIN); + assert_eq!(Int64::MAX.saturating_mul(Int64::from(2u32)), Int64::MAX); + assert_eq!(Int64::from(4u32).saturating_pow(2u32), Int64::from(16u32)); + assert_eq!(Int64::MAX.saturating_pow(2u32), Int64::MAX); + } + + #[test] + #[allow(clippy::op_ref)] + fn int64_implements_rem() { + let a = Int64::from(10u32); + assert_eq!(a % Int64::from(10u32), Int64::zero()); + assert_eq!(a % Int64::from(2u32), Int64::zero()); + assert_eq!(a % Int64::from(1u32), Int64::zero()); + assert_eq!(a % Int64::from(3u32), Int64::from(1u32)); + assert_eq!(a % Int64::from(4u32), Int64::from(2u32)); + + assert_eq!(Int64::from(-12i32) % Int64::from(10i32), Int64::from(-2i32)); + assert_eq!(Int64::from(12i32) % Int64::from(-10i32), Int64::from(2i32)); + assert_eq!( + Int64::from(-12i32) % Int64::from(-10i32), + Int64::from(-2i32) + ); + + // works for refs + let a = Int64::from(10u32); + let b = Int64::from(3u32); + let expected = Int64::from(1u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[test] + #[should_panic(expected = "divisor of zero")] + fn int64_rem_panics_for_zero() { + let _ = Int64::from(10u32) % Int64::zero(); + } + + #[test] + fn int64_rem_assign_works() { + let mut a = Int64::from(30u32); + a %= Int64::from(4u32); + assert_eq!(a, Int64::from(2u32)); + + // works for refs + let mut a = Int64::from(25u32); + let b = Int64::from(6u32); + a %= &b; + assert_eq!(a, Int64::from(1u32)); + } + + #[test] + fn int64_shr() { + let x: Int64 = 0x4000_0000_0000_0000i64.into(); + assert_eq!(x >> 0, x); // right shift by 0 should be no-op + assert_eq!(x >> 1, Int64::from(0x2000_0000_0000_0000i64)); + assert_eq!(x >> 4, Int64::from(0x0400_0000_0000_0000i64)); + // right shift of MIN value by the maximum shift value should result in -1 (filled with 1s) + assert_eq!( + Int64::MIN >> (std::mem::size_of::() as u32 * 8 - 1), + -Int64::one() + ); + } + + #[test] + fn int64_shl() { + let x: Int64 = 0x0800_0000_0000_0000i64.into(); + assert_eq!(x << 0, x); // left shift by 0 should be no-op + assert_eq!(x << 1, Int64::from(0x1000_0000_0000_0000i64)); + assert_eq!(x << 4, Int64::from(0x0800_0000_0000_0000i64 << 4)); + // left shift by by the maximum shift value should result in MIN + assert_eq!( + Int64::one() << (std::mem::size_of::() as u32 * 8 - 1), + Int64::MIN + ); + } + + #[test] + fn int64_abs_diff_works() { + let a = Int64::from(42u32); + let b = Int64::from(5u32); + let expected = Uint64::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + + let c = Int64::from(-5i32); + assert_eq!(b.abs_diff(c), Uint64::from(10u32)); + assert_eq!(c.abs_diff(b), Uint64::from(10u32)); + } + + #[test] + #[should_panic = "attempt to negate with overflow"] + fn int64_neg_min_panics() { + _ = -Int64::MIN; + } + + #[test] + fn int64_partial_eq() { + let test_cases = [(1, 1, true), (42, 42, true), (42, 24, false), (0, 0, true)] + .into_iter() + .map(|(lhs, rhs, expected): (i64, i64, bool)| { + (Int64::from(lhs), Int64::from(rhs), expected) + }); + + #[allow(clippy::op_ref)] + for (lhs, rhs, expected) in test_cases { + assert_eq!(lhs == rhs, expected); + assert_eq!(&lhs == rhs, expected); + assert_eq!(lhs == &rhs, expected); + assert_eq!(&lhs == &rhs, expected); + } + } +} diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 72e55d9f1d..782276af37 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,6 +1,10 @@ mod decimal; mod decimal256; mod fraction; +mod int128; +mod int256; +mod int512; +mod int64; mod isqrt; mod uint128; mod uint256; @@ -10,6 +14,10 @@ mod uint64; pub use decimal::{Decimal, DecimalRangeExceeded}; pub use decimal256::{Decimal256, Decimal256RangeExceeded}; pub use fraction::Fraction; +pub use int128::Int128; +pub use int256::Int256; +pub use int512::Int512; +pub use int64::Int64; pub use isqrt::Isqrt; pub use uint128::Uint128; pub use uint256::Uint256; @@ -64,14 +72,31 @@ mod tests { { } + trait SignedImpl<'a>: IntImpl<'a> + Neg {} + impl AllImpl<'_> for Uint64 {} impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} impl AllImpl<'_> for Uint512 {} + impl AllImpl<'_> for Int64 {} + impl AllImpl<'_> for Int128 {} + impl AllImpl<'_> for Int256 {} + impl AllImpl<'_> for Int512 {} + + impl IntImpl<'_> for Int64 {} + impl IntImpl<'_> for Int128 {} + impl IntImpl<'_> for Int256 {} + impl IntImpl<'_> for Int512 {} impl IntImpl<'_> for Uint64 {} impl IntImpl<'_> for Uint128 {} impl IntImpl<'_> for Uint256 {} impl IntImpl<'_> for Uint512 {} + impl AllImpl<'_> for Decimal {} impl AllImpl<'_> for Decimal256 {} + + impl SignedImpl<'_> for Int64 {} + impl SignedImpl<'_> for Int128 {} + impl SignedImpl<'_> for Int256 {} + impl SignedImpl<'_> for Int512 {} } diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 9c58c2cc3b..8dfd89e976 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -37,7 +37,7 @@ use crate::{ /// assert_eq!(c.u128(), 70); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint128(#[schemars(with = "String")] u128); +pub struct Uint128(#[schemars(with = "String")] pub(crate) u128); forward_ref_partial_eq!(Uint128, Uint128); diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index e074be4a45..8e329e20c7 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -14,18 +14,9 @@ use crate::errors::{ }; use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128, Uint512, Uint64}; -/// This module is purely a workaround that lets us ignore lints for all the code -/// the `construct_uint!` macro generates. -#[allow(clippy::all)] -mod uints { - uint::construct_uint! { - pub struct U256(4); - } -} - /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. -use uints::U256; +use bnum::types::U256; /// An implementation of u256 that is using strings for JSON encoding/decoding, /// such that the full u256 range can be used for clients that convert JSON numbers to floats, @@ -48,13 +39,13 @@ use uints::U256; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint256(#[schemars(with = "String")] U256); +pub struct Uint256(#[schemars(with = "String")] pub(crate) U256); forward_ref_partial_eq!(Uint256, Uint256); impl Uint256 { pub const MAX: Uint256 = Uint256(U256::MAX); - pub const MIN: Uint256 = Uint256(U256::zero()); + pub const MIN: Uint256 = Uint256(U256::ZERO); /// Creates a Uint256(value) from a big endian representation. It's just an alias for /// [`Uint256::from_be_bytes`]. @@ -67,16 +58,13 @@ impl Uint256 { /// Creates a Uint256(0) #[inline] pub const fn zero() -> Self { - Uint256(U256::zero()) + Self(U256::ZERO) } /// Creates a Uint256(1) #[inline] pub const fn one() -> Self { - Self::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, - ]) + Self(U256::ONE) } #[must_use] @@ -95,7 +83,7 @@ impl Uint256 { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], ]), ]; - Self(U256(words)) + Self(U256::from_digits(words)) } #[must_use] @@ -114,7 +102,7 @@ impl Uint256 { data[24], data[25], data[26], data[27], data[28], data[29], data[30], data[31], ]), ]; - Uint256(U256(words)) + Self(U256::from_digits(words)) } /// A conversion from `u128` that, unlike the one provided by the `From` trait, @@ -140,11 +128,12 @@ impl Uint256 { /// Returns a copy of the number as big endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 32] { + let words = self.0.digits(); let words = [ - (self.0).0[3].to_be_bytes(), - (self.0).0[2].to_be_bytes(), - (self.0).0[1].to_be_bytes(), - (self.0).0[0].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } } @@ -152,25 +141,24 @@ impl Uint256 { /// Returns a copy of the number as little endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_le_bytes(self) -> [u8; 32] { + let words = self.0.digits(); let words = [ - (self.0).0[0].to_le_bytes(), - (self.0).0[1].to_le_bytes(), - (self.0).0[2].to_le_bytes(), - (self.0).0[3].to_le_bytes(), + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 4], [u8; 32]>(words) } } #[must_use] pub const fn is_zero(&self) -> bool { - let words = (self.0).0; - words[0] == 0 && words[1] == 0 && words[2] == 0 && words[3] == 0 + self.0.is_zero() } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp.into()); - Self(res) + Self(self.0.pow(exp)) } /// Returns `self * numerator / denominator`. @@ -257,7 +245,7 @@ impl Uint256 { pub fn checked_pow(self, exp: u32) -> Result { self.0 - .checked_pow(exp.into()) + .checked_pow(exp) .map(Self) .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) } @@ -299,29 +287,25 @@ impl Uint256 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_add(other.0); - Self(value) + Self(self.0.wrapping_add(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_sub(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_sub(other.0); - Self(value) + Self(self.0.wrapping_sub(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_mul(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_mul(other.0); - Self(value) + Self(self.0.wrapping_mul(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other.into()); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -341,19 +325,12 @@ impl Uint256 { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Ok(value) => value, - Err(_) => Self::MAX, - } + Self(self.0.saturating_pow(exp)) } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn abs_diff(self, other: Self) -> Self { - if self < other { - other - self - } else { - self - other - } + Self(self.0.abs_diff(other.0)) } } @@ -427,7 +404,7 @@ impl FromStr for Uint256 { return Err(StdError::generic_err("Parsing u256: received empty string")); } - match U256::from_dec_str(s) { + match U256::from_str_radix(s, 10) { Ok(u) => Ok(Uint256(u)), Err(e) => Err(StdError::generic_err(format!("Parsing u256: {}", e))), } @@ -1047,22 +1024,25 @@ mod tests { #[test] fn uint256_convert_from() { let a = Uint256::from(5u128); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u64); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u32); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u16); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let a = Uint256::from(5u8); - assert_eq!(a.0, U256::from(5)); + assert_eq!(a.0, U256::from(5u32)); let result = Uint256::try_from("34567"); - assert_eq!(result.unwrap().0, U256::from_dec_str("34567").unwrap()); + assert_eq!( + result.unwrap().0, + U256::from_str_radix("34567", 10).unwrap() + ); let result = Uint256::try_from("1.23"); assert!(result.is_err()); @@ -1222,7 +1202,7 @@ mod tests { #[test] fn uint256_is_zero_works() { assert!(Uint256::zero().is_zero()); - assert!(Uint256(U256::from(0)).is_zero()); + assert!(Uint256(U256::from(0u32)).is_zero()); assert!(!Uint256::from(1u32).is_zero()); assert!(!Uint256::from(123u32).is_zero()); @@ -1643,7 +1623,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn uint256_rem_panics_for_zero() { let _ = Uint256::from(10u32) % Uint256::zero(); } diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index 9b5809d3d0..b3f1f278cf 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -3,8 +3,8 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use std::fmt; use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, - Sub, SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, }; use std::str::FromStr; @@ -13,18 +13,9 @@ use crate::errors::{ }; use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; -/// This module is purely a workaround that lets us ignore lints for all the code -/// the `construct_uint!` macro generates. -#[allow(clippy::all)] -mod uints { - uint::construct_uint! { - pub struct U512(8); - } -} - /// Used internally - we don't want to leak this type since we might change /// the implementation in the future. -use uints::U512; +use bnum::types::U512; /// An implementation of u512 that is using strings for JSON encoding/decoding, /// such that the full u512 range can be used for clients that convert JSON numbers to floats, @@ -51,16 +42,16 @@ use uints::U512; /// assert_eq!(a, b); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint512(#[schemars(with = "String")] U512); +pub struct Uint512(#[schemars(with = "String")] pub(crate) U512); forward_ref_partial_eq!(Uint512, Uint512); impl Uint512 { pub const MAX: Uint512 = Uint512(U512::MAX); - pub const MIN: Uint512 = Uint512(U512::zero()); + pub const MIN: Uint512 = Uint512(U512::ZERO); /// Creates a Uint512(value) from a big endian representation. It's just an alias for - /// `from_big_endian`. + /// `from_be_bytes`. pub const fn new(value: [u8; 64]) -> Self { Self::from_be_bytes(value) } @@ -68,17 +59,13 @@ impl Uint512 { /// Creates a Uint512(0) #[inline] pub const fn zero() -> Self { - Uint512(U512::zero()) + Uint512(U512::ZERO) } /// Creates a Uint512(1) #[inline] pub const fn one() -> Self { - Self::from_be_bytes([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, - ]) + Self(U512::ONE) } #[must_use] @@ -109,7 +96,7 @@ impl Uint512 { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], ]), ]; - Self(U512(words)) + Self(U512::from_digits(words)) } #[must_use] @@ -140,7 +127,7 @@ impl Uint512 { data[56], data[57], data[58], data[59], data[60], data[61], data[62], data[63], ]), ]; - Self(U512(words)) + Self(U512::from_digits(words)) } /// A conversion from `Uint256` that, unlike the one provided by the `From` trait, @@ -161,15 +148,16 @@ impl Uint512 { /// Returns a copy of the number as big endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_be_bytes(self) -> [u8; 64] { + let words = self.0.digits(); let words = [ - (self.0).0[7].to_be_bytes(), - (self.0).0[6].to_be_bytes(), - (self.0).0[5].to_be_bytes(), - (self.0).0[4].to_be_bytes(), - (self.0).0[3].to_be_bytes(), - (self.0).0[2].to_be_bytes(), - (self.0).0[1].to_be_bytes(), - (self.0).0[0].to_be_bytes(), + words[7].to_be_bytes(), + words[6].to_be_bytes(), + words[5].to_be_bytes(), + words[4].to_be_bytes(), + words[3].to_be_bytes(), + words[2].to_be_bytes(), + words[1].to_be_bytes(), + words[0].to_be_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } } @@ -177,36 +165,28 @@ impl Uint512 { /// Returns a copy of the number as little endian bytes. #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn to_le_bytes(self) -> [u8; 64] { + let words = self.0.digits(); let words = [ - (self.0).0[0].to_le_bytes(), - (self.0).0[1].to_le_bytes(), - (self.0).0[2].to_le_bytes(), - (self.0).0[3].to_le_bytes(), - (self.0).0[4].to_le_bytes(), - (self.0).0[5].to_le_bytes(), - (self.0).0[6].to_le_bytes(), - (self.0).0[7].to_le_bytes(), + words[0].to_le_bytes(), + words[1].to_le_bytes(), + words[2].to_le_bytes(), + words[3].to_le_bytes(), + words[4].to_le_bytes(), + words[5].to_le_bytes(), + words[6].to_le_bytes(), + words[7].to_le_bytes(), ]; unsafe { std::mem::transmute::<[[u8; 8]; 8], [u8; 64]>(words) } } #[must_use] pub const fn is_zero(&self) -> bool { - let words = (self.0).0; - words[0] == 0 - && words[1] == 0 - && words[2] == 0 - && words[3] == 0 - && words[4] == 0 - && words[5] == 0 - && words[6] == 0 - && words[7] == 0 + self.0.is_zero() } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn pow(self, exp: u32) -> Self { - let res = self.0.pow(exp.into()); - Self(res) + Self(self.0.pow(exp)) } pub fn checked_add(self, other: Self) -> Result { @@ -232,7 +212,7 @@ impl Uint512 { pub fn checked_pow(self, exp: u32) -> Result { self.0 - .checked_pow(exp.into()) + .checked_pow(exp) .map(Self) .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) } @@ -256,11 +236,10 @@ impl Uint512 { } pub fn checked_shr(self, other: u32) -> Result { - if other >= 512 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); - } - - Ok(Self(self.0.shr(other))) + self.0 + .checked_shr(other) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Shr, self, other)) } pub fn checked_shl(self, other: u32) -> Result { @@ -274,29 +253,25 @@ impl Uint512 { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_add(other.0); - Self(value) + Self(self.0.wrapping_add(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_sub(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_sub(other.0); - Self(value) + Self(self.0.wrapping_sub(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_mul(self, other: Self) -> Self { - let (value, _did_overflow) = self.0.overflowing_mul(other.0); - Self(value) + Self(self.0.wrapping_mul(other.0)) } #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_pow(self, other: u32) -> Self { - let (value, _did_overflow) = self.0.overflowing_pow(other.into()); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -316,19 +291,12 @@ impl Uint512 { #[must_use = "this returns the result of the operation, without modifying the original"] pub fn saturating_pow(self, exp: u32) -> Self { - match self.checked_pow(exp) { - Ok(value) => value, - Err(_) => Self::MAX, - } + Self(self.0.saturating_pow(exp)) } #[must_use = "this returns the result of the operation, without modifying the original"] pub fn abs_diff(self, other: Self) -> Self { - if self < other { - other - self - } else { - self - other - } + Self(self.0.abs_diff(other.0)) } } @@ -424,7 +392,7 @@ impl FromStr for Uint512 { type Err = StdError; fn from_str(s: &str) -> Result { - match U512::from_dec_str(s) { + match U512::from_str_radix(s, 10) { Ok(u) => Ok(Self(u)), Err(e) => Err(StdError::generic_err(format!("Parsing u512: {}", e))), } @@ -508,6 +476,14 @@ impl Rem for Uint512 { } forward_ref_binop!(impl Rem, rem for Uint512, Uint512); +impl Not for Uint512 { + type Output = Self; + + fn not(self) -> Self::Output { + Self(!self.0) + } +} + impl RemAssign for Uint512 { fn rem_assign(&mut self, rhs: Uint512) { *self = *self % rhs; @@ -743,22 +719,25 @@ mod tests { #[test] fn uint512_convert_from() { let a = Uint512::from(5u128); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u64); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u32); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u16); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let a = Uint512::from(5u8); - assert_eq!(a.0, U512::from(5)); + assert_eq!(a.0, U512::from(5u32)); let result = Uint512::try_from("34567"); - assert_eq!(result.unwrap().0, U512::from_dec_str("34567").unwrap()); + assert_eq!( + result.unwrap().0, + U512::from_str_radix("34567", 10).unwrap() + ); let result = Uint512::try_from("1.23"); assert!(result.is_err()); @@ -936,7 +915,7 @@ mod tests { #[test] fn uint512_is_zero_works() { assert!(Uint512::zero().is_zero()); - assert!(Uint512(U512::from(0)).is_zero()); + assert!(Uint512(U512::from(0u32)).is_zero()); assert!(!Uint512::from(1u32).is_zero()); assert!(!Uint512::from(123u32).is_zero()); @@ -1301,7 +1280,7 @@ mod tests { } #[test] - #[should_panic(expected = "division by zero")] + #[should_panic(expected = "divisor of zero")] fn uint512_rem_panics_for_zero() { let _ = Uint512::from(10u32) % Uint512::zero(); } diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index 9afd119647..c0a6603b5a 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -30,7 +30,7 @@ use crate::{forward_ref_partial_eq, impl_mul_fraction, Fraction, Uint128}; /// assert_eq!(b.u64(), 70); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] -pub struct Uint64(#[schemars(with = "String")] u64); +pub struct Uint64(#[schemars(with = "String")] pub(crate) u64); forward_ref_partial_eq!(Uint64, Uint64);