Documentation

This commit is contained in:
Ben Edgington 2021-02-11 19:36:35 +00:00
parent 7b023e10c4
commit 8497b7bc4b
3 changed files with 103 additions and 60 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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},