Documentation
This commit is contained in:
parent
7b023e10c4
commit
8497b7bc4b
|
@ -14,46 +14,82 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** @file fft_common.c */
|
||||
/**
|
||||
* @file fft_common.c
|
||||
*
|
||||
* Code shared between the FFTs over field elements and FFTs over G1 group elements.
|
||||
*/
|
||||
|
||||
#include <stdlib.h> // free()
|
||||
#include "fft_common.h"
|
||||
#include "blst_util.h"
|
||||
#include "c_kzg_util.h"
|
||||
|
||||
bool is_power_of_two(const uint64_t n) {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
// Create an array of powers of the root of unity
|
||||
// The `out` array must be of size `width + 1`
|
||||
C_KZG_RET expand_root_of_unity(blst_fr *roots, const blst_fr *root_of_unity, const uint64_t width) {
|
||||
roots[0] = fr_one;
|
||||
roots[1] = *root_of_unity;
|
||||
/**
|
||||
* Generate powers of a root of unity in the field for use in the FFTs.
|
||||
*
|
||||
* @remark @p root must be such that @p root ^ @p width is equal to one, but no smaller power of @p root is equal to
|
||||
* one.
|
||||
*
|
||||
* @param[out] out The generated powers of the root of unity (array size @p width + 1)
|
||||
* @param[in] root A root of unity
|
||||
* @param[in] width One less than the size of @p out
|
||||
* @retval C_CZK_OK All is well
|
||||
* @retval C_CZK_BADARGS Invalid parameters were supplied
|
||||
*/
|
||||
C_KZG_RET expand_root_of_unity(blst_fr *out, const blst_fr *root, uint64_t width) {
|
||||
out[0] = fr_one;
|
||||
out[1] = *root;
|
||||
|
||||
for (uint64_t i = 2; !fr_is_one(&roots[i - 1]); i++) {
|
||||
ASSERT(i <= width, C_KZG_ERROR);
|
||||
blst_fr_mul(&roots[i], &roots[i - 1], root_of_unity);
|
||||
for (uint64_t i = 2; !fr_is_one(&out[i - 1]); i++) {
|
||||
ASSERT(i <= width, C_KZG_BADARGS);
|
||||
blst_fr_mul(&out[i], &out[i - 1], root);
|
||||
}
|
||||
ASSERT(fr_is_one(&roots[width]), C_KZG_ERROR);
|
||||
ASSERT(fr_is_one(&out[width]), C_KZG_BADARGS);
|
||||
|
||||
return C_KZG_OK;
|
||||
}
|
||||
|
||||
// Create a reversed list of Fr provided
|
||||
// `width` is one less than the length of `roots`
|
||||
C_KZG_RET reverse(blst_fr *out, const blst_fr *roots, const uint64_t width) {
|
||||
for (uint64_t i = 0; i <= width; i++) {
|
||||
out[i] = roots[width - i];
|
||||
}
|
||||
|
||||
return C_KZG_OK;
|
||||
}
|
||||
|
||||
C_KZG_RET new_fft_settings(FFTSettings *fs, const unsigned int max_scale) {
|
||||
/**
|
||||
* Initialise an FFTSettings structure.
|
||||
*
|
||||
* Space is allocated for, and arrays are populated with, powers of the roots of unity. The two arrays contain the same
|
||||
* values in reverse order for convenience in inverse FFTs.
|
||||
*
|
||||
* `max_width` is the maximum size of FFT that can be calculated with these settings, and is a power of two by
|
||||
* construction. The same settings may be used to calculated FFTs of smaller power sizes.
|
||||
*
|
||||
* @remark This structure *must* be deallocated after use by calling #free_fft_settings.
|
||||
* @remark These settings may be used for FFTs on both field elements and G1 group elements.
|
||||
*
|
||||
* @param[out] fs The new settings
|
||||
* @param[in] max_scale Log base 2 of the max FFT size to be used with these settings
|
||||
* @retval C_CZK_OK All is well
|
||||
* @retval C_CZK_BADARGS Invalid parameters were supplied
|
||||
* @retval C_CZK_ERROR An internal error occurred
|
||||
* @retval C_CZK_MALLOC Memory allocation failed
|
||||
*/
|
||||
C_KZG_RET new_fft_settings(FFTSettings *fs, unsigned int max_scale) {
|
||||
fs->max_width = (uint64_t)1 << max_scale;
|
||||
|
||||
ASSERT((max_scale < sizeof scale2_root_of_unity / sizeof scale2_root_of_unity[0]), C_KZG_BADARGS);
|
||||
blst_fr_from_uint64(&fs->root_of_unity, scale2_root_of_unity[max_scale]);
|
||||
|
||||
// Allocate space for the roots of unity
|
||||
ASSERT(c_kzg_malloc((void **)&fs->expanded_roots_of_unity,
|
||||
(fs->max_width + 1) * sizeof *fs->expanded_roots_of_unity) == C_KZG_OK,
|
||||
C_KZG_MALLOC);
|
||||
|
@ -61,13 +97,25 @@ C_KZG_RET new_fft_settings(FFTSettings *fs, const unsigned int max_scale) {
|
|||
(fs->max_width + 1) * sizeof *fs->reverse_roots_of_unity) == C_KZG_OK,
|
||||
C_KZG_MALLOC);
|
||||
|
||||
// Populate the roots of unity
|
||||
ASSERT(expand_root_of_unity(fs->expanded_roots_of_unity, &fs->root_of_unity, fs->max_width) == C_KZG_OK,
|
||||
C_KZG_ERROR);
|
||||
ASSERT(reverse(fs->reverse_roots_of_unity, fs->expanded_roots_of_unity, fs->max_width) == C_KZG_OK, C_KZG_ERROR);
|
||||
|
||||
// Populate reverse roots of unity
|
||||
for (uint64_t i = 0; i <= fs->max_width; i++) {
|
||||
fs->reverse_roots_of_unity[i] = fs->expanded_roots_of_unity[fs->max_width - i];
|
||||
}
|
||||
|
||||
return C_KZG_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the memory that was previously allocated by #new_fft_settings.
|
||||
*
|
||||
* @param fs The settings to be freed
|
||||
*/
|
||||
void free_fft_settings(FFTSettings *fs) {
|
||||
free(fs->expanded_roots_of_unity);
|
||||
free(fs->reverse_roots_of_unity);
|
||||
fs->max_width = 0;
|
||||
}
|
||||
|
|
|
@ -21,11 +21,19 @@
|
|||
|
||||
#include "c_kzg.h"
|
||||
|
||||
// MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513
|
||||
// PRIMITIVE_ROOT = 5
|
||||
// [pow(PRIMITIVE_ROOT, (MODULUS - 1) // (2**i), MODULUS) for i in range(32)]
|
||||
//
|
||||
// These are not in `blst_fr` limb format and must be converted via `blst_fr_from_uint64()`
|
||||
/**
|
||||
* The first 32 roots of unity in the finite field F_r.
|
||||
*
|
||||
* For element `{A, B, C, D}`, the field element value is `A + B * 2^64 + C * 2^128 + D * 2^192`. This format may be
|
||||
* converted to a `blst_fr` type via the blst_fr_from_uint64() library function.
|
||||
*
|
||||
* The decimal values may be calculated with the following Python code:
|
||||
* @code{.py}
|
||||
* MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513
|
||||
* PRIMITIVE_ROOT = 5
|
||||
* [pow(PRIMITIVE_ROOT, (MODULUS - 1) // (2**i), MODULUS) for i in range(32)]
|
||||
* @endcode
|
||||
*/
|
||||
static const uint64_t scale2_root_of_unity[][4] = {
|
||||
{0x0000000000000001L, 0x0000000000000000L, 0x0000000000000000L, 0x0000000000000000L},
|
||||
{0xffffffff00000000L, 0x53bda402fffe5bfeL, 0x3339d80809a1d805L, 0x73eda753299d7d48L},
|
||||
|
@ -60,17 +68,21 @@ static const uint64_t scale2_root_of_unity[][4] = {
|
|||
{0x8409a9ea14ebb608L, 0xfad0084e66bac611L, 0x04287254811c1dfbL, 0x086d072b23b30c29L},
|
||||
{0xb427c9b367e4756aL, 0xc7537fb902ebc38dL, 0x51de21becd6a205fL, 0x6064ab727923597dL}};
|
||||
|
||||
/**
|
||||
* Stores the setup and parameters needed for performing FFTs.
|
||||
*
|
||||
* Initialise with #new_fft_settings. Free after use with #free_fft_settings.
|
||||
*/
|
||||
typedef struct {
|
||||
uint64_t max_width;
|
||||
blst_fr root_of_unity;
|
||||
blst_fr *expanded_roots_of_unity;
|
||||
blst_fr *reverse_roots_of_unity;
|
||||
uint64_t max_width; /**< The maximum size of FFT these settings support, a power of 2. */
|
||||
blst_fr root_of_unity; /**< The root of unity used to generate the lists in the structure. */
|
||||
blst_fr *expanded_roots_of_unity; /**< Ascending powers of the root of unity, size `width + 1`. */
|
||||
blst_fr *reverse_roots_of_unity; /**< Descending powers of the root of unity, size `width + 1`. */
|
||||
} FFTSettings;
|
||||
|
||||
bool is_power_of_two(const uint64_t n);
|
||||
C_KZG_RET expand_root_of_unity(blst_fr *roots, const blst_fr *root_of_unity, const uint64_t width);
|
||||
C_KZG_RET reverse(blst_fr *out, const blst_fr *roots, const uint64_t width);
|
||||
C_KZG_RET new_fft_settings(FFTSettings *s, const unsigned int max_scale);
|
||||
bool is_power_of_two(uint64_t n);
|
||||
C_KZG_RET expand_root_of_unity(blst_fr *out, const blst_fr *root, uint64_t width);
|
||||
C_KZG_RET new_fft_settings(FFTSettings *s, unsigned int max_scale);
|
||||
void free_fft_settings(FFTSettings *s);
|
||||
|
||||
#endif
|
||||
#endif // FFT_COMMON
|
|
@ -26,6 +26,11 @@ void roots_of_unity_is_the_expected_size(void) {
|
|||
TEST_CHECK(NUM_ROOTS == sizeof(scale2_root_of_unity) / sizeof(scale2_root_of_unity[0]));
|
||||
}
|
||||
|
||||
void roots_of_unity_out_of_bounds_fails(void) {
|
||||
FFTSettings fs;
|
||||
TEST_CHECK(C_KZG_BADARGS == new_fft_settings(&fs, NUM_ROOTS));
|
||||
}
|
||||
|
||||
void roots_of_unity_are_plausible(void) {
|
||||
blst_fr r;
|
||||
for (int i = 0; i < NUM_ROOTS; i++) {
|
||||
|
@ -38,28 +43,6 @@ void roots_of_unity_are_plausible(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void reverse_works(void) {
|
||||
int n = 24;
|
||||
blst_fr arr[n + 1], rev[n + 1];
|
||||
blst_fr diff;
|
||||
|
||||
// Initialise - increasing values
|
||||
arr[0] = fr_one;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
blst_fr_add(arr + i, arr + i - 1, &fr_one);
|
||||
}
|
||||
|
||||
// Reverse
|
||||
TEST_CHECK(reverse(rev, arr, n) == C_KZG_OK);
|
||||
|
||||
// Verify - decreasing values
|
||||
for (int i = 0; i < n; i++) {
|
||||
blst_fr_sub(&diff, rev + i, rev + i + 1);
|
||||
TEST_CHECK(true == fr_is_one(&diff));
|
||||
}
|
||||
TEST_CHECK(true == fr_is_one(rev + n));
|
||||
}
|
||||
|
||||
void expand_roots_is_plausible(void) {
|
||||
// Just test one (largeish) value of scale
|
||||
unsigned int scale = 15;
|
||||
|
@ -115,8 +98,8 @@ void is_power_of_two_works(void) {
|
|||
TEST_LIST = {
|
||||
{"FFT_COMMON_TEST", title},
|
||||
{"roots_of_unity_is_the_expected_size", roots_of_unity_is_the_expected_size},
|
||||
{"roots_of_unity_out_of_bounds_fails", roots_of_unity_out_of_bounds_fails},
|
||||
{"roots_of_unity_are_plausible", roots_of_unity_are_plausible},
|
||||
{"reverse_works", reverse_works},
|
||||
{"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},
|
||||
|
|
Loading…
Reference in New Issue