Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,53 @@ std::array<field_t<Builder>, 3> field_t<Builder>::slice(const uint8_t msb, const
return result;
}

template <typename Builder>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nice! I can re-use it in byte_array byte decomposition method

std::pair<field_t<Builder>, field_t<Builder>> field_t<Builder>::split_at(const size_t lsb_index,
const size_t num_bits) const
{
ASSERT(lsb_index < num_bits);

const uint256_t value = get_value();
const uint256_t hi = value >> lsb_index;
const uint256_t lo = value % (uint256_t(1) << lsb_index);

if (is_constant()) {
// If `*this` is constant, we can return the split values directly
ASSERT(lo + (hi << lsb_index) == value);
return std::make_pair(field_t<Builder>(lo), field_t<Builder>(hi));
}

// Handle edge case when lsb_index == 0
if (lsb_index == 0) {
ASSERT(hi == value);
ASSERT(lo == 0);
create_range_constraint(num_bits, "split_at: hi value too large.");
return std::make_pair(field_t<Builder>(0), *this);
}

Builder* ctx = get_context();
ASSERT(ctx != nullptr);

field_t<Builder> lo_wit(witness_t(ctx, lo));
field_t<Builder> hi_wit(witness_t(ctx, hi));

// Ensure that `lo_wit` is in the range [0, 2^lsb_index - 1]
lo_wit.create_range_constraint(lsb_index, "split_at: lo value too large.");

// Ensure that `hi_wit` is in the range [0, 2^(num_bits - lsb_index) - 1]
hi_wit.create_range_constraint(num_bits - lsb_index, "split_at: hi value too large.");

// Check that *this = lo_wit + hi_wit * 2^{lsb_index}
const field_t<Builder> reconstructed = lo_wit + (hi_wit * field_t<Builder>(uint256_t(1) << lsb_index));
assert_equal(reconstructed, "split_at: decomposition failed");

// Set the origin tag for both witnesses
lo_wit.set_origin_tag(tag);
hi_wit.set_origin_tag(tag);

return std::make_pair(lo_wit, hi_wit);
}

/**
* @brief Build constraints establishing the decomposition of `*this` into bits.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ template <typename Builder> class field_t {

std::array<field_t, 3> slice(uint8_t msb, uint8_t lsb) const;

std::pair<field_t<Builder>, field_t<Builder>> split_at(
const size_t lsb_index, const size_t num_bits = grumpkin::MAX_NO_WRAP_INTEGER_BIT_LENGTH) const;

bool_t<Builder> is_zero() const;

void create_range_constraint(size_t num_bits, std::string const& msg = "field_t::range_constraint") const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "barretenberg/circuit_checker/circuit_checker.hpp"
#include "barretenberg/common/streams.hpp"
#include "barretenberg/numeric/random/engine.hpp"
#include "barretenberg/numeric/uint256/uint256.hpp"
#include "barretenberg/stdlib/primitives/circuit_builders/circuit_builders.hpp"
#include <gtest/gtest.h>
#include <utility>
Expand Down Expand Up @@ -859,6 +860,56 @@ template <typename Builder> class stdlib_field : public testing::Test {
EXPECT_TRUE(builder.err() == "slice: hi value too large.");
}

static void test_split_at()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is it possible to produce a failure test with the current circuit design?

{
Builder builder = Builder();

// Test different bit sizes
std::vector<size_t> test_bit_sizes = { 8, 16, 32, 100, 252 };

// Lambda to check split_at functionality
auto check_split_at = [&](const field_ct& a, size_t start, size_t num_bits) {
const uint256_t a_native = a.get_value();
auto split_data = a.split_at(start, num_bits);
EXPECT_EQ(split_data.first.get_value(), a_native & ((uint256_t(1) << start) - 1));
EXPECT_EQ(split_data.second.get_value(), (a_native >> start) & ((uint256_t(1) << num_bits) - 1));

if (a.is_constant()) {
EXPECT_TRUE(split_data.first.is_constant());
EXPECT_TRUE(split_data.second.is_constant());
}

if (start == 0) {
EXPECT_TRUE(split_data.first.is_constant());
EXPECT_TRUE(split_data.first.get_value() == 0);
EXPECT_EQ(split_data.second.get_value(), a.get_value());
}
};

for (size_t num_bits : test_bit_sizes) {
uint256_t a_native = engine.get_random_uint256() & ((uint256_t(1) << num_bits) - 1);

// check split_at for a constant
field_ct a_constant(a_native);
check_split_at(a_constant, 0, num_bits);
check_split_at(a_constant, num_bits / 4, num_bits);
check_split_at(a_constant, num_bits / 3, num_bits);
check_split_at(a_constant, num_bits / 2, num_bits);
check_split_at(a_constant, num_bits - 1, num_bits);

// check split_at for a witness
field_ct a_witness(witness_ct(&builder, a_native));
check_split_at(a_witness, 0, num_bits);
check_split_at(a_witness, num_bits / 4, num_bits);
check_split_at(a_witness, num_bits / 3, num_bits);
check_split_at(a_witness, num_bits / 2, num_bits);
check_split_at(a_witness, num_bits - 1, num_bits);
}

bool result = CircuitChecker::check(builder);
EXPECT_EQ(result, true);
}

static void test_three_bit_table()
{
Builder builder = Builder();
Expand Down Expand Up @@ -1362,6 +1413,12 @@ template <typename Builder> class stdlib_field : public testing::Test {
EXPECT_EQ(element.get_origin_tag(), submitted_value_origin_tag);
}

// Split preserves tags
const size_t num_bits = uint256_t(a.get_value()).get_msb() + 1;
auto split_data = a.split_at(num_bits / 2, num_bits);
EXPECT_EQ(split_data.first.get_origin_tag(), submitted_value_origin_tag);
EXPECT_EQ(split_data.second.get_origin_tag(), submitted_value_origin_tag);

// Decomposition preserves tags

auto decomposed_bits = a.decompose_into_bits();
Expand Down Expand Up @@ -1555,6 +1612,10 @@ TYPED_TEST(stdlib_field, test_slice_random)
{
TestFixture::test_slice_random();
}
TYPED_TEST(stdlib_field, test_split_at)
{
TestFixture::test_split_at();
}
TYPED_TEST(stdlib_field, test_three_bit_table)
{
TestFixture::test_three_bit_table();
Expand Down
Loading
Loading