Skip to content

Add uint128#21

Open
aritkulova wants to merge 8 commits into
devfrom
feat/add-uint128
Open

Add uint128#21
aritkulova wants to merge 8 commits into
devfrom
feat/add-uint128

Conversation

@aritkulova

Copy link
Copy Markdown
Collaborator

No description provided.

@aritkulova aritkulova requested a review from Hrom131 July 7, 2026 21:47
@aritkulova aritkulova self-assigned this Jul 7, 2026
@LesterEvSe

Copy link
Copy Markdown
Collaborator

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 match if_test_this_function(...) we run a lot of similar operations, and in fact, to reach, for example, assert_bool(is_zero_128(a), expected_bool);, we need to go through 10 checks beforehand.

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 => (), };
}

Note: The same optimization is true for other operations as well. I checked this for fn_idx == 195.

I have two possible solutions:

Comment thread simf/lib/u128.simf
Comment thread simf/lib/u128.simf

@LesterEvSe LesterEvSe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread simf/u128_test_bits.simf

/// Bit logic

match if_test_this_function(192, fn_idx) { true => { assert!(eq_128(and_128(a, b), unwrap(expected))); }, false => (), };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same problem with index here


/// Arithmetic

match if_test_this_function(192, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => (), };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread simf/lib/u128.simf
Comment on lines +138 to +153
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)))
}
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)))
}

Comment thread simf/lib/u128.simf
Comment on lines +171 to +193
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)
},
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)
}

Comment thread simf/lib/u128.simf
Comment on lines +196 to +202
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),
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am following your style as defined by the checked_add_128 function

Suggested change
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),
}
}

Comment thread simf/lib/u128.simf
// 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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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

Comment thread simf/lib/u128.simf
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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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

Comment thread simf/lib/u128.simf
},
false => {
match jet::eq_64(a_high, b_high) {
true => (1, <(u64, u64)>::into((0, safe_sub_64(a_low, b_low)))),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Splitting by module makes it easier to run a focused subset. u128_tests currently has 55 tests, which is a lot to run together.

Suggested change
mod u128_tests {
mod u128_tests_arithmetic {

Comment thread tests/u128_test_bits.rs
}
}

mod u128_tests {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mod u128_tests {
mod u128_tests_bits {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants