From 985d03ac065691d83836d72deb732e5c9c843562 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Wed, 2 Aug 2023 13:19:42 -0500 Subject: [PATCH 01/16] impl Power for U256 --- sway-lib-std/src/u256.sw | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 60196d97cd8..f3e2c037274 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -5,6 +5,7 @@ use ::assert::assert; use ::convert::From; use ::result::Result::{self, *}; use ::u128::U128; +use ::math::Power; /// Left shift a `u64` and preserve the overflow amount if any. fn lsh_with_carry(word: u64, shift_amount: u64) -> (u64, u64) { @@ -570,3 +571,49 @@ impl core::ops::Divide for U256 { quotient } } + +impl Power for U256 { + fn pow(self, exponent: Self) -> Self { + let mut value = self; + let mut exp = exponent; + let one = Self::from((0, 1)); + let zero = Self::from((0, 0)); + + if exp == zero { + return one; + } + + if exp == one { + // Manually clone `self`. Otherwise, we may have a `MemoryOverflow` + // issue with code that looks like: `x = x.pow(other)` + return Self::from((self.upper, self.lower)); + } + + while exp & one == zero { + value = value * value; + exp >>= 1; + } + + if exp == one { + return value; + } + + let mut acc = value; + while exp > one { + exp >>= 1; + value = value * value; + if exp & one == one { + acc = acc * value; + } + } + acc + } +} + +#[test] +fn test_pow_u256() { + let num = 5; + assert_eq(num.pow(2), 25); + assert_eq(num.pow(3), 125); + assert_eq(num.pow(10), 9765625) +} From 35b0dfe1d3bc568cc48a7fea7535d683e8a2e335 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Wed, 2 Aug 2023 14:33:24 -0500 Subject: [PATCH 02/16] tests --- sway-lib-std/src/u256.sw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index f3e2c037274..07deb0ddad7 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -1,7 +1,7 @@ //! A 256-bit unsigned integer type. library; -use ::assert::assert; +use ::assert::{assert_eq, assert}; use ::convert::From; use ::result::Result::{self, *}; use ::u128::U128; @@ -586,7 +586,7 @@ impl Power for U256 { if exp == one { // Manually clone `self`. Otherwise, we may have a `MemoryOverflow` // issue with code that looks like: `x = x.pow(other)` - return Self::from((self.upper, self.lower)); + return Self::from((self.a, self.b, self.c, self.d)); } while exp & one == zero { From 7f4d462ff09f01411e3c5d0f91a065384420ff6d Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Thu, 3 Aug 2023 11:55:15 -0500 Subject: [PATCH 03/16] tests --- sway-lib-std/src/u128.sw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway-lib-std/src/u128.sw b/sway-lib-std/src/u128.sw index b7aea44da89..61eb5f9d254 100644 --- a/sway-lib-std/src/u128.sw +++ b/sway-lib-std/src/u128.sw @@ -321,7 +321,7 @@ impl core::ops::Subtract for U128 { } } impl core::ops::Multiply for U128 { - /// Multiply a `U128` with a `U128`. Panics of overflow. + /// Multiply a `U128` with a `U128`. Panics if overflow. fn multiply(self, other: Self) -> Self { // in case both of the `U128` upper parts are bigger than zero, // it automatically means overflow, as any `U128` value From d2065bed38eb7a30941e5bdd16183d5f3de50128 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Thu, 3 Aug 2023 11:59:36 -0500 Subject: [PATCH 04/16] tests --- sway-lib-std/src/u256.sw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 07deb0ddad7..e7ec2443cac 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -576,8 +576,8 @@ impl Power for U256 { fn pow(self, exponent: Self) -> Self { let mut value = self; let mut exp = exponent; - let one = Self::from((0, 1)); - let zero = Self::from((0, 0)); + let one = Self::from((0, 0, 0, 1)); + let zero = Self::from((0, 0, 0, 1)); if exp == zero { return one; From c86bbdbbcebb6965447177170d016d7efb4bb0b9 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Thu, 3 Aug 2023 12:12:01 -0500 Subject: [PATCH 05/16] zero test --- sway-lib-std/src/u256.sw | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index e7ec2443cac..173095b1d9f 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -577,7 +577,7 @@ impl Power for U256 { let mut value = self; let mut exp = exponent; let one = Self::from((0, 0, 0, 1)); - let zero = Self::from((0, 0, 0, 1)); + let zero = Self::from((0, 0, 0, 0)); if exp == zero { return one; @@ -615,5 +615,6 @@ fn test_pow_u256() { let num = 5; assert_eq(num.pow(2), 25); assert_eq(num.pow(3), 125); - assert_eq(num.pow(10), 9765625) + assert_eq(num.pow(10), 9765625); + assert_eq(num.pow(0), 5) } From ad75f1967a22fd5a06ce7ae4a2a0a904b058efd2 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Tue, 8 Aug 2023 14:41:47 -0500 Subject: [PATCH 06/16] fixed test --- sway-lib-std/src/u256.sw | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 173095b1d9f..f6e268977ed 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -612,9 +612,11 @@ impl Power for U256 { #[test] fn test_pow_u256() { - let num = 5; - assert_eq(num.pow(2), 25); - assert_eq(num.pow(3), 125); - assert_eq(num.pow(10), 9765625); - assert_eq(num.pow(0), 5) -} + let five = U256::from((0, 0, 0, 5)); + let two = U256::from((0, 0, 0, 2)); + let three = U256::from((0, 0, 0, 3)); + let twenty_eight = U256::from((0, 0, 0, 28)); + assert_eq(five.pow(two), U256::from((0, 0, 0, 25))); + assert_eq(five.pow(three), U256::from((0, 0, 0, 125))); + assert_eq(five.pow(twenty_eight), U256::from((0, 359414837200037395, 18446744073709551615, 18446744073709551615))); +} \ No newline at end of file From 5809cf04a1c414c686ad7e1a5df358256e8a2554 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Wed, 9 Aug 2023 12:39:49 -0500 Subject: [PATCH 07/16] added documentation and helper methods --- sway-lib-std/src/u256.sw | 54 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index f6e268977ed..2c2c69bf8dd 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -82,6 +82,41 @@ impl U256 { } } + /// Initializes a new `U256` with a value of 1. + /// + /// ### Examples + /// + /// ```sway + /// use std::u256::U256; + /// + /// let init_one = U256::one(); + /// let one_u256 = U256 { a: 0, b: 0, c: 0, d: 1 }; + /// + /// assert(init_one == one_u256); + /// ``` + pub fn one() -> Self { + Self { + a: 0, + b: 0, + c: 0, + d: 1, + } + } + + /// Returns true if value is zero. + /// + /// ### Examples + /// + /// ```sway + /// use std::u256::U256 + /// + /// let zero_u256 = U256::new(); + /// assert(zero_u256.is_zero()); + /// ``` + pub fn is_zero(self) -> bool { + self.a == 0 && self.b == 0 && self.c == 0 && self.d == 0 + } + /// Safely downcast to `u64` without loss of precision. /// Returns `Err` if the `number > u64::max()`. /// @@ -573,13 +608,18 @@ impl core::ops::Divide for U256 { } impl Power for U256 { + /// Fast exponentiation by squaring + /// https://en.wikipedia.org/wiki/Exponentiation_by_squaring + /// + /// # Panics + /// + /// Panics if the result overflows the type. fn pow(self, exponent: Self) -> Self { let mut value = self; let mut exp = exponent; - let one = Self::from((0, 0, 0, 1)); - let zero = Self::from((0, 0, 0, 0)); + let one = Self::one(); - if exp == zero { + if exp.is_zero() { return one; } @@ -589,7 +629,7 @@ impl Power for U256 { return Self::from((self.a, self.b, self.c, self.d)); } - while exp & one == zero { + while (exp & one).is_zero() { value = value * value; exp >>= 1; } @@ -619,4 +659,10 @@ fn test_pow_u256() { assert_eq(five.pow(two), U256::from((0, 0, 0, 25))); assert_eq(five.pow(three), U256::from((0, 0, 0, 125))); assert_eq(five.pow(twenty_eight), U256::from((0, 359414837200037395, 18446744073709551615, 18446744073709551615))); +} + +#[test] +fn test_is_zero() { + let zero_u256 = U256::new(); + assert(zero_u256.is_zero()); } \ No newline at end of file From 6a733e699ec7722eed282af52a00740d467e0824 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Mon, 21 Aug 2023 16:37:04 -0500 Subject: [PATCH 08/16] reworking --- sway-lib-std/src/u256.sw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 2c2c69bf8dd..92e5a64621c 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -658,7 +658,7 @@ fn test_pow_u256() { let twenty_eight = U256::from((0, 0, 0, 28)); assert_eq(five.pow(two), U256::from((0, 0, 0, 25))); assert_eq(five.pow(three), U256::from((0, 0, 0, 125))); - assert_eq(five.pow(twenty_eight), U256::from((0, 359414837200037395, 18446744073709551615, 18446744073709551615))); + assert_eq(five.pow(twenty_eight), U256::from((0, 0, 2, 359414837200037395))); } #[test] From c9e720656ade3deaf7671f61bbcbdd6272d91524 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Mon, 21 Aug 2023 16:38:02 -0500 Subject: [PATCH 09/16] reworking fn --- sway-lib-std/src/u256.sw | 81 +++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 92e5a64621c..5cb33da121d 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -1,7 +1,7 @@ //! A 256-bit unsigned integer type. library; -use ::assert::{assert_eq, assert}; +use ::assert::assert; use ::convert::From; use ::result::Result::{self, *}; use ::u128::U128; @@ -117,6 +117,10 @@ impl U256 { self.a == 0 && self.b == 0 && self.c == 0 && self.d == 0 } + pub fn low_u64(self) -> u64 { + self.a + } + /// Safely downcast to `u64` without loss of precision. /// Returns `Err` if the `number > u64::max()`. /// @@ -614,55 +618,62 @@ impl Power for U256 { /// # Panics /// /// Panics if the result overflows the type. - fn pow(self, exponent: Self) -> Self { - let mut value = self; - let mut exp = exponent; - let one = Self::one(); - - if exp.is_zero() { - return one; - } - - if exp == one { - // Manually clone `self`. Otherwise, we may have a `MemoryOverflow` - // issue with code that looks like: `x = x.pow(other)` - return Self::from((self.a, self.b, self.c, self.d)); - } - - while (exp & one).is_zero() { - value = value * value; - exp >>= 1; + fn pow(self, expon: Self) -> Self { + if expon.is_zero() { + return Self::one() } - - if exp == one { - return value; - } - - let mut acc = value; - while exp > one { - exp >>= 1; - value = value * value; - if exp & one == one { - acc = acc * value; + + let u_one = Self::one(); + let mut y = u_one; + let mut n = expon; + let mut x = self; + while n > u_one { + if is_even(n) { + x = x * x; + n = n >> 1u64; + } else { + y = x * y; + x = x * x; + } } - acc + x * y } } + +fn is_even(ref x: U256) -> bool { + x.low_u64() & 1 == 0 +} + #[test] fn test_pow_u256() { let five = U256::from((0, 0, 0, 5)); let two = U256::from((0, 0, 0, 2)); let three = U256::from((0, 0, 0, 3)); let twenty_eight = U256::from((0, 0, 0, 28)); - assert_eq(five.pow(two), U256::from((0, 0, 0, 25))); - assert_eq(five.pow(three), U256::from((0, 0, 0, 125))); - assert_eq(five.pow(twenty_eight), U256::from((0, 0, 2, 359414837200037395))); + + let five_pow_two = five.pow(two); + assert(five_pow_two.a == 0); + assert(five_pow_two.b == 0); + assert(five_pow_two.c == 0); + assert(five_pow_two.d == 25); + + let five_pow_three = five.pow(three); + assert(five_pow_three.a == 0); + assert(five_pow_three.b == 0); + assert(five_pow_three.c == 0); + assert(five_pow_three.d == 125); + + let five_pow_28 = five.pow(twenty_eight); + assert(five_pow_28.a == 0); + assert(five_pow_28.b == 359414837200037395); + assert(five_pow_28.c == 18446744073709551615); + assert(five_pow_28.d == 18446744073709551615); } #[test] fn test_is_zero() { let zero_u256 = U256::new(); assert(zero_u256.is_zero()); -} \ No newline at end of file +} From 91747c300c903f3a86d36d717d1d2344bd98b7ff Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Wed, 23 Aug 2023 12:55:20 -0500 Subject: [PATCH 10/16] rewrite pow fn --- sway-lib-std/src/u256.sw | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 5cb33da121d..c32182ce634 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -620,21 +620,23 @@ impl Power for U256 { /// Panics if the result overflows the type. fn pow(self, expon: Self) -> Self { if expon.is_zero() { - return Self::one() + return Self::one() //declares = 1 when expon = 0 } - let u_one = Self::one(); - let mut y = u_one; + let u_one = Self::one(); //not sure where/how u_one //will take a seperate fn + let mut y = u_one; //and y are being used //to get working? let mut n = expon; - let mut x = self; + let mut x = self; while n > u_one { - if is_even(n) { - x = x * x; - n = n >> 1u64; + if is_even(n) { //if expon an even number given, self x self till + x = x * x; //expon is shifted completely to 0 + n = n >> 1; //need to rewrite usize protion for sway } else { - y = x * y; + y = x * y; x = x * x; - + // to reduce odd number by 1 we should just clear the last bit + n.c = n.c & ((0u64)>>1); + n = n >> 1; } } x * y From 96dae95837cb7161924118fa6b39880acfa31e5e Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Wed, 23 Aug 2023 14:43:17 -0500 Subject: [PATCH 11/16] rework tweak --- sway-lib-std/src/u256.sw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index c32182ce634..052a600d9df 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -635,7 +635,7 @@ impl Power for U256 { y = x * y; x = x * x; // to reduce odd number by 1 we should just clear the last bit - n.c = n.c & ((0u64)>>1); + n.d = n.d & ((0u64)>>1); n = n >> 1; } } From 64dd5cf2a1e901930049299ee511bb78b6bb9c92 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Thu, 24 Aug 2023 13:13:03 -0500 Subject: [PATCH 12/16] add missing bang, remove ref --- sway-lib-std/src/u256.sw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 12a4d0b1a01..fa175dc37db 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -682,7 +682,7 @@ impl Power for U256 { y = x * y; x = x * x; // to reduce odd number by 1 we should just clear the last bit - n.d = n.d & ((0u64)>>1); + n.d = n.d & ((!0u64)>>1); n = n >> 1; } } @@ -691,7 +691,7 @@ impl Power for U256 { } -fn is_even(ref x: U256) -> bool { +fn is_even(x: U256) -> bool { x.low_u64() & 1 == 0 } From 83b6c18e04e8b16b380ef52917f76a38297be730 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Thu, 24 Aug 2023 13:18:11 -0500 Subject: [PATCH 13/16] update 5^28 test --- sway-lib-std/src/u256.sw | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index fa175dc37db..dc8c9045882 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -716,9 +716,9 @@ fn test_pow_u256() { let five_pow_28 = five.pow(twenty_eight); assert(five_pow_28.a == 0); - assert(five_pow_28.b == 359414837200037395); - assert(five_pow_28.c == 18446744073709551615); - assert(five_pow_28.d == 18446744073709551615); + assert(five_pow_28.b == 0); + assert(five_pow_28.c == 2); + assert(five_pow_28.d == 359414837200037395); } #[test] From 222eed7ad5ca6fd5791604c248995e58bb5168f8 Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Thu, 24 Aug 2023 13:29:18 -0500 Subject: [PATCH 14/16] separate pow tests --- sway-lib-std/src/u256.sw | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index dc8c9045882..c9954c1f406 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -667,19 +667,19 @@ impl Power for U256 { /// Panics if the result overflows the type. fn pow(self, expon: Self) -> Self { if expon.is_zero() { - return Self::one() //declares = 1 when expon = 0 + return Self::one() } - let u_one = Self::one(); //not sure where/how u_one //will take a seperate fn - let mut y = u_one; //and y are being used //to get working? + let u_one = Self::one(); + let mut y = u_one; let mut n = expon; - let mut x = self; + let mut x = self; while n > u_one { - if is_even(n) { //if expon an even number given, self x self till - x = x * x; //expon is shifted completely to 0 - n = n >> 1; //need to rewrite usize protion for sway + if is_even(n) { + x = x * x; + n = n >> 1; } else { - y = x * y; + y = x * y; x = x * x; // to reduce odd number by 1 we should just clear the last bit n.d = n.d & ((!0u64)>>1); @@ -696,23 +696,33 @@ fn is_even(x: U256) -> bool { } #[test] -fn test_pow_u256() { +fn test_five_pow_two_u256() { let five = U256::from((0, 0, 0, 5)); let two = U256::from((0, 0, 0, 2)); - let three = U256::from((0, 0, 0, 3)); - let twenty_eight = U256::from((0, 0, 0, 28)); - + let five_pow_two = five.pow(two); assert(five_pow_two.a == 0); assert(five_pow_two.b == 0); assert(five_pow_two.c == 0); assert(five_pow_two.d == 25); +} + +#[test] +fn test_five_pow_three_u256() { + let five = U256::from((0, 0, 0, 5)); + let three = U256::from((0, 0, 0, 3)); let five_pow_three = five.pow(three); assert(five_pow_three.a == 0); assert(five_pow_three.b == 0); assert(five_pow_three.c == 0); assert(five_pow_three.d == 125); +} + +#[test] +fn test_five_pow_28_u256() { + let five = U256::from((0, 0, 0, 5)); + let twenty_eight = U256::from((0, 0, 0, 28)); let five_pow_28 = five.pow(twenty_eight); assert(five_pow_28.a == 0); From b71d29845bf021d2b5308baad0ce48168aa5a4ad Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Thu, 24 Aug 2023 13:45:01 -0500 Subject: [PATCH 15/16] try shift equals & direct size assignment --- sway-lib-std/src/u256.sw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index c9954c1f406..2def0a94b5d 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -677,13 +677,13 @@ impl Power for U256 { while n > u_one { if is_even(n) { x = x * x; - n = n >> 1; + n >>= 1u64; } else { y = x * y; x = x * x; // to reduce odd number by 1 we should just clear the last bit n.d = n.d & ((!0u64)>>1); - n = n >> 1; + n >>= 1u64; } } x * y From fd4fe9d1c0f913bda13989681d906573dc911d9e Mon Sep 17 00:00:00 2001 From: Andrew O'Brien Date: Thu, 24 Aug 2023 13:49:45 -0500 Subject: [PATCH 16/16] remove u64 assignment --- sway-lib-std/src/u256.sw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw index 2def0a94b5d..39d8c4887af 100644 --- a/sway-lib-std/src/u256.sw +++ b/sway-lib-std/src/u256.sw @@ -677,13 +677,13 @@ impl Power for U256 { while n > u_one { if is_even(n) { x = x * x; - n >>= 1u64; + n >>= 1; } else { y = x * y; x = x * x; // to reduce odd number by 1 we should just clear the last bit n.d = n.d & ((!0u64)>>1); - n >>= 1u64; + n >>= 1; } } x * y