Add uint128#21
Conversation
|
Okay, I see the problem with running the tests, they take a lot of time. I have been playing around with the code, and the main problem is that with For example, when I run code like this, simplex tests finishes in under 3s, compared to the current 45s: fn main() {
let fn_idx: u8 = witness::FUNCTION_INDEX;
let a: u128 = witness::FIRST_ARG;
let b: u128 = witness::SECOND_ARG;
let expected: Option<u128> = witness::EXPECTED;
let expected_bool: bool = witness::EXPECTED_BOOL;
let second_expected: u128 = witness::SECOND_EXPECTED; // for cases where the result is a 256 bit value
let third_expected: u128 = witness::THIRD_EXPECTED; // for cases where the result is 256 bit + 128 bit values
match if_test_this_function(197, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => (), };
}
I have two possible solutions:
|
LesterEvSe
left a comment
There was a problem hiding this comment.
Please add tests for this optimization. Suggested cases: a_low == b_low, a_low - b_low == u64::MAX, and if possible a property test asserting q * b + r == a on randomized inputs
|
|
||
| /// Bit logic | ||
|
|
||
| match if_test_this_function(192, fn_idx) { true => { assert!(eq_128(and_128(a, b), unwrap(expected))); }, false => (), }; |
There was a problem hiding this comment.
Same problem with index here
|
|
||
| /// Arithmetic | ||
|
|
||
| match if_test_this_function(192, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => (), }; |
There was a problem hiding this comment.
Let's start from 0 because a shift of 192 is redundant here. We need this within the scope of a single file, where we have the default operations from the uint_tests! macro, along with other custom functionality
| pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { | ||
| let (a_high, a_low): (u64, u64) = <u128>::into(a); | ||
|
|
||
| let (carry, res_low): (bool, u64) = jet::add_64(a_low, b); | ||
|
|
||
| match carry { | ||
| true => { | ||
| let (carry, res_high): (bool, u64) = jet::add_64(a_high, 1); | ||
| let res: u128 = <(u64, u64)>::into((res_high, res_low)); | ||
|
|
||
| (carry, res) | ||
| }, false => { | ||
| (carry, <(u64, u64)>::into((a_high, res_low))) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
| pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { | |
| let (a_high, a_low): (u64, u64) = <u128>::into(a); | |
| let (carry, res_low): (bool, u64) = jet::add_64(a_low, b); | |
| match carry { | |
| true => { | |
| let (carry, res_high): (bool, u64) = jet::add_64(a_high, 1); | |
| let res: u128 = <(u64, u64)>::into((res_high, res_low)); | |
| (carry, res) | |
| }, false => { | |
| (carry, <(u64, u64)>::into((a_high, res_low))) | |
| } | |
| } | |
| } | |
| pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { | |
| let (a_high, a_low): (u64, u64) = <u128>::into(a); | |
| let (carry_low, res_low): (bool, u64) = jet::add_64(a_low, b); | |
| let (carry_high, res_high): (bool, u64) = jet::full_add_64(carry_low, a_high, 0); | |
| (carry_high, <(u64, u64)>::into((res_high, res_low))) | |
| } |
| pub fn sub_128(a: u128, b: u128) -> (bool, u128) { | ||
| let (a_high, a_low): (u64, u64) = <u128>::into(a); | ||
| let (b_high, b_low): (u64, u64) = <u128>::into(b); | ||
|
|
||
| let (borrow_high, diff_high): (bool, u64) = jet::subtract_64(a_high, b_high); | ||
| let (borrow_low, diff_low): (bool, u64) = jet::subtract_64(a_low, b_low); | ||
|
|
||
| match borrow_low { | ||
| true => { | ||
| let (borrow, diff_high): (bool, u64) = jet::subtract_64(diff_high, 1); | ||
| let res: u128 = <(u64, u64)>::into((diff_high, diff_low)); | ||
|
|
||
| let final_borrow: bool = or(borrow, borrow_high); | ||
|
|
||
| (final_borrow, res) | ||
| }, | ||
| false => { | ||
| let res: u128 = <(u64, u64)>::into((diff_high, diff_low)); | ||
|
|
||
| (borrow_high, res) | ||
| }, | ||
| } | ||
| } |
There was a problem hiding this comment.
| pub fn sub_128(a: u128, b: u128) -> (bool, u128) { | |
| let (a_high, a_low): (u64, u64) = <u128>::into(a); | |
| let (b_high, b_low): (u64, u64) = <u128>::into(b); | |
| let (borrow_high, diff_high): (bool, u64) = jet::subtract_64(a_high, b_high); | |
| let (borrow_low, diff_low): (bool, u64) = jet::subtract_64(a_low, b_low); | |
| match borrow_low { | |
| true => { | |
| let (borrow, diff_high): (bool, u64) = jet::subtract_64(diff_high, 1); | |
| let res: u128 = <(u64, u64)>::into((diff_high, diff_low)); | |
| let final_borrow: bool = or(borrow, borrow_high); | |
| (final_borrow, res) | |
| }, | |
| false => { | |
| let res: u128 = <(u64, u64)>::into((diff_high, diff_low)); | |
| (borrow_high, res) | |
| }, | |
| } | |
| } | |
| pub fn sub_128(a: u128, b: u128) -> (bool, u128) { | |
| let (a_high, a_low): (u64, u64) = <u128>::into(a); | |
| let (b_high, b_low): (u64, u64) = <u128>::into(b); | |
| let (borrow_low, diff_low): (bool, u64) = jet::subtract_64(a_low, b_low); | |
| let (borrow_high, diff_high): (bool, u64) = jet::full_subtract_64(borrow_low, a_high, b_high); | |
| let res: u128 = <(u64, u64)>::into((diff_high, diff_low)); | |
| (borrow_high, res) | |
| } |
| pub fn checked_sub_128(a: u128, b: u128) -> Option<u128> { | ||
| let (borrow, diff): (bool, u128) = sub_128(a, b); | ||
| match borrow { | ||
| true => None, | ||
| false => Some(diff), | ||
| } | ||
| } |
There was a problem hiding this comment.
I am following your style as defined by the checked_add_128 function
| pub fn checked_sub_128(a: u128, b: u128) -> Option<u128> { | |
| let (borrow, diff): (bool, u128) = sub_128(a, b); | |
| match borrow { | |
| true => None, | |
| false => Some(diff), | |
| } | |
| } | |
| pub fn checked_sub_128(a: u128, b: u128) -> Option<u128> { | |
| let (borrow, diff): (bool, u128) = sub_128(a, b); | |
| match borrow { | |
| true => None, | |
| false => Some(diff), | |
| } | |
| } |
| // a' = remainder * 2^64 + a_low | ||
| let a_prime: u128 = <(u64, u64)>::into((remainder, a_low)); | ||
|
|
||
| // we need to normalize here, because jet::div_mod_128_64 only acceps b >= 2^63 |
There was a problem hiding this comment.
| // we need to normalize here, because jet::div_mod_128_64 only acceps b >= 2^63 | |
| // we need to normalize here, because jet::div_mod_128_64 only accepts b >= 2^63 |
| false => { | ||
| match and(jet::is_zero_64(a_high), jet::is_zero_64(b_high)) { | ||
| true => { | ||
| // if both a_high and b_high is equal to zero, this narrows down to 64-bit division |
There was a problem hiding this comment.
| // if both a_high and b_high is equal to zero, this narrows down to 64-bit division | |
| // if both a_high and b_high are zero, this narrows down to 64-bit division |
| }, | ||
| false => { | ||
| match jet::eq_64(a_high, b_high) { | ||
| true => (1, <(u64, u64)>::into((0, safe_sub_64(a_low, b_low)))), |
There was a problem hiding this comment.
| true => (1, <(u64, u64)>::into((0, safe_sub_64(a_low, b_low)))), | |
| true => { | |
| // Safe: !lt_128(a, b) and a_high == b_high implies a_low >= b_low, | |
| // so the subtraction cannot underflow. | |
| let (_, diff): (bool, u64) = jet::subtract_64(a_low, b_low); | |
| (1, <(u64, u64)>::into((0, diff))) | |
| }, |
| (a_high, a_low) | ||
| } | ||
|
|
||
| mod u128_tests { |
There was a problem hiding this comment.
Splitting by module makes it easier to run a focused subset. u128_tests currently has 55 tests, which is a lot to run together.
| mod u128_tests { | |
| mod u128_tests_arithmetic { |
| } | ||
| } | ||
|
|
||
| mod u128_tests { |
There was a problem hiding this comment.
| mod u128_tests { | |
| mod u128_tests_bits { |
No description provided.