Skip to content

Commit

Permalink
pow impl for u256 (#5195)
Browse files Browse the repository at this point in the history
## Description

This PR is part of #4794 and it
implements `pow` for u256. It is using the same algorithm as Rust stdlib
https://github.com/rust-lang/rust/blob/193e8a196b7700542473a477effd8c6c5786f8de/library/core/src/num/uint_macros.rs#L1976.

#4900 implements `pow` for `U256`
(upper case `U`) which is being deprecated.

Closes #4449

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

---------

Co-authored-by: Andrew O'Brien <[email protected]>
  • Loading branch information
xunilrj and andrewvious authored Oct 24, 2023
1 parent 13f33f5 commit 1762793
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 1 deletion.
57 changes: 57 additions & 0 deletions sway-lib-core/src/primitives.sw
Original file line number Diff line number Diff line change
@@ -1,5 +1,62 @@
library;

impl u256 {
/// The smallest value that can be represented by this integer type.
///
/// # Returns
///
/// * [u256] - The smallest `u256` value.
///
/// # Examples
///
/// ```sway
/// fn foo() {
/// let val = u256::min();
/// assert(val == 0x0000000000000000000000000000000000000000000000000000000000000000u256);
// }
/// ```
pub fn min() -> Self {
0x0000000000000000000000000000000000000000000000000000000000000000u256
}

/// The largest value that can be represented by this integer type,
/// 2<sup>256</sup> - 1.
///
/// # Returns
///
/// * [u256] - The largest `u256` value.
///
/// # Examples
///
/// ```sway
/// fn foo() {
/// let val = u256::max();
/// assert(val == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFu256);
/// }
/// ```
pub fn max() -> Self {
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFu256
}

/// The size of this integer type in bits.
///
/// # Returns
///
/// * [u32] - The number of bits for a `u256`.
///
/// # Examples
///
/// ```sway
/// fn foo() {
/// let bits = u256::bits();
/// assert(bits == 256);
/// }
/// ```
pub fn bits() -> u64 {
256
}
}

impl u64 {
/// The smallest value that can be represented by this integer type.
///
Expand Down
43 changes: 43 additions & 0 deletions sway-lib-std/src/math.sw
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,36 @@ pub trait Power {
fn pow(self, exponent: u32) -> Self;
}


impl Power for u256 {
/// Raises self to the power of `exponent`, using exponentiation by squaring.
///
/// # Panics
///
/// Panics if the result overflows the type.
fn pow(self, exponent: u32) -> Self {
let one = 0x0000000000000000000000000000000000000000000000000000000000000001u256;

if exponent == 0 {
return one;
}

let mut exp = exponent;
let mut base = self;
let mut acc = one;

while exp > 1 {
if (exp & 1) == 1 {
acc = acc * base;
}
exp = exp >> 1;
base = base * base;
}

acc * base
}
}

impl Power for u64 {
fn pow(self, exponent: u32) -> Self {
asm(r1: self, r2: exponent, r3) {
Expand Down Expand Up @@ -163,3 +193,16 @@ impl BinaryLogarithm for u8 {
self.log(2u8)
}
}

#[test]
fn u256_pow_tests() {
let five = 0x0000000000000000000000000000000000000000000000000000000000000005u256;

use ::assert::*;

// 5^2 = 25 = 0x19
assert_eq(five.pow(2), 0x0000000000000000000000000000000000000000000000000000000000000019u256);

// 5^28 = 0x204FCE5E3E2502611 (see https://www.wolframalpha.com/input?i=convert+5%5E28+in+hex)
assert_eq(five.pow(28), 0x0000000000000000204FCE5E3E2502611u256);
}
116 changes: 115 additions & 1 deletion sway-lib-std/src/u256.sw
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
//! A 256-bit unsigned integer type.
library;

use ::assert::assert;
use ::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) {
Expand Down Expand Up @@ -100,6 +101,45 @@ 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
}

pub fn low_u64(self) -> u64 {
self.a
}

/// Safely downcast to `u64` without loss of precision.
///
/// # Additional Information
Expand Down Expand Up @@ -638,3 +678,77 @@ impl core::ops::Divide for U256 {
quotient
}
}

impl Power for U256 {
/// Raises self to the power of `exponent`, using exponentiation by squaring.
///
/// # Panics
///
/// Panics if the result overflows the type.
fn pow(self, exponent: u32) -> Self {
let one = U256::from((0, 0, 0, 1));
let two = U256::from((0, 0, 0, 2));

if exponent == 0 {
return one;
}

let mut exp = exponent;
let mut base = self;
let mut acc = one;

while exp > 1 {
if (exp & 1) == 1 {
acc = acc * base;
}
exp = exp >> 1;
base = base * base;
}

acc * base
}
}


fn is_even(x: U256) -> bool {
x.low_u64() & 1 == 0
}

#[test]
fn test_five_pow_two_u256() {
let five = U256::from((0, 0, 0, 5));

let five_pow_two = five.pow(2);
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 five_pow_three = five.pow(3);
assert_eq(five_pow_three.a, 0);
assert_eq(five_pow_three.b, 0);
assert_eq(five_pow_three.c, 0);
assert_eq(five_pow_three.d, 125);
}

#[test]
fn test_five_pow_28_u256() {
let five = U256::from((0, 0, 0, 5));

let five_pow_28 = five.pow(28);
assert_eq(five_pow_28.a, 0);
assert_eq(five_pow_28.b, 0);
assert_eq(five_pow_28.c, 2);
assert_eq(five_pow_28.d, 359414837200037393);
}

#[test]
fn test_is_zero() {
let zero_u256 = U256::new();
assert(zero_u256.is_zero());
}

0 comments on commit 1762793

Please sign in to comment.