From 78c9adfeeb29d64e890fa45827ce5de4c9d43eb4 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 11:34:54 +0200 Subject: [PATCH 01/33] Port big uint types to bnum --- Cargo.lock | 7 ++ contracts/burner/Cargo.lock | 7 ++ contracts/crypto-verify/Cargo.lock | 7 ++ contracts/cyberpunk/Cargo.lock | 7 ++ contracts/floaty/Cargo.lock | 7 ++ contracts/hackatom/Cargo.lock | 7 ++ contracts/ibc-reflect-send/Cargo.lock | 7 ++ contracts/ibc-reflect/Cargo.lock | 7 ++ contracts/queue/Cargo.lock | 7 ++ contracts/reflect/Cargo.lock | 7 ++ contracts/staking/Cargo.lock | 7 ++ contracts/virus/Cargo.lock | 7 ++ packages/std/Cargo.toml | 1 + packages/std/src/math/decimal256.rs | 2 +- packages/std/src/math/uint256.rs | 67 ++++++++--------- packages/std/src/math/uint512.rs | 104 +++++++++++++------------- 16 files changed, 167 insertions(+), 91 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a7dafafed..dc3a1c5bf9 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", diff --git a/contracts/burner/Cargo.lock b/contracts/burner/Cargo.lock index ffc5bc3faf..129021b06e 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", diff --git a/contracts/crypto-verify/Cargo.lock b/contracts/crypto-verify/Cargo.lock index 525807cef8..a4e25b5b03 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", diff --git a/contracts/cyberpunk/Cargo.lock b/contracts/cyberpunk/Cargo.lock index b254a657ac..e6ab3c05f3 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", diff --git a/contracts/floaty/Cargo.lock b/contracts/floaty/Cargo.lock index 7903f0555d..e9932e6158 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", diff --git a/contracts/hackatom/Cargo.lock b/contracts/hackatom/Cargo.lock index 77eb0d0e6d..f7ac3df002 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", diff --git a/contracts/ibc-reflect-send/Cargo.lock b/contracts/ibc-reflect-send/Cargo.lock index 68f519d999..b5688564bc 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", diff --git a/contracts/ibc-reflect/Cargo.lock b/contracts/ibc-reflect/Cargo.lock index 06dcdd37bc..1491d26548 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", diff --git a/contracts/queue/Cargo.lock b/contracts/queue/Cargo.lock index ec0086d567..f4d4dd6354 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", diff --git a/contracts/reflect/Cargo.lock b/contracts/reflect/Cargo.lock index 90a729bb0d..8099b681ec 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", diff --git a/contracts/staking/Cargo.lock b/contracts/staking/Cargo.lock index 7d8e834d27..0886b36c00 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", diff --git a/contracts/virus/Cargo.lock b/contracts/virus/Cargo.lock index a55dc46412..d01769b280 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", diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index af772d64e4..fbec9eb810 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -55,6 +55,7 @@ serde = { version = "1.0.103", default-features = false, features = ["derive", " 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/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/uint256.rs b/packages/std/src/math/uint256.rs index e074be4a45..b770f6614b 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, @@ -54,7 +45,7 @@ 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,7 +58,7 @@ impl Uint256 { /// Creates a Uint256(0) #[inline] pub const fn zero() -> Self { - Uint256(U256::zero()) + Uint256(U256::ZERO) } /// Creates a Uint256(1) @@ -95,7 +86,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 +105,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 +131,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,24 +144,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()); + let res = self.0.pow(exp); Self(res) } @@ -257,7 +249,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)) } @@ -320,7 +312,7 @@ impl Uint256 { #[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()); + let (value, _did_overflow) = self.0.overflowing_pow(other); Self(value) } @@ -427,7 +419,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 +1039,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 +1217,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 +1638,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..c44e214b50 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, @@ -57,10 +48,10 @@ 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,7 +59,7 @@ impl Uint512 { /// Creates a Uint512(0) #[inline] pub const fn zero() -> Self { - Uint512(U512::zero()) + Uint512(U512::ZERO) } /// Creates a Uint512(1) @@ -109,7 +100,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 +131,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 +152,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,35 +169,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()); + let res = self.0.pow(exp); Self(res) } @@ -232,7 +217,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)) } @@ -295,7 +280,7 @@ impl Uint512 { #[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()); + let (value, _did_overflow) = self.0.overflowing_pow(other); Self(value) } @@ -424,7 +409,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 +493,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 +736,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 +932,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 +1297,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(); } From 6b9b234cc0f283abd12daf6f451d0a153ea5c796 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 12:07:02 +0200 Subject: [PATCH 02/33] Implement Int512 --- packages/std/src/lib.rs | 4 +- packages/std/src/math/int512.rs | 1293 +++++++++++++++++++++++++++++++ packages/std/src/math/mod.rs | 3 + 3 files changed, 1298 insertions(+), 2 deletions(-) create mode 100644 packages/std/src/math/int512.rs diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index 28dfcc9ec1..e05f1b4eb1 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, Int512, Isqrt, + Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs new file mode 100644 index 0000000000..ddf1a89675 --- /dev/null +++ b/packages/std/src/math/int512.rs @@ -0,0 +1,1293 @@ +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, Not, Rem, RemAssign, Shr, ShrAssign, Sub, + SubAssign, +}; +use std::str::FromStr; + +use crate::errors::{ + ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError, +}; +use crate::{forward_ref_partial_eq, 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::{I512, 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, +/// 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`. + 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::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, + ]) + } + + #[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))) + } + + /// A conversion from `Uint256` that, unlike the one provided by the `From` trait, + /// can be used in a `const` context. + #[must_use] + pub const fn from_uint256(num: Uint256) -> Self { + let bytes = num.to_le_bytes(); + Self::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], + bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23], + bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31], + 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, + ]) + } + + /// 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 { + let res = self.0.pow(exp); + Self(res) + } + + 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 { + self.0 + .checked_div(other.0) + .map(Self) + .ok_or_else(|| DivideByZeroError::new(self)) + } + + pub fn checked_div_euclid(self, other: Self) -> Result { + self.checked_div(other) + } + + 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))) + } + + #[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) + } + + #[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) + } + + #[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) + } + + #[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); + Self(value) + } + + #[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 { + match self.checked_pow(exp) { + Ok(value) => value, + Err(_) => Self::MAX, + } + } + + #[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 + } + } +} + +impl From for Int512 { + fn from(val: Uint256) -> Self { + let bytes = [[0u8; 32], val.to_be_bytes()].concat(); + + Self::from_be_bytes(bytes.try_into().unwrap()) + } +} + +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 for Int256 { +// type Error = ConversionOverflowError; + +// fn try_from(value: Int512) -> Result { +// let bytes = value.to_be_bytes(); +// let (first_bytes, last_bytes) = bytes.split_at(32); + +// if first_bytes != [0u8; 32] { +// return Err(ConversionOverflowError::new( +// "Int512", +// "Uint256", +// value.to_string(), +// )); +// } + +// Ok(Self::from_be_bytes(last_bytes.try_into().unwrap())) +// } +// } + +// impl TryFrom for Int128 { +// type Error = ConversionOverflowError; + +// fn try_from(value: Int512) -> Result { +// Ok(Uint128::new(value.0.try_into().map_err(|_| { +// ConversionOverflowError::new("Int512", "Uint128", value.to_string()) +// })?)) +// } +// } + +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 u512: {}", 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. + let unpadded = self.0.to_string(); + + f.pad_integral(true, "", &unpadded) + } +} + +impl Add for Int512 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Int512(self.0.checked_add(rhs.0).unwrap()) + } +} + +impl<'a> Add<&'a Int512> for Int512 { + type Output = Self; + + fn add(self, rhs: &'a Int512) -> Self { + Int512(self.0.checked_add(rhs.0).unwrap()) + } +} + +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()) + } +} + +impl<'a> Div<&'a Int512> for Int512 { + type Output = Self; + + fn div(self, rhs: &'a Int512) -> Self::Output { + Self(self.0.checked_div(rhs.0).unwrap()) + } +} + +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 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, + ) + }) + } +} + +impl<'a> Shr<&'a u32> for Int512 { + type Output = Self; + + fn shr(self, rhs: &'a u32) -> Self::Output { + Shr::::shr(self, *rhs) + } +} + +impl AddAssign for Int512 { + fn add_assign(&mut self, rhs: Int512) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} + +impl<'a> AddAssign<&'a Int512> for Int512 { + fn add_assign(&mut self, rhs: &'a Int512) { + self.0 = self.0.checked_add(rhs.0).unwrap(); + } +} + +impl DivAssign for Int512 { + fn div_assign(&mut self, rhs: Self) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} + +impl<'a> DivAssign<&'a Int512> for Int512 { + fn div_assign(&mut self, rhs: &'a Int512) { + self.0 = self.0.checked_div(rhs.0).unwrap(); + } +} + +impl ShrAssign for Int512 { + fn shr_assign(&mut self, rhs: u32) { + *self = Shr::::shr(*self, rhs); + } +} + +impl<'a> ShrAssign<&'a u32> for Int512 { + fn shr_assign(&mut self, rhs: &'a u32) { + *self = Shr::::shr(*self, *rhs); + } +} + +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, 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 + ] + ); + } + + #[test] + fn uin512_one_works() { + let one = Int512::one(); + assert_eq!( + one.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, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 + ] + ); + } + + #[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 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_convert_to_uint128() { + // let source = Int512::from(42u128); + // let target = Uint128::try_from(source); + // assert_eq!(target, Ok(Uint128::new(42u128))); + + // let source = Int512::MAX; + // let target = Uint128::try_from(source); + // assert_eq!( + // target, + // Err(ConversionOverflowError::new( + // "Int512", + // "Uint128", + // Int512::MAX.to_string() + // )) + // ); + // } + + #[test] + fn int512_from_uint256() { + assert_eq!( + Int512::from_uint256(Uint256::from_str("123").unwrap()), + Int512::from_str("123").unwrap() + ); + + assert_eq!( + Int512::from_uint256(Uint256::from_str("9785746283745").unwrap()), + Int512::from_str("9785746283745").unwrap() + ); + + assert_eq!( + Int512::from_uint256( + Uint256::from_str( + "97857462837575757832978493758398593853985452378423874623874628736482736487236" + ) + .unwrap() + ), + Int512::from_str( + "97857462837575757832978493758398593853985452378423874623874628736482736487236" + ) + .unwrap() + ); + } + + #[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::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"); + } + + #[test] + fn int512_to_be_bytes_works() { + assert_eq!( + Int512::zero().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, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ] + ); + assert_eq!( + Int512::MAX.to_be_bytes(), + [ + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ] + ); + assert_eq!( + Int512::from(1u128).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, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 + ] + ); + // 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, 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 + ] + ); + assert_eq!( + Int512::MAX.to_le_bytes(), + [ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f + ] + ); + assert_eq!( + Int512::from(1u128).to_le_bytes(), + [ + 1, 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 + ] + ); + // 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()); + } + + #[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(12345u32); + let b = Int512::from(23456u32); + + // test + with owned and reference right hand side + assert_eq!(a + b, Int512::from(35801u32)); + assert_eq!(a + &b, Int512::from(35801u32)); + + // test - with owned and reference right hand side + assert_eq!(b - a, Int512::from(11111u32)); + assert_eq!(b - &a, Int512::from(11111u32)); + + // 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(-11111i32)); + } + + #[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)); + + // 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!(matches!( + Int512::MAX.checked_div(Int512::from(0u32)), + Err(DivideByZeroError { .. }) + )); + assert_eq!( + Int512::from(6u32).checked_div(Int512::from(2u32)), + Ok(Int512::from(3u32)), + ); + assert!(matches!( + Int512::MAX.checked_div_euclid(Int512::from(0u32)), + Err(DivideByZeroError { .. }) + )); + 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 { .. }) + )); + + // 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)); + + // 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] + #[allow(clippy::op_ref)] + fn int512_rem_works() { + assert_eq!( + Int512::from(12u32) % Int512::from(10u32), + Int512::from(2u32) + ); + assert_eq!(Int512::from(50u32) % Int512::from(5u32), Int512::zero()); + + // works for refs + let a = Int512::from(42u32); + let b = Int512::from(5u32); + let expected = Int512::from(2u32); + assert_eq!(a % b, expected); + assert_eq!(a % &b, expected); + assert_eq!(&a % b, expected); + assert_eq!(&a % &b, expected); + } + + #[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_abs_diff_works() { + let a = Int512::from(42u32); + let b = Int512::from(5u32); + let expected = Int512::from(37u32); + assert_eq!(a.abs_diff(b), expected); + assert_eq!(b.abs_diff(a), expected); + } + + #[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/mod.rs b/packages/std/src/math/mod.rs index 72e55d9f1d..ee514f36b8 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,6 +1,7 @@ mod decimal; mod decimal256; mod fraction; +mod int512; mod isqrt; mod uint128; mod uint256; @@ -10,6 +11,7 @@ mod uint64; pub use decimal::{Decimal, DecimalRangeExceeded}; pub use decimal256::{Decimal256, Decimal256RangeExceeded}; pub use fraction::Fraction; +pub use int512::Int512; pub use isqrt::Isqrt; pub use uint128::Uint128; pub use uint256::Uint256; @@ -68,6 +70,7 @@ mod tests { impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} impl AllImpl<'_> for Uint512 {} + impl AllImpl<'_> for Int512 {} impl IntImpl<'_> for Uint64 {} impl IntImpl<'_> for Uint128 {} impl IntImpl<'_> for Uint256 {} From 1f157e7e7fcd0593980d9db511cd69593eee5ed9 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 15:12:11 +0200 Subject: [PATCH 03/33] Update ci rust version --- .circleci/config.yml | 144 +++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ff5bb17087..ab1f977075 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 @@ -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: From 560d1d9769d1babaf8b689cf9533133b5e62bbef Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 16:21:32 +0200 Subject: [PATCH 04/33] Disable coverage check temporarily --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ab1f977075..ddd1d20bf1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -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: From ee059cd76fb41b3509d9341ee222c08392a9a5b7 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 7 Jun 2023 16:43:35 +0200 Subject: [PATCH 05/33] Simplify uint impls --- packages/std/src/math/uint256.rs | 35 ++++++++------------------ packages/std/src/math/uint512.rs | 43 ++++++++++---------------------- 2 files changed, 23 insertions(+), 55 deletions(-) diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index b770f6614b..8e329e20c7 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -39,7 +39,7 @@ use bnum::types::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); @@ -58,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] @@ -161,8 +158,7 @@ impl Uint256 { #[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); - Self(res) + Self(self.0.pow(exp)) } /// Returns `self * numerator / denominator`. @@ -291,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); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -333,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)) } } diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index c44e214b50..b3f1f278cf 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -42,7 +42,7 @@ use bnum::types::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); @@ -65,11 +65,7 @@ impl Uint512 { /// 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] @@ -190,8 +186,7 @@ impl Uint512 { #[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); - Self(res) + Self(self.0.pow(exp)) } pub fn checked_add(self, other: Self) -> Result { @@ -241,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 { @@ -259,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); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -301,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)) } } From 77be691886a96dcd186f248805a25572da4bf5a2 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 9 Jun 2023 09:56:28 +0200 Subject: [PATCH 06/33] Cleanup int512 --- packages/std/src/math/int512.rs | 133 ++++---------------------------- 1 file changed, 14 insertions(+), 119 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index ddf1a89675..7533c8dcd8 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -8,17 +8,15 @@ use std::ops::{ }; use std::str::FromStr; -use crate::errors::{ - ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation, StdError, -}; -use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; +use crate::errors::{DivideByZeroError, 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 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, +/// 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 @@ -65,11 +63,7 @@ impl Int512 { /// Creates a Int512(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(I512::ONE) } #[must_use] @@ -134,21 +128,6 @@ impl Int512 { Self(I512::from_bits(U512::from_digits(words))) } - /// A conversion from `Uint256` that, unlike the one provided by the `From` trait, - /// can be used in a `const` context. - #[must_use] - pub const fn from_uint256(num: Uint256) -> Self { - let bytes = num.to_le_bytes(); - Self::from_le_bytes([ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], - bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], - bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23], - bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31], - 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, - ]) - } - /// 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] { @@ -192,8 +171,7 @@ impl Int512 { #[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); - Self(res) + Self(self.0.pow(exp)) } pub fn checked_add(self, other: Self) -> Result { @@ -253,22 +231,19 @@ impl Int512 { #[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"] @@ -295,19 +270,12 @@ impl Int512 { #[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 - } + pub fn abs_diff(self, other: Self) -> Uint512 { + Uint512(self.0.abs_diff(other.0)) } } @@ -391,35 +359,6 @@ impl From for Int512 { } } -// impl TryFrom for Int256 { -// type Error = ConversionOverflowError; - -// fn try_from(value: Int512) -> Result { -// let bytes = value.to_be_bytes(); -// let (first_bytes, last_bytes) = bytes.split_at(32); - -// if first_bytes != [0u8; 32] { -// return Err(ConversionOverflowError::new( -// "Int512", -// "Uint256", -// value.to_string(), -// )); -// } - -// Ok(Self::from_be_bytes(last_bytes.try_into().unwrap())) -// } -// } - -// impl TryFrom for Int128 { -// type Error = ConversionOverflowError; - -// fn try_from(value: Int512) -> Result { -// Ok(Uint128::new(value.0.try_into().map_err(|_| { -// ConversionOverflowError::new("Int512", "Uint128", value.to_string()) -// })?)) -// } -// } - impl TryFrom<&str> for Int512 { type Error = StdError; @@ -434,7 +373,7 @@ impl FromStr for Int512 { 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 u512: {}", e))), + Err(e) => Err(StdError::generic_err(format!("Parsing i512: {}", e))), } } } @@ -754,50 +693,6 @@ mod tests { assert!(result.is_err()); } - // #[test] - // fn int512_convert_to_uint128() { - // let source = Int512::from(42u128); - // let target = Uint128::try_from(source); - // assert_eq!(target, Ok(Uint128::new(42u128))); - - // let source = Int512::MAX; - // let target = Uint128::try_from(source); - // assert_eq!( - // target, - // Err(ConversionOverflowError::new( - // "Int512", - // "Uint128", - // Int512::MAX.to_string() - // )) - // ); - // } - - #[test] - fn int512_from_uint256() { - assert_eq!( - Int512::from_uint256(Uint256::from_str("123").unwrap()), - Int512::from_str("123").unwrap() - ); - - assert_eq!( - Int512::from_uint256(Uint256::from_str("9785746283745").unwrap()), - Int512::from_str("9785746283745").unwrap() - ); - - assert_eq!( - Int512::from_uint256( - Uint256::from_str( - "97857462837575757832978493758398593853985452378423874623874628736482736487236" - ) - .unwrap() - ), - Int512::from_str( - "97857462837575757832978493758398593853985452378423874623874628736482736487236" - ) - .unwrap() - ); - } - #[test] fn int512_implements_display() { let a = Int512::from(12345u32); @@ -1269,7 +1164,7 @@ mod tests { fn int512_abs_diff_works() { let a = Int512::from(42u32); let b = Int512::from(5u32); - let expected = Int512::from(37u32); + let expected = Uint512::from(37u32); assert_eq!(a.abs_diff(b), expected); assert_eq!(b.abs_diff(a), expected); } From 776d6295ce93c4f97b1319b461c955878725ce51 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 9 Jun 2023 11:00:51 +0200 Subject: [PATCH 07/33] Update rust in github workflow --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 92ef1c64cf13b3e947fae2c8f6016f207efc34d3 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 12 Jun 2023 12:59:34 +0200 Subject: [PATCH 08/33] Improve int512 tests --- packages/std/src/math/int512.rs | 89 ++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 25 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 7533c8dcd8..a7877f9f69 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -683,6 +683,21 @@ mod tests { 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, @@ -699,6 +714,10 @@ mod tests { 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"); @@ -708,6 +727,9 @@ mod tests { 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] @@ -825,6 +847,7 @@ mod tests { assert!(!Int512::from(1u32).is_zero()); assert!(!Int512::from(123u32).is_zero()); + assert!(!Int512::from(-123i32).is_zero()); } #[test] @@ -880,16 +903,16 @@ mod tests { #[test] #[allow(clippy::op_ref)] fn int512_math() { - let a = Int512::from(12345u32); + let a = Int512::from(-12345i32); let b = Int512::from(23456u32); // test + with owned and reference right hand side - assert_eq!(a + b, Int512::from(35801u32)); - assert_eq!(a + &b, Int512::from(35801u32)); + 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(11111u32)); - assert_eq!(b - &a, Int512::from(11111u32)); + 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); @@ -908,7 +931,7 @@ mod tests { assert_eq!(d, Int512::from(276544u32)); // test - with negative result - assert_eq!(a - b, Int512::from(-11111i32)); + assert_eq!(a - b, Int512::from(-35801i32)); } #[test] @@ -923,6 +946,7 @@ mod tests { 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); @@ -1093,6 +1117,23 @@ mod tests { 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)), + ); // saturating_* assert_eq!(Int512::MAX.saturating_add(Int512::from(1u32)), Int512::MAX); @@ -1112,6 +1153,19 @@ mod tests { 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); @@ -1128,25 +1182,6 @@ mod tests { let _ = Int512::from(10u32) % Int512::zero(); } - #[test] - #[allow(clippy::op_ref)] - fn int512_rem_works() { - assert_eq!( - Int512::from(12u32) % Int512::from(10u32), - Int512::from(2u32) - ); - assert_eq!(Int512::from(50u32) % Int512::from(5u32), Int512::zero()); - - // works for refs - let a = Int512::from(42u32); - let b = Int512::from(5u32); - let expected = Int512::from(2u32); - assert_eq!(a % b, expected); - assert_eq!(a % &b, expected); - assert_eq!(&a % b, expected); - assert_eq!(&a % &b, expected); - } - #[test] fn int512_rem_assign_works() { let mut a = Int512::from(30u32); @@ -1167,6 +1202,10 @@ mod tests { 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] From 59f7f83e06def8154a6cd85f8a6c93dcc097cc58 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Mon, 12 Jun 2023 13:04:04 +0200 Subject: [PATCH 09/33] Add Shl and Neg impl for Int512 --- packages/std/src/math/int512.rs | 99 +++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index a7877f9f69..8726e3e4b2 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.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, Not, Rem, RemAssign, Shr, ShrAssign, Sub, - SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, + ShrAssign, Sub, SubAssign, }; use std::str::FromStr; @@ -228,6 +228,14 @@ impl Int512 { Ok(Self(self.0.shr(other))) } + pub fn checked_shl(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shr, 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 { @@ -249,8 +257,7 @@ impl Int512 { #[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); - Self(value) + Self(self.0.wrapping_pow(other)) } #[must_use = "this returns the result of the operation, without modifying the original"] @@ -387,10 +394,11 @@ impl From for 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. + // 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(true, "", &unpadded) + f.pad_integral(self >= &Self::zero(), "", numeric) } } @@ -463,6 +471,14 @@ impl Not for Int512 { } } +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; @@ -507,6 +523,27 @@ impl<'a> Shr<&'a u32> for Int512 { } } +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, + ) + }) + } +} + +impl<'a> Shl<&'a u32> for Int512 { + type Output = Self; + + fn shl(self, rhs: &'a u32) -> Self::Output { + Shl::::shl(self, *rhs) + } +} + impl AddAssign for Int512 { fn add_assign(&mut self, rhs: Int512) { self.0 = self.0.checked_add(rhs.0).unwrap(); @@ -543,6 +580,18 @@ impl<'a> ShrAssign<&'a u32> for Int512 { } } +impl ShlAssign for Int512 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} + +impl<'a> ShlAssign<&'a u32> for Int512 { + fn shl_assign(&mut self, rhs: &'a u32) { + *self = Shl::::shl(*self, *rhs); + } +} + impl Serialize for Int512 { /// Serializes as an integer string using base 10 fn serialize(&self, serializer: S) -> Result @@ -1195,6 +1244,44 @@ mod tests { 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); From bcbb3e169ca8827c0b35977b93c454169ac0e035 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 13 Jun 2023 11:07:39 +0200 Subject: [PATCH 10/33] Int512::checked_div_euclid --- packages/std/src/errors/mod.rs | 4 ++-- packages/std/src/errors/std_error.rs | 11 ++++++++++ packages/std/src/math/int512.rs | 33 ++++++++++++++++++++++------ 3 files changed, 39 insertions(+), 9 deletions(-) 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..f06e91fa59 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -481,6 +481,8 @@ pub enum OverflowOperation { Add, Sub, Mul, + // TODO: Adding these is technically breaking + Div, Pow, Shr, Shl, @@ -556,6 +558,15 @@ impl DivideByZeroError { } } +#[derive(Error, Debug, PartialEq, Eq)] +pub enum DivisionError { + #[error("Divide by zero error: {0}")] + DivideByZero(#[from] DivideByZeroError), + + #[error("Overflow error: {0}")] + Overflow(#[from] OverflowError), +} + #[derive(Error, Debug, PartialEq, Eq)] pub enum CheckedMultiplyFractionError { #[error("{0}")] diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 8726e3e4b2..a9da55ff4f 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -8,7 +8,7 @@ use std::ops::{ }; use std::str::FromStr; -use crate::errors::{DivideByZeroError, OverflowError, OverflowOperation, StdError}; +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 @@ -202,15 +202,24 @@ impl Int512 { .ok_or_else(|| OverflowError::new(OverflowOperation::Pow, self, exp)) } - pub fn checked_div(self, other: Self) -> Result { + pub fn checked_div(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivideByZeroError::new(self).into()); + } self.0 .checked_div(other.0) .map(Self) - .ok_or_else(|| DivideByZeroError::new(self)) + .ok_or_else(|| OverflowError::new(OverflowOperation::Div, self, other).into()) } - pub fn checked_div_euclid(self, other: Self) -> Result { - self.checked_div(other) + pub fn checked_div_euclid(self, other: Self) -> Result { + if other.is_zero() { + return Err(DivideByZeroError::new(self).into()); + } + self.0 + .checked_div_euclid(other.0) + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Div, self, other).into()) } pub fn checked_rem(self, other: Self) -> Result { @@ -1144,7 +1153,7 @@ mod tests { assert_eq!(Int512::from(2u32).checked_pow(3u32), Ok(Int512::from(8u32)),); assert!(matches!( Int512::MAX.checked_div(Int512::from(0u32)), - Err(DivideByZeroError { .. }) + Err(DivisionError::DivideByZero(_)) )); assert_eq!( Int512::from(6u32).checked_div(Int512::from(2u32)), @@ -1152,7 +1161,7 @@ mod tests { ); assert!(matches!( Int512::MAX.checked_div_euclid(Int512::from(0u32)), - Err(DivideByZeroError { .. }) + Err(DivisionError::DivideByZero(_)) )); assert_eq!( Int512::from(6u32).checked_div_euclid(Int512::from(2u32)), @@ -1183,6 +1192,10 @@ mod tests { 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); @@ -1295,6 +1308,12 @@ mod tests { 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)] From 98e877f95514ecfd8cfa547821d25791eb016d8c Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Tue, 13 Jun 2023 12:19:25 +0200 Subject: [PATCH 11/33] Add checked_neg to Int512 --- packages/std/src/errors/std_error.rs | 1 + packages/std/src/math/int512.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index f06e91fa59..f4121e6b9e 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -483,6 +483,7 @@ pub enum OverflowOperation { Mul, // TODO: Adding these is technically breaking Div, + Neg, Pow, Shr, Shl, diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index a9da55ff4f..cebf7b409b 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -245,6 +245,13 @@ impl Int512 { Ok(Self(self.0.shl(other))) } + pub fn checked_neg(self) -> Result { + self.0 + .checked_neg() + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) + } + #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -1102,6 +1109,16 @@ mod tests { let _ = Int512::from(1u32) >> 512u32; } + #[test] + fn int512_checked_neg() { + assert_eq!(Int512::one().checked_neg(), Ok(Int512::from(-1i32))); + assert!(matches!( + Int512::MIN.checked_neg(), + Err(OverflowError { .. }) + )); + assert_eq!(Int512::MAX.checked_neg(), Ok(Int512::MIN + Int512::one())); + } + #[test] fn sum_works() { let nums = vec![ From 630e299aada1951a3069957a9f611788982fefae Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 10:12:01 +0200 Subject: [PATCH 12/33] Remove uint dependency --- Cargo.lock | 25 ------------------------- contracts/burner/Cargo.lock | 25 ------------------------- contracts/crypto-verify/Cargo.lock | 25 ------------------------- contracts/cyberpunk/Cargo.lock | 25 ------------------------- contracts/floaty/Cargo.lock | 25 ------------------------- contracts/hackatom/Cargo.lock | 25 ------------------------- contracts/ibc-reflect-send/Cargo.lock | 25 ------------------------- contracts/ibc-reflect/Cargo.lock | 25 ------------------------- contracts/queue/Cargo.lock | 25 ------------------------- contracts/reflect/Cargo.lock | 25 ------------------------- contracts/staking/Cargo.lock | 25 ------------------------- contracts/virus/Cargo.lock | 25 ------------------------- packages/std/Cargo.toml | 1 - 13 files changed, 301 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc3a1c5bf9..c76a0f514a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -371,7 +371,6 @@ dependencies = [ "serde_json", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -572,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" @@ -1691,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" @@ -1824,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 129021b06e..8ebe8fa1a9 100644 --- a/contracts/burner/Cargo.lock +++ b/contracts/burner/Cargo.lock @@ -232,7 +232,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -388,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" @@ -1395,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" @@ -1503,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 a4e25b5b03..8193676a13 100644 --- a/contracts/crypto-verify/Cargo.lock +++ b/contracts/crypto-verify/Cargo.lock @@ -227,7 +227,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -391,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" @@ -1453,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" @@ -1561,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 e6ab3c05f3..60fb94530e 100644 --- a/contracts/cyberpunk/Cargo.lock +++ b/contracts/cyberpunk/Cargo.lock @@ -250,7 +250,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -414,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" @@ -1445,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" @@ -1553,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 e9932e6158..b7eefeda60 100644 --- a/contracts/floaty/Cargo.lock +++ b/contracts/floaty/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,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" @@ -1405,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" @@ -1513,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 f7ac3df002..4a22c86347 100644 --- a/contracts/hackatom/Cargo.lock +++ b/contracts/hackatom/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,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" @@ -1406,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" @@ -1514,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 b5688564bc..dfff7aa4e7 100644 --- a/contracts/ibc-reflect-send/Cargo.lock +++ b/contracts/ibc-reflect-send/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,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" @@ -1404,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" @@ -1512,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 1491d26548..f14335aa9a 100644 --- a/contracts/ibc-reflect/Cargo.lock +++ b/contracts/ibc-reflect/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,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" @@ -1404,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" @@ -1512,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 f4d4dd6354..baecd4b897 100644 --- a/contracts/queue/Cargo.lock +++ b/contracts/queue/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -377,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" @@ -1395,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" @@ -1503,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 8099b681ec..6539fc1a75 100644 --- a/contracts/reflect/Cargo.lock +++ b/contracts/reflect/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,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" @@ -1405,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" @@ -1513,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 0886b36c00..ab6d49866e 100644 --- a/contracts/staking/Cargo.lock +++ b/contracts/staking/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -385,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" @@ -1432,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" @@ -1540,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 d01769b280..ba54a8d9e3 100644 --- a/contracts/virus/Cargo.lock +++ b/contracts/virus/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "serde-json-wasm", "sha2 0.10.3", "thiserror", - "uint", ] [[package]] @@ -377,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" @@ -1384,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" @@ -1492,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 fbec9eb810..0175ec7b3d 100644 --- a/packages/std/Cargo.toml +++ b/packages/std/Cargo.toml @@ -54,7 +54,6 @@ 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] From d530dbcaedcc4001c45382fc4794719de0eb25ba Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 12:40:32 +0200 Subject: [PATCH 13/33] Use forward_ref macros for Int512 impls --- packages/std/src/math/int512.rs | 64 +++++---------------------------- 1 file changed, 8 insertions(+), 56 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index cebf7b409b..9027dd8cef 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -425,14 +425,7 @@ impl Add for Int512 { Int512(self.0.checked_add(rhs.0).unwrap()) } } - -impl<'a> Add<&'a Int512> for Int512 { - type Output = Self; - - fn add(self, rhs: &'a Int512) -> 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; @@ -457,14 +450,7 @@ impl Div for Int512 { Self(self.0.checked_div(rhs.0).unwrap()) } } - -impl<'a> Div<&'a Int512> for Int512 { - type Output = Self; - - fn div(self, rhs: &'a Int512) -> 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; @@ -530,14 +516,7 @@ impl Shr for Int512 { }) } } - -impl<'a> Shr<&'a u32> for Int512 { - type Output = Self; - - fn shr(self, rhs: &'a u32) -> Self::Output { - Shr::::shr(self, *rhs) - } -} +forward_ref_binop!(impl Shr, shr for Int512, u32); impl Shl for Int512 { type Output = Self; @@ -551,62 +530,35 @@ impl Shl for Int512 { }) } } - -impl<'a> Shl<&'a u32> for Int512 { - type Output = Self; - - fn shl(self, rhs: &'a u32) -> Self::Output { - Shl::::shl(self, *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(); } } - -impl<'a> AddAssign<&'a Int512> for Int512 { - fn add_assign(&mut self, rhs: &'a 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(); } } - -impl<'a> DivAssign<&'a Int512> for Int512 { - fn div_assign(&mut self, rhs: &'a Int512) { - 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); } } - -impl<'a> ShrAssign<&'a u32> for Int512 { - fn shr_assign(&mut self, rhs: &'a 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); } } - -impl<'a> ShlAssign<&'a u32> for Int512 { - fn shl_assign(&mut self, rhs: &'a 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 From 5bcb9d2332f010a0d309e5003eac7122f3ebfc58 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 13:19:00 +0200 Subject: [PATCH 14/33] Improve DivisionError --- packages/std/src/errors/std_error.rs | 10 ++++------ packages/std/src/math/int512.rs | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index f4121e6b9e..a6837a98b6 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -481,8 +481,6 @@ pub enum OverflowOperation { Add, Sub, Mul, - // TODO: Adding these is technically breaking - Div, Neg, Pow, Shr, @@ -561,11 +559,11 @@ impl DivideByZeroError { #[derive(Error, Debug, PartialEq, Eq)] pub enum DivisionError { - #[error("Divide by zero error: {0}")] - DivideByZero(#[from] DivideByZeroError), + #[error("Divide by zero")] + DivideByZero, - #[error("Overflow error: {0}")] - Overflow(#[from] OverflowError), + #[error("Overflow in division")] + Overflow, } #[derive(Error, Debug, PartialEq, Eq)] diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 9027dd8cef..d6d427afab 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -204,22 +204,22 @@ impl Int512 { pub fn checked_div(self, other: Self) -> Result { if other.is_zero() { - return Err(DivideByZeroError::new(self).into()); + return Err(DivisionError::DivideByZero); } self.0 .checked_div(other.0) .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Div, self, other).into()) + .ok_or(DivisionError::Overflow) } pub fn checked_div_euclid(self, other: Self) -> Result { if other.is_zero() { - return Err(DivideByZeroError::new(self).into()); + return Err(DivisionError::DivideByZero); } self.0 .checked_div_euclid(other.0) .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Div, self, other).into()) + .ok_or(DivisionError::Overflow) } pub fn checked_rem(self, other: Self) -> Result { @@ -1120,18 +1120,18 @@ mod tests { Err(OverflowError { .. }) )); assert_eq!(Int512::from(2u32).checked_pow(3u32), Ok(Int512::from(8u32)),); - assert!(matches!( + assert_eq!( Int512::MAX.checked_div(Int512::from(0u32)), - Err(DivisionError::DivideByZero(_)) - )); + Err(DivisionError::DivideByZero) + ); assert_eq!( Int512::from(6u32).checked_div(Int512::from(2u32)), Ok(Int512::from(3u32)), ); - assert!(matches!( + assert_eq!( Int512::MAX.checked_div_euclid(Int512::from(0u32)), - Err(DivisionError::DivideByZero(_)) - )); + Err(DivisionError::DivideByZero) + ); assert_eq!( Int512::from(6u32).checked_div_euclid(Int512::from(2u32)), Ok(Int512::from(3u32)), From 6b38adf08d083517342da992923eb32713fb7f6e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 13:19:16 +0200 Subject: [PATCH 15/33] Improve error string --- packages/std/src/math/int512.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index d6d427afab..74ba0218c2 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -396,7 +396,7 @@ impl FromStr for Int512 { 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 i512: {}", e))), + Err(e) => Err(StdError::generic_err(format!("Parsing Int512: {}", e))), } } } From 67fe1b438cc346483e6bf4b7448d8520ca4dde15 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 13:28:33 +0200 Subject: [PATCH 16/33] Inline Int512 constructor Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- packages/std/src/math/int512.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 74ba0218c2..954f355a51 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -50,6 +50,7 @@ impl Int512 { /// 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) } From c65232f327f9fef810d90f236b9a7dcdffae6b01 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 13:26:02 +0200 Subject: [PATCH 17/33] Add non_exhaustive to OverflowOperation --- packages/std/src/errors/std_error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index a6837a98b6..5accfcc6be 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -477,6 +477,7 @@ impl From for StdError { pub type StdResult = core::result::Result; #[derive(Error, Debug, PartialEq, Eq)] +#[non_exhaustive] pub enum OverflowOperation { Add, Sub, From 7a379c5491250001729ac7f9f553d4592b88d7e7 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 16:15:51 +0200 Subject: [PATCH 18/33] Simplify tests --- packages/std/src/math/int512.rs | 91 +++++++++------------------------ 1 file changed, 23 insertions(+), 68 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 954f355a51..b4f5655e89 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -637,27 +637,16 @@ mod tests { #[test] fn int512_zero_works() { let zero = Int512::zero(); - assert_eq!( - zero.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, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); + assert_eq!(zero.to_be_bytes(), [0; 64]); } #[test] fn uin512_one_works() { let one = Int512::one(); - assert_eq!( - one.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, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1 - ] - ); + let mut one_be = [0; 64]; + one_be[63] = 1; + + assert_eq!(one.to_be_bytes(), one_be); } #[test] @@ -752,32 +741,15 @@ mod tests { #[test] fn int512_to_be_bytes_works() { - assert_eq!( - Int512::zero().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, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - ] - ); - assert_eq!( - Int512::MAX.to_be_bytes(), - [ - 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - ] - ); - assert_eq!( - Int512::from(1u128).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, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1 - ] - ); + 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(), @@ -806,32 +778,15 @@ mod tests { #[test] fn int512_to_le_bytes_works() { - assert_eq!( - Int512::zero().to_le_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, 0 - ] - ); - assert_eq!( - Int512::MAX.to_le_bytes(), - [ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f - ] - ); - assert_eq!( - Int512::from(1u128).to_le_bytes(), - [ - 1, 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 - ] - ); + 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(), From ac2b0be321b34515fa901569514257f5761f6c67 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 16:33:41 +0200 Subject: [PATCH 19/33] Add Int256 --- packages/std/src/lib.rs | 4 +- packages/std/src/math/int256.rs | 1202 +++++++++++++++++++++++++++++++ packages/std/src/math/mod.rs | 2 + 3 files changed, 1206 insertions(+), 2 deletions(-) create mode 100644 packages/std/src/math/int256.rs diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index e05f1b4eb1..e6abac0efb 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, Int512, Isqrt, - Uint128, Uint256, Uint512, Uint64, + Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int256, Int512, + Isqrt, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; pub use crate::never::Never; diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs new file mode 100644 index 0000000000..211719d1e6 --- /dev/null +++ b/packages/std/src/math/int256.rs @@ -0,0 +1,1202 @@ +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, 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::Shr, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + pub fn checked_neg(self) -> Result { + self.0 + .checked_neg() + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) + } + + #[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: Uint256) -> Self { + let bytes = [[0u8; 32], val.to_be_bytes()].concat(); + + Self::from_be_bytes(bytes.try_into().unwrap()) + } +} + +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: 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 uin256_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 int256_checked_neg() { + assert_eq!(Int256::one().checked_neg(), Ok(Int256::from(-1i32))); + assert!(matches!( + Int256::MIN.checked_neg(), + Err(OverflowError { .. }) + )); + assert_eq!(Int256::MAX.checked_neg(), Ok(Int256::MIN + Int256::one())); + } + + #[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/mod.rs b/packages/std/src/math/mod.rs index ee514f36b8..d1cce93bf5 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,6 +1,7 @@ mod decimal; mod decimal256; mod fraction; +mod int256; mod int512; mod isqrt; mod uint128; @@ -11,6 +12,7 @@ mod uint64; pub use decimal::{Decimal, DecimalRangeExceeded}; pub use decimal256::{Decimal256, Decimal256RangeExceeded}; pub use fraction::Fraction; +pub use int256::Int256; pub use int512::Int512; pub use isqrt::Isqrt; pub use uint128::Uint128; From 49867d900ff083f46ded912816cee06636a0b53e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 17:18:45 +0200 Subject: [PATCH 20/33] Fix typo --- packages/std/src/math/int256.rs | 2 +- packages/std/src/math/int512.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 211719d1e6..d8d666e1e8 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -603,7 +603,7 @@ mod tests { } #[test] - fn uin256_one_works() { + fn uint256_one_works() { let one = Int256::one(); let mut one_be = [0; 32]; one_be[31] = 1; diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index b4f5655e89..7fbb89411b 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -641,7 +641,7 @@ mod tests { } #[test] - fn uin512_one_works() { + fn uint512_one_works() { let one = Int512::one(); let mut one_be = [0; 64]; one_be[63] = 1; From b395d8e7cb960446677388921c9c99eed4b80607 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 17:18:54 +0200 Subject: [PATCH 21/33] Add Int128 --- packages/std/src/lib.rs | 2 +- packages/std/src/math/int128.rs | 1106 ++++++++++++++++++++++++++++++ packages/std/src/math/mod.rs | 2 + packages/std/src/math/uint128.rs | 2 +- 4 files changed, 1110 insertions(+), 2 deletions(-) create mode 100644 packages/std/src/math/int128.rs diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index e6abac0efb..a0a6a546d8 100644 --- a/packages/std/src/lib.rs +++ b/packages/std/src/lib.rs @@ -52,7 +52,7 @@ pub use crate::ibc::{ #[cfg(feature = "iterator")] pub use crate::iterator::{Order, Record}; pub use crate::math::{ - Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int256, Int512, + Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Int256, Int512, Int128, Isqrt, Uint128, Uint256, Uint512, Uint64, }; pub use crate::metadata::{DenomMetadata, DenomUnit}; diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs new file mode 100644 index 0000000000..c88c4b3a79 --- /dev/null +++ b/packages/std/src/math/int128.rs @@ -0,0 +1,1106 @@ +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, 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 out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int128; +/// let a = Int128::from(258i128); +/// let b = Int128::new([ +/// 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 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) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 16]) -> Self { + Self::from_be_bytes(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) + } + + #[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::Shr, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + pub fn checked_neg(self) -> Result { + self.0 + .checked_neg() + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) + } + + #[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: 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 { + // 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 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_new_works() { + let num = Int128::new([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::new(be_bytes); + let resulting_bytes: [u8; 16] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[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::new(be_bytes); + let num2 = Int128::from_be_bytes(be_bytes); + let num3 = Int128::from_le_bytes(le_bytes); + assert_eq!(num1, Int128::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[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::new([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ]); + + let shifted = Int128::new([ + 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 int128_checked_neg() { + assert_eq!(Int128::one().checked_neg(), Ok(Int128::from(-1i32))); + assert!(matches!( + Int128::MIN.checked_neg(), + Err(OverflowError { .. }) + )); + assert_eq!(Int128::MAX.checked_neg(), Ok(Int128::MIN + Int128::one())); + } + + #[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/mod.rs b/packages/std/src/math/mod.rs index d1cce93bf5..6122814f36 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -1,6 +1,7 @@ mod decimal; mod decimal256; mod fraction; +mod int128; mod int256; mod int512; mod isqrt; @@ -12,6 +13,7 @@ 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 isqrt::Isqrt; 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); From a5ae44ae04c1a2907cd737b0f11fecfc9be60139 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 17:38:10 +0200 Subject: [PATCH 22/33] Add Int64 --- packages/std/src/lib.rs | 4 +- packages/std/src/math/int64.rs | 1054 +++++++++++++++++++++++++++++++ packages/std/src/math/mod.rs | 2 + packages/std/src/math/uint64.rs | 2 +- 4 files changed, 1059 insertions(+), 3 deletions(-) create mode 100644 packages/std/src/math/int64.rs diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs index a0a6a546d8..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, Int256, Int512, Int128, - 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/int64.rs b/packages/std/src/math/int64.rs new file mode 100644 index 0000000000..088c7d4565 --- /dev/null +++ b/packages/std/src/math/int64.rs @@ -0,0 +1,1054 @@ +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 out of primitive uint types or `new` to provide big +/// endian bytes: +/// +/// ``` +/// # use cosmwasm_std::Int64; +/// let a = Int64::from(258i64); +/// let b = Int64::new([ +/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, +/// ]); +/// assert_eq!(a, b); +/// ``` +#[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) from a big endian representation. It's just an alias for + /// `from_be_bytes`. + #[inline] + pub const fn new(value: [u8; 8]) -> Self { + Self::from_be_bytes(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) + } + + #[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::Shr, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + + pub fn checked_neg(self) -> Result { + self.0 + .checked_neg() + .map(Self) + .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) + } + + #[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 { + // 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 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_new_works() { + let num = Int64::new([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::new(be_bytes); + let resulting_bytes: [u8; 8] = num.to_be_bytes(); + assert_eq!(be_bytes, resulting_bytes); + } + + #[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::new(be_bytes); + let num2 = Int64::from_be_bytes(be_bytes); + let num3 = Int64::from_le_bytes(le_bytes); + assert_eq!(num1, Int64::from(65536u32 + 512 + 3)); + assert_eq!(num1, num2); + assert_eq!(num1, num3); + } + + #[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::new([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); + + let shifted = Int64::new([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 int64_checked_neg() { + assert_eq!(Int64::one().checked_neg(), Ok(Int64::from(-1i32))); + assert!(matches!( + Int64::MIN.checked_neg(), + Err(OverflowError { .. }) + )); + assert_eq!(Int64::MAX.checked_neg(), Ok(Int64::MIN + Int64::one())); + } + + #[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 6122814f36..fde3d4789b 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -4,6 +4,7 @@ mod fraction; mod int128; mod int256; mod int512; +mod int64; mod isqrt; mod uint128; mod uint256; @@ -16,6 +17,7 @@ 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; 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); From 8d8968479faf9653a911ff7138ddcc69afc2747f Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 28 Jun 2023 17:41:37 +0200 Subject: [PATCH 23/33] Add trait impl check for signed ints --- packages/std/src/math/mod.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index fde3d4789b..782276af37 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -72,15 +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 {} } From 4d560c99b101adaebd49244da5725e26e0cf40dc Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 10:14:44 +0200 Subject: [PATCH 24/33] Fix left-shift error message --- packages/std/src/math/int128.rs | 2 +- packages/std/src/math/int256.rs | 2 +- packages/std/src/math/int512.rs | 2 +- packages/std/src/math/int64.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index c88c4b3a79..75073ce30a 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -154,7 +154,7 @@ impl Int128 { pub fn checked_shl(self, other: u32) -> Result { if other >= 128 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } Ok(Self(self.0.shl(other))) diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index d8d666e1e8..0e0717bae6 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -204,7 +204,7 @@ impl Int256 { pub fn checked_shl(self, other: u32) -> Result { if other >= 256 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } Ok(Self(self.0.shl(other))) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index 7fbb89411b..a29b9746d5 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -240,7 +240,7 @@ impl Int512 { pub fn checked_shl(self, other: u32) -> Result { if other >= 512 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } Ok(Self(self.0.shl(other))) diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 088c7d4565..0f9bb0982a 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -153,7 +153,7 @@ impl Int64 { pub fn checked_shl(self, other: u32) -> Result { if other >= 64 { - return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } Ok(Self(self.0.shl(other))) From 87d685cb503790b0e1ae5ff301e103385d64952e Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 10:31:00 +0200 Subject: [PATCH 25/33] Avoid vec allocation --- packages/std/src/math/int512.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index a29b9746d5..cc63c39626 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -305,9 +305,10 @@ impl Int512 { impl From for Int512 { fn from(val: Uint256) -> Self { - let bytes = [[0u8; 32], val.to_be_bytes()].concat(); + let mut bytes = [0u8; 64]; + bytes[32..].copy_from_slice(&val.to_be_bytes()); - Self::from_be_bytes(bytes.try_into().unwrap()) + Self::from_be_bytes(bytes) } } From bdb28e29299255111d60066d45c0e676ccd8b453 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 10:45:59 +0200 Subject: [PATCH 26/33] Fix integer conversions --- packages/std/src/math/int128.rs | 13 ++++++++++++- packages/std/src/math/int256.rs | 22 +++++++++++++--------- packages/std/src/math/int64.rs | 5 +++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index 75073ce30a..ab1da7cd35 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -9,7 +9,7 @@ use std::ops::{ use std::str::FromStr; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Uint128, Uint64}; +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, @@ -57,6 +57,11 @@ impl Int128 { 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)) @@ -247,6 +252,12 @@ impl From for Int128 { } } +impl From for Int128 { + fn from(val: Int64) -> Self { + val.i64().into() + } +} + impl From for Int128 { fn from(val: i128) -> Self { Int128(val) diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 0e0717bae6..6bc061df39 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -9,7 +9,7 @@ use std::ops::{ use std::str::FromStr; use crate::errors::{DivideByZeroError, DivisionError, OverflowError, OverflowOperation, StdError}; -use crate::{forward_ref_partial_eq, Uint128, Uint256, Uint64}; +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. @@ -267,14 +267,6 @@ impl Int256 { } } -impl From for Int256 { - fn from(val: Uint256) -> Self { - let bytes = [[0u8; 32], val.to_be_bytes()].concat(); - - Self::from_be_bytes(bytes.try_into().unwrap()) - } -} - impl From for Int256 { fn from(val: Uint128) -> Self { val.u128().into() @@ -317,6 +309,18 @@ impl From for Int256 { } } +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()) diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 0f9bb0982a..82c56ca853 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -56,6 +56,11 @@ impl Int64 { 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)) From 8d5a243980265c539ae16659c31794c41c96fbc4 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 10:56:21 +0200 Subject: [PATCH 27/33] Fix Int64 and Int128 Display impls --- packages/std/src/math/int128.rs | 7 +------ packages/std/src/math/int64.rs | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index ab1da7cd35..18283de135 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -315,12 +315,7 @@ impl From for String { impl fmt::Display for Int128 { 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) + self.0.fmt(f) } } diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 82c56ca853..0ea734998d 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -290,12 +290,7 @@ impl From for String { impl fmt::Display for Int64 { 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) + self.0.fmt(f) } } From 4ecaea72d78f5ad1467d3379bd8923cd97767293 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 11:06:00 +0200 Subject: [PATCH 28/33] Improve Int64 and Int128 constructor --- packages/std/src/math/int128.rs | 32 +++++++++++++------------------- packages/std/src/math/int64.rs | 31 +++++++++++++------------------ 2 files changed, 26 insertions(+), 37 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index 18283de135..37f14a9764 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -17,17 +17,12 @@ use crate::{forward_ref_partial_eq, Int64, Uint128, Uint64}; /// /// # Examples /// -/// Use `from` to create instances out of primitive uint types or `new` to provide big -/// endian bytes: +/// Use `from` to create instances of this and `i128` to get the value out: /// /// ``` /// # use cosmwasm_std::Int128; /// let a = Int128::from(258i128); -/// let b = Int128::new([ -/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, -/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, -/// ]); -/// assert_eq!(a, b); +/// assert_eq!(a.i128(), 258); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] pub struct Int128(#[schemars(with = "String")] i128); @@ -38,11 +33,12 @@ impl Int128 { pub const MAX: Int128 = Int128(i128::MAX); pub const MIN: Int128 = Int128(i128::MIN); - /// Creates a Int128(value) from a big endian representation. It's just an alias for - /// `from_be_bytes`. + /// 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: [u8; 16]) -> Self { - Self::from_be_bytes(value) + pub const fn new(value: i128) -> Self { + Self(value) } /// Creates a Int128(0) @@ -519,14 +515,14 @@ mod tests { #[test] fn int128_new_works() { - let num = Int128::new([1; 16]); + 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::new(be_bytes); + let num = Int128::from_be_bytes(be_bytes); let resulting_bytes: [u8; 16] = num.to_be_bytes(); assert_eq!(be_bytes, resulting_bytes); } @@ -556,12 +552,10 @@ mod tests { ]; // These should all be the same. - let num1 = Int128::new(be_bytes); - let num2 = Int128::from_be_bytes(be_bytes); - let num3 = Int128::from_le_bytes(le_bytes); + 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); - assert_eq!(num1, num3); } #[test] @@ -854,11 +848,11 @@ mod tests { #[test] fn int128_shr_works() { - let original = Int128::new([ + 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::new([ + let shifted = Int128::from_be_bytes([ 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, ]); diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 0ea734998d..6f75d7c54e 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -17,16 +17,12 @@ use crate::{forward_ref_partial_eq, Uint64}; /// /// # Examples /// -/// Use `from` to create instances out of primitive uint types or `new` to provide big -/// endian bytes: +/// Use `from` to create instances of this and `i64` to get the value out: /// /// ``` /// # use cosmwasm_std::Int64; /// let a = Int64::from(258i64); -/// let b = Int64::new([ -/// 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, -/// ]); -/// assert_eq!(a, b); +/// assert_eq!(a.i64(), 258); /// ``` #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema)] pub struct Int64(#[schemars(with = "String")] i64); @@ -37,11 +33,12 @@ impl Int64 { pub const MAX: Int64 = Int64(i64::MAX); pub const MIN: Int64 = Int64(i64::MIN); - /// Creates a Int64(value) from a big endian representation. It's just an alias for - /// `from_be_bytes`. + /// 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: [u8; 8]) -> Self { - Self::from_be_bytes(value) + pub const fn new(value: i64) -> Self { + Self(value) } /// Creates a Int64(0) @@ -494,12 +491,12 @@ mod tests { #[test] fn int64_new_works() { - let num = Int64::new([1; 8]); + 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::new(be_bytes); + let num = Int64::from_be_bytes(be_bytes); let resulting_bytes: [u8; 8] = num.to_be_bytes(); assert_eq!(be_bytes, resulting_bytes); } @@ -525,12 +522,10 @@ mod tests { let le_bytes = [3u8, 2u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8]; // These should all be the same. - let num1 = Int64::new(be_bytes); - let num2 = Int64::from_be_bytes(be_bytes); - let num3 = Int64::from_le_bytes(le_bytes); + 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); - assert_eq!(num1, num3); } #[test] @@ -821,9 +816,9 @@ mod tests { #[test] fn int64_shr_works() { - let original = Int64::new([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); + let original = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8]); - let shifted = Int64::new([0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8]); + let shifted = Int64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8]); assert_eq!(original >> 2u32, shifted); } From 3c1abf3c385c5d5905b851aa139971905ef4d615 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 16:26:42 +0200 Subject: [PATCH 29/33] Remove breaking checked_neg implementations --- packages/std/src/errors/std_error.rs | 2 -- packages/std/src/math/int128.rs | 17 ----------------- packages/std/src/math/int256.rs | 17 ----------------- packages/std/src/math/int512.rs | 17 ----------------- packages/std/src/math/int64.rs | 17 ----------------- packages/std/src/math/mod.rs | 7 ------- 6 files changed, 77 deletions(-) diff --git a/packages/std/src/errors/std_error.rs b/packages/std/src/errors/std_error.rs index 5accfcc6be..aecdb82cc6 100644 --- a/packages/std/src/errors/std_error.rs +++ b/packages/std/src/errors/std_error.rs @@ -477,12 +477,10 @@ impl From for StdError { pub type StdResult = core::result::Result; #[derive(Error, Debug, PartialEq, Eq)] -#[non_exhaustive] pub enum OverflowOperation { Add, Sub, Mul, - Neg, Pow, Shr, Shl, diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index 37f14a9764..f95c2e0b02 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -161,13 +161,6 @@ impl Int128 { Ok(Self(self.0.shl(other))) } - pub fn checked_neg(self) -> Result { - self.0 - .checked_neg() - .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) - } - #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -865,16 +858,6 @@ mod tests { let _ = Int128::from(1u32) >> 128u32; } - #[test] - fn int128_checked_neg() { - assert_eq!(Int128::one().checked_neg(), Ok(Int128::from(-1i32))); - assert!(matches!( - Int128::MIN.checked_neg(), - Err(OverflowError { .. }) - )); - assert_eq!(Int128::MAX.checked_neg(), Ok(Int128::MIN + Int128::one())); - } - #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/int256.rs b/packages/std/src/math/int256.rs index 6bc061df39..937cf8df80 100644 --- a/packages/std/src/math/int256.rs +++ b/packages/std/src/math/int256.rs @@ -210,13 +210,6 @@ impl Int256 { Ok(Self(self.0.shl(other))) } - pub fn checked_neg(self) -> Result { - self.0 - .checked_neg() - .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) - } - #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -965,16 +958,6 @@ mod tests { let _ = Int256::from(1u32) >> 256u32; } - #[test] - fn int256_checked_neg() { - assert_eq!(Int256::one().checked_neg(), Ok(Int256::from(-1i32))); - assert!(matches!( - Int256::MIN.checked_neg(), - Err(OverflowError { .. }) - )); - assert_eq!(Int256::MAX.checked_neg(), Ok(Int256::MIN + Int256::one())); - } - #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/int512.rs b/packages/std/src/math/int512.rs index cc63c39626..980b81ee5c 100644 --- a/packages/std/src/math/int512.rs +++ b/packages/std/src/math/int512.rs @@ -246,13 +246,6 @@ impl Int512 { Ok(Self(self.0.shl(other))) } - pub fn checked_neg(self) -> Result { - self.0 - .checked_neg() - .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) - } - #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -1018,16 +1011,6 @@ mod tests { let _ = Int512::from(1u32) >> 512u32; } - #[test] - fn int512_checked_neg() { - assert_eq!(Int512::one().checked_neg(), Ok(Int512::from(-1i32))); - assert!(matches!( - Int512::MIN.checked_neg(), - Err(OverflowError { .. }) - )); - assert_eq!(Int512::MAX.checked_neg(), Ok(Int512::MIN + Int512::one())); - } - #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 6f75d7c54e..abd3f162a6 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -161,13 +161,6 @@ impl Int64 { Ok(Self(self.0.shl(other))) } - pub fn checked_neg(self) -> Result { - self.0 - .checked_neg() - .map(Self) - .ok_or_else(|| OverflowError::new(OverflowOperation::Neg, self, self)) - } - #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -829,16 +822,6 @@ mod tests { let _ = Int64::from(1u32) >> 64u32; } - #[test] - fn int64_checked_neg() { - assert_eq!(Int64::one().checked_neg(), Ok(Int64::from(-1i32))); - assert!(matches!( - Int64::MIN.checked_neg(), - Err(OverflowError { .. }) - )); - assert_eq!(Int64::MAX.checked_neg(), Ok(Int64::MIN + Int64::one())); - } - #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 782276af37..900b46f6ae 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -72,8 +72,6 @@ mod tests { { } - trait SignedImpl<'a>: IntImpl<'a> + Neg {} - impl AllImpl<'_> for Uint64 {} impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} @@ -94,9 +92,4 @@ mod tests { impl AllImpl<'_> for Decimal {} impl AllImpl<'_> for Decimal256 {} - - impl SignedImpl<'_> for Int64 {} - impl SignedImpl<'_> for Int128 {} - impl SignedImpl<'_> for Int256 {} - impl SignedImpl<'_> for Int512 {} } From e2b7437eb2c51f39502e21072e65e494e311f527 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 16:45:38 +0200 Subject: [PATCH 30/33] Actually test constructors --- packages/std/src/math/int128.rs | 17 ++++++++++++++++- packages/std/src/math/int64.rs | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index f95c2e0b02..e30aea7914 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -507,7 +507,7 @@ mod tests { } #[test] - fn int128_new_works() { + 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]); @@ -520,6 +520,21 @@ mod tests { 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(); diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index abd3f162a6..8cfbc756c6 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -483,7 +483,7 @@ mod tests { } #[test] - fn int64_new_works() { + 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]); @@ -494,6 +494,21 @@ mod tests { 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(); From e16f13d1c1627d3fd8fae673d8d89d220bca7b35 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 16:47:49 +0200 Subject: [PATCH 31/33] Don't fix bnum version Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- packages/std/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml index 0175ec7b3d..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" -bnum = "=0.7.0" +bnum = "0.7.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] cosmwasm-crypto = { path = "../crypto", version = "1.2.7" } From a173bb82f8210ac4ec0e169910a63bfe07fd33dd Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 29 Jun 2023 16:51:45 +0200 Subject: [PATCH 32/33] Add signed int trait check back in --- packages/std/src/math/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 900b46f6ae..782276af37 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -72,6 +72,8 @@ mod tests { { } + trait SignedImpl<'a>: IntImpl<'a> + Neg {} + impl AllImpl<'_> for Uint64 {} impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} @@ -92,4 +94,9 @@ mod tests { impl AllImpl<'_> for Decimal {} impl AllImpl<'_> for Decimal256 {} + + impl SignedImpl<'_> for Int64 {} + impl SignedImpl<'_> for Int128 {} + impl SignedImpl<'_> for Int256 {} + impl SignedImpl<'_> for Int512 {} } From 6f01a8193e8d81915de97cd321245f31485d805a Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Fri, 30 Jun 2023 10:28:19 +0200 Subject: [PATCH 33/33] Add changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) 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