Collect various utility functions into a separate file

This commit is contained in:
Ben Edgington 2021-02-20 16:03:04 +00:00
parent 7fd0899972
commit 159fc0024d
13 changed files with 308 additions and 217 deletions

View File

@ -1,7 +1,7 @@
TESTS = bls12_381_test c_kzg_util_test fft_common_test fft_fr_test fft_g1_test \
fk20_proofs_test kzg_proofs_test poly_test
fk20_proofs_test kzg_proofs_test poly_test utility_test
BENCH = fft_fr_bench fft_g1_bench
LIB_SRC = bls12_381.c c_kzg_util.c das_extension.c fft_common.c fft_fr.c fft_g1.c fk20_proofs.c kzg_proofs.c poly.c
LIB_SRC = bls12_381.c c_kzg_util.c das_extension.c fft_common.c fft_fr.c fft_g1.c fk20_proofs.c kzg_proofs.c poly.c utility.c
LIB_OBJ = $(LIB_SRC:.c=.o)
CFLAGS =

View File

@ -23,20 +23,6 @@
#include "fft_common.h"
#include "c_kzg_util.h"
/**
* Utility function to test whether the argument is a power of two.
*
* @remark This method returns `true` for `is_power_of_two(0)` which is a bit weird, but not an issue in the contexts in
* which we use it.
*
* @param[in] n The number to test
* @retval true if @p n is a power of two or zero
* @retval false otherwise
*/
bool is_power_of_two(uint64_t n) {
return (n & (n - 1)) == 0;
}
/**
* Generate powers of a root of unity in the field for use in the FFTs.
*

View File

@ -80,7 +80,6 @@ typedef struct {
fr_t *reverse_roots_of_unity; /**< Descending powers of the root of unity, size `width + 1`. */
} FFTSettings;
bool is_power_of_two(uint64_t n);
C_KZG_RET expand_root_of_unity(fr_t *out, const fr_t *root, uint64_t width);
C_KZG_RET new_fft_settings(FFTSettings *s, unsigned int max_scale);
void free_fft_settings(FFTSettings *s);

View File

@ -17,6 +17,7 @@
#include "../inc/acutest.h"
#include "test_util.h"
#include "fft_common.h"
#include "utility.h"
#define NUM_ROOTS 32
@ -78,21 +79,6 @@ void new_fft_settings_is_plausible(void) {
free_fft_settings(&s);
}
void is_power_of_two_works(void) {
// All actual powers of two
for (int i = 0; i <= 63; i++) {
TEST_CHECK(true == is_power_of_two((uint64_t)1 << i));
TEST_MSG("Case %d", i);
}
// This is a bit weird
TEST_CHECK(true == is_power_of_two(0));
// Not powers of two
TEST_CHECK(false == is_power_of_two(123));
TEST_CHECK(false == is_power_of_two(1234567));
}
TEST_LIST = {
{"FFT_COMMON_TEST", title},
{"roots_of_unity_is_the_expected_size", roots_of_unity_is_the_expected_size},
@ -100,6 +86,5 @@ TEST_LIST = {
{"roots_of_unity_are_plausible", roots_of_unity_are_plausible},
{"expand_roots_is_plausible", expand_roots_is_plausible},
{"new_fft_settings_is_plausible", new_fft_settings_is_plausible},
{"is_power_of_two_works", is_power_of_two_works},
{NULL, NULL} /* zero record marks the end of the list */
};

View File

@ -27,6 +27,7 @@
#include "fft_fr.h"
#include "c_kzg_util.h"
#include "utility.h"
/**
* Slow Fourier Transform.

View File

@ -27,6 +27,7 @@
#include "fft_g1.h"
#include "c_kzg_util.h"
#include "utility.h"
/**
* Slow Fourier Transform.

View File

@ -23,90 +23,10 @@
* @todo Split this out into smaller files.
*/
#include <string.h> // memcpy()
#include "fk20_proofs.h"
#include "fft_g1.h"
#include "c_kzg_util.h"
/**
* Calculate log base two of a power of two.
*
* In other words, the bit index of the one bit.
*
* @remark Works only for n a power of two, and only for n up to 2^31.
*
* @param[in] n The power of two
* @return the log base two of n
*/
int log2_pow2(uint32_t n) {
const uint32_t b[] = {0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000};
register uint32_t r;
r = (n & b[0]) != 0;
r |= ((n & b[1]) != 0) << 1;
r |= ((n & b[2]) != 0) << 2;
r |= ((n & b[3]) != 0) << 3;
r |= ((n & b[4]) != 0) << 4;
return r;
}
/**
* Reverse the bit order in a 32 bit integer.
*
* @remark This simply wraps the macro to enforce the type check.
*
* @param[in] a The integer to be reversed
* @return An integer with the bits of @p a reversed
*/
uint32_t reverse_bits(uint32_t a) {
return rev_4byte(a);
}
/**
* Reverse the low-order bits in a 32 bit integer.
*
* The lowest log_base_two(@p n) bits of @p value are returned reversed. @p n must be a power of two.
*
* @param[in] n To reverse `b` bits, set `n = 2 ^ b`
* @param[in] value The bits to be reversed
* @return The reversal of the lowest log_2(@p n) bits of the input @p value
*/
uint32_t reverse_bits_limited(uint32_t n, uint32_t value) {
int unused_bit_len = 32 - log2_pow2(n);
return reverse_bits(value) >> unused_bit_len;
}
/**
* Reorder an array in reverse bit order of its indices.
*
* @remark Operates in-place on the array.
* @remark Can handle arrays of any type: provide the element size in @p size.
*
* @param[in,out] values The array, which is re-ordered in-place
* @param[in] size The size in bytes of an element of the array
* @param[in] n The length of the array, must be a power of two less that 2^32
* @retval C_CZK_OK All is well
* @retval C_CZK_BADARGS Invalid parameters were supplied
*/
C_KZG_RET reverse_bit_order(void *values, size_t size, uint64_t n) {
CHECK(n >> 32 == 0);
CHECK(is_power_of_two(n));
// Pointer arithmetic on `void *` is naughty, so cast to something definite
byte *v = values;
byte tmp[size];
int unused_bit_len = 32 - log2_pow2(n);
for (uint32_t i = 0; i < n; i++) {
uint32_t r = reverse_bits(i) >> unused_bit_len;
if (r > i) {
// Swap the two elements
memcpy(tmp, v + (i * size), size);
memcpy(v + (i * size), v + (r * size), size);
memcpy(v + (r * size), tmp, size);
}
}
return C_KZG_OK;
}
#include "utility.h"
/**
* The first part of the Toeplitz matrix multiplication algorithm: the Fourier

View File

@ -18,26 +18,6 @@
#include "kzg_proofs.h"
/**
* Reverse the bits in a byte.
*
* From https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
*
* @param a A byte
* @return A byte that is bit-reversed with respect to @p a
*
* @todo Benchmark some of the other bit-reversal options in the list. Maybe.
*/
#define rev_byte(a) ((((a)&0xff) * 0x0202020202ULL & 0x010884422010ULL) % 1023)
/**
* Reverse the bits in a 32 bit word.
*
* @param a A 32 bit unsigned integer
* @return A 32 bit unsigned integer that is bit-reversed with respect to @p a
*/
#define rev_4byte(a) (rev_byte(a) << 24 | rev_byte((a) >> 8) << 16 | rev_byte((a) >> 16) << 8 | rev_byte((a) >> 24))
/**
* Stores the setup and parameters needed for computing FK20 single proofs.
*
@ -59,10 +39,6 @@ typedef struct {
uint64_t length; /**< TODO */
} FK20MultiSettings;
int log2_pow2(uint32_t n);
uint32_t reverse_bits(uint32_t a);
uint32_t reverse_bits_limited(uint32_t n, uint32_t value);
C_KZG_RET reverse_bit_order(void *values, size_t size, uint64_t n);
C_KZG_RET toeplitz_part_1(g1_t *out, const g1_t *x, uint64_t n, const FFTSettings *fs);
C_KZG_RET toeplitz_part_2(g1_t *out, const poly *toeplitz_coeffs, const g1_t *x_ext_fft, const FFTSettings *fs);
C_KZG_RET toeplitz_part_3(g1_t *out, const g1_t *h_ext_fft, uint64_t n2, const FFTSettings *fs);

View File

@ -18,79 +18,7 @@
#include "test_util.h"
#include "fk20_proofs.h"
#include "c_kzg_util.h"
void test_reverse_bits_macros(void) {
TEST_CHECK(128 == rev_byte(1));
TEST_CHECK(128 == rev_byte(257));
TEST_CHECK((uint32_t)1 << 31 == rev_4byte(1));
TEST_CHECK(0x1e6a2c48 == rev_4byte(0x12345678));
TEST_CHECK(0x00000000 == rev_4byte(0x00000000));
TEST_CHECK(0xffffffff == rev_4byte(0xffffffff));
}
void test_reverse_bits_0(void) {
uint32_t actual, expected;
for (int i = 0; i < 32; i++) {
expected = (uint32_t)1 << (31 - i);
actual = reverse_bits((uint32_t)1 << i);
TEST_CHECK(expected == actual);
}
}
void test_reverse_bits_1(void) {
TEST_CHECK(0x84c2a6e1 == reverse_bits(0x87654321));
}
void test_log2_pow2(void) {
int actual, expected;
for (int i = 0; i < 32; i++) {
expected = i;
actual = log2_pow2((uint32_t)1 << i);
TEST_CHECK(expected == actual);
}
}
void test_reverse_bit_order_g1(void) {
int size = 10, n = 1 << size;
g1_t a[n], b[n];
fr_t tmp;
for (int i = 0; i < n; i++) {
fr_from_uint64(&tmp, i);
g1_mul(&a[i], &g1_generator, &tmp);
b[i] = a[i];
}
TEST_CHECK(C_KZG_OK == reverse_bit_order(a, sizeof(g1_t), n));
for (int i = 0; i < n; i++) {
TEST_CHECK(true == g1_equal(&b[reverse_bits(i) >> (32 - size)], &a[i]));
}
// Hand check a few select values
TEST_CHECK(true == g1_equal(&b[0], &a[0]));
TEST_CHECK(false == g1_equal(&b[1], &a[1]));
TEST_CHECK(true == g1_equal(&b[n - 1], &a[n - 1]));
}
void test_reverse_bit_order_fr(void) {
int size = 12, n = 1 << size;
fr_t a[n], b[n];
for (int i = 0; i < n; i++) {
fr_from_uint64(&a[i], i);
b[i] = a[i];
}
TEST_CHECK(C_KZG_OK == reverse_bit_order(a, sizeof(fr_t), n));
for (int i = 0; i < n; i++) {
TEST_CHECK(true == fr_equal(&b[reverse_bits(i) >> (32 - size)], &a[i]));
}
// Hand check a few select values
TEST_CHECK(true == fr_equal(&b[0], &a[0]));
TEST_CHECK(false == fr_equal(&b[1], &a[1]));
TEST_CHECK(true == fr_equal(&b[n - 1], &a[n - 1]));
}
#include "utility.h"
void fk_single(void) {
// Our polynomial: degree 15, 16 coefficients
@ -354,12 +282,6 @@ void fk_multi_0(void) {
TEST_LIST = {
{"FK20_PROOFS_TEST", title},
{"test_reverse_bits_macros", test_reverse_bits_macros},
{"test_reverse_bits_0", test_reverse_bits_0},
{"test_reverse_bits_1", test_reverse_bits_1},
{"test_log2_pow2", test_log2_pow2},
{"test_reverse_bit_order_g1", test_reverse_bit_order_g1},
{"test_reverse_bit_order_fr", test_reverse_bit_order_fr},
{"fk_single", fk_single},
{"fk_single_strided", fk_single_strided},
{"fk_multi_settings", fk_multi_settings},

View File

@ -26,6 +26,7 @@
#include <stddef.h> // NULL
#include "kzg_proofs.h"
#include "c_kzg_util.h"
#include "utility.h"
/**
* Make a KZG commitment to a polynomial.

131
src/utility.c Normal file
View File

@ -0,0 +1,131 @@
/*
* Copyright 2021 Benjamin Edgington
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file utility.c
*
* A collection of useful functions used in various places throughout the library.
*/
#include <string.h> // memcpy()
#include "utility.h"
/**
* Utility function to test whether the argument is a power of two.
*
* @remark This method returns `true` for `is_power_of_two(0)` which is a bit weird, but not an issue in the contexts in
* which we use it.
*
* @param[in] n The number to test
* @retval true if @p n is a power of two or zero
* @retval false otherwise
*/
bool is_power_of_two(uint64_t n) {
return (n & (n - 1)) == 0;
}
/**
* Calculate log base two of a power of two.
*
* In other words, the bit index of the one bit.
*
* @remark Works only for n a power of two, and only for n up to 2^31.
*
* @param[in] n The power of two
* @return the log base two of n
*/
int log2_pow2(uint32_t n) {
const uint32_t b[] = {0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000};
register uint32_t r;
r = (n & b[0]) != 0;
r |= ((n & b[1]) != 0) << 1;
r |= ((n & b[2]) != 0) << 2;
r |= ((n & b[3]) != 0) << 3;
r |= ((n & b[4]) != 0) << 4;
return r;
}
// Adapted from https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
uint64_t next_power_of_two(uint64_t v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v |= v >> 32;
v++;
return v += (v == 0);
}
/**
* Reverse the bit order in a 32 bit integer.
*
* @remark This simply wraps the macro to enforce the type check.
*
* @param[in] a The integer to be reversed
* @return An integer with the bits of @p a reversed
*/
uint32_t reverse_bits(uint32_t a) {
return rev_4byte(a);
}
/**
* Reverse the low-order bits in a 32 bit integer.
*
* The lowest log_base_two(@p n) bits of @p value are returned reversed. @p n must be a power of two.
*
* @param[in] n To reverse `b` bits, set `n = 2 ^ b`
* @param[in] value The bits to be reversed
* @return The reversal of the lowest log_2(@p n) bits of the input @p value
*/
uint32_t reverse_bits_limited(uint32_t n, uint32_t value) {
int unused_bit_len = 32 - log2_pow2(n);
return reverse_bits(value) >> unused_bit_len;
}
/**
* Reorder an array in reverse bit order of its indices.
*
* @remark Operates in-place on the array.
* @remark Can handle arrays of any type: provide the element size in @p size.
*
* @param[in,out] values The array, which is re-ordered in-place
* @param[in] size The size in bytes of an element of the array
* @param[in] n The length of the array, must be a power of two less that 2^32
* @retval C_CZK_OK All is well
* @retval C_CZK_BADARGS Invalid parameters were supplied
*/
C_KZG_RET reverse_bit_order(void *values, size_t size, uint64_t n) {
CHECK(n >> 32 == 0);
CHECK(is_power_of_two(n));
// Pointer arithmetic on `void *` is naughty, so cast to something definite
byte *v = values;
byte tmp[size];
int unused_bit_len = 32 - log2_pow2(n);
for (uint32_t i = 0; i < n; i++) {
uint32_t r = reverse_bits(i) >> unused_bit_len;
if (r > i) {
// Swap the two elements
memcpy(tmp, v + (i * size), size);
memcpy(v + (i * size), v + (r * size), size);
memcpy(v + (r * size), tmp, size);
}
}
return C_KZG_OK;
}

50
src/utility.h Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright 2021 Benjamin Edgington
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file utility.h
*
* A collection of useful functions used in various places throughout the library.
*/
#include "c_kzg.h"
/**
* Reverse the bits in a byte.
*
* From https://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64BitsDiv
*
* @param a A byte
* @return A byte that is bit-reversed with respect to @p a
*
* @todo Benchmark some of the other bit-reversal options in the list. Maybe.
*/
#define rev_byte(a) ((((a)&0xff) * 0x0202020202ULL & 0x010884422010ULL) % 1023)
/**
* Reverse the bits in a 32 bit word.
*
* @param a A 32 bit unsigned integer
* @return A 32 bit unsigned integer that is bit-reversed with respect to @p a
*/
#define rev_4byte(a) (rev_byte(a) << 24 | rev_byte((a) >> 8) << 16 | rev_byte((a) >> 16) << 8 | rev_byte((a) >> 24))
bool is_power_of_two(uint64_t n);
int log2_pow2(uint32_t n);
uint64_t next_power_of_two(uint64_t v);
uint32_t reverse_bits(uint32_t a);
uint32_t reverse_bits_limited(uint32_t n, uint32_t value);
C_KZG_RET reverse_bit_order(void *values, size_t size, uint64_t n);

119
src/utility_test.c Normal file
View File

@ -0,0 +1,119 @@
/*
* Copyright 2021 Benjamin Edgington
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../inc/acutest.h"
#include "test_util.h"
#include "utility.h"
void is_power_of_two_works(void) {
// All actual powers of two
for (int i = 0; i <= 63; i++) {
TEST_CHECK(true == is_power_of_two((uint64_t)1 << i));
TEST_MSG("Case %d", i);
}
// This is a bit weird
TEST_CHECK(true == is_power_of_two(0));
// Not powers of two
TEST_CHECK(false == is_power_of_two(123));
TEST_CHECK(false == is_power_of_two(1234567));
}
void test_reverse_bits_macros(void) {
TEST_CHECK(128 == rev_byte(1));
TEST_CHECK(128 == rev_byte(257));
TEST_CHECK((uint32_t)1 << 31 == rev_4byte(1));
TEST_CHECK(0x1e6a2c48 == rev_4byte(0x12345678));
TEST_CHECK(0x00000000 == rev_4byte(0x00000000));
TEST_CHECK(0xffffffff == rev_4byte(0xffffffff));
}
void test_reverse_bits_0(void) {
uint32_t actual, expected;
for (int i = 0; i < 32; i++) {
expected = (uint32_t)1 << (31 - i);
actual = reverse_bits((uint32_t)1 << i);
TEST_CHECK(expected == actual);
}
}
void test_reverse_bits_1(void) {
TEST_CHECK(0x84c2a6e1 == reverse_bits(0x87654321));
}
void test_log2_pow2(void) {
int actual, expected;
for (int i = 0; i < 32; i++) {
expected = i;
actual = log2_pow2((uint32_t)1 << i);
TEST_CHECK(expected == actual);
}
}
void test_reverse_bit_order_g1(void) {
int size = 10, n = 1 << size;
g1_t a[n], b[n];
fr_t tmp;
for (int i = 0; i < n; i++) {
fr_from_uint64(&tmp, i);
g1_mul(&a[i], &g1_generator, &tmp);
b[i] = a[i];
}
TEST_CHECK(C_KZG_OK == reverse_bit_order(a, sizeof(g1_t), n));
for (int i = 0; i < n; i++) {
TEST_CHECK(true == g1_equal(&b[reverse_bits(i) >> (32 - size)], &a[i]));
}
// Hand check a few select values
TEST_CHECK(true == g1_equal(&b[0], &a[0]));
TEST_CHECK(false == g1_equal(&b[1], &a[1]));
TEST_CHECK(true == g1_equal(&b[n - 1], &a[n - 1]));
}
void test_reverse_bit_order_fr(void) {
int size = 12, n = 1 << size;
fr_t a[n], b[n];
for (int i = 0; i < n; i++) {
fr_from_uint64(&a[i], i);
b[i] = a[i];
}
TEST_CHECK(C_KZG_OK == reverse_bit_order(a, sizeof(fr_t), n));
for (int i = 0; i < n; i++) {
TEST_CHECK(true == fr_equal(&b[reverse_bits(i) >> (32 - size)], &a[i]));
}
// Hand check a few select values
TEST_CHECK(true == fr_equal(&b[0], &a[0]));
TEST_CHECK(false == fr_equal(&b[1], &a[1]));
TEST_CHECK(true == fr_equal(&b[n - 1], &a[n - 1]));
}
TEST_LIST = {
{"UTILITY_TEST", title},
{"is_power_of_two_works", is_power_of_two_works},
{"test_reverse_bits_macros", test_reverse_bits_macros},
{"test_reverse_bits_0", test_reverse_bits_0},
{"test_reverse_bits_1", test_reverse_bits_1},
{"test_log2_pow2", test_log2_pow2},
{"test_reverse_bit_order_g1", test_reverse_bit_order_g1},
{"test_reverse_bit_order_fr", test_reverse_bit_order_fr},
{NULL, NULL} /* zero record marks the end of the list */
};