Make zero poly work for large numbers of missing indices

Previously, like the Go code, calculating the zero polynomial would fail
for very lare numbers of missing indices. For example, 253 missing with
a domain size of 256 - this is where the number of partials flips from 4
to 5 and more working space is needed.

With this commit, the zero polynomial can be calculated right up to all
but one of the indices missing. The case with all indices missing
doesn't work as the return data is too large, but the solution is known
to be `x^width - 1` in case we need to know.
This commit is contained in:
Ben Edgington 2021-03-03 21:51:32 +00:00
parent f09d1a70b2
commit 9d1b622f21
4 changed files with 116 additions and 131 deletions

View File

@ -108,17 +108,17 @@ C_KZG_RET recover_poly_from_samples(fr_t *reconstructed_data, fr_t *samples, uin
fr_t *scratch2 = scratch1 + len_samples;
// Assign meaningful names to scratch spaces
poly zero_poly;
fr_t *zero_eval = scratch0;
fr_t *zero_poly = scratch1;
zero_poly.coeffs = scratch1;
fr_t *poly_evaluations_with_zero = scratch2;
fr_t *poly_with_zero = scratch0;
fr_t *eval_shifted_poly_with_zero = scratch2;
fr_t *eval_shifted_zero_poly = scratch0;
fr_t *shifted_reconstructed_poly = scratch1;
uint64_t zero_poly_len;
TRY(zero_polynomial_via_multiplication(zero_eval, zero_poly, &zero_poly_len, len_samples, missing, len_missing,
fs));
// Calculate `Z_r,I`
TRY(zero_polynomial_via_multiplication(zero_eval, &zero_poly, len_samples, missing, len_missing, fs));
// Check all is well
for (uint64_t i = 0; i < len_samples; i++) {
@ -134,11 +134,10 @@ C_KZG_RET recover_poly_from_samples(fr_t *reconstructed_data, fr_t *samples, uin
}
TRY(fft_fr(poly_with_zero, poly_evaluations_with_zero, true, len_samples, fs));
shift_poly(poly_with_zero, len_samples);
shift_poly(zero_poly, zero_poly_len);
shift_poly(zero_poly.coeffs, zero_poly.length);
// Renamings:
fr_t *shifted_poly_with_zero = poly_with_zero;
fr_t *shifted_zero_poly = zero_poly;
fr_t *shifted_poly_with_zero = poly_with_zero; // Renaming
fr_t *shifted_zero_poly = zero_poly.coeffs; // Renaming
TRY(fft_fr(eval_shifted_poly_with_zero, shifted_poly_with_zero, false, len_samples, fs));
TRY(fft_fr(eval_shifted_zero_poly, shifted_zero_poly, false, len_samples, fs));
@ -152,9 +151,7 @@ C_KZG_RET recover_poly_from_samples(fr_t *reconstructed_data, fr_t *samples, uin
unshift_poly(shifted_reconstructed_poly, len_samples);
// Renaming:
fr_t *reconstructed_poly = shifted_reconstructed_poly;
fr_t *reconstructed_poly = shifted_reconstructed_poly; // Renaming
TRY(fft_fr(reconstructed_data, reconstructed_poly, false, len_samples, fs));
// Check all is well

View File

@ -31,8 +31,8 @@
* Uses straightforward multiplication to calculate the product of `(x - r^i)` where `r` is a root of unity and the `i`s
* are the indices at which it must evaluate to zero. This results in a polynomial of degree @p len_indices.
*
* @param[out] dst The resulting polynomial, length @p len_dst
* @param[in] len_dst Length of the output polynomial, @p dst
* @param[in,out] dst The zero polynomial for @p indices. The space allocated for coefficients must be at least @p
* len_indices + 1, as indicated by the `length` value on entry.
* @param[in] indices Array of missing indices of length @p len_indices
* @param[in] len_indices Length of the missing indices array, @p indices
* @param[in] stride Stride length through the powers of the root of unity
@ -42,41 +42,42 @@
*
* @todo rework to pass polynomials in and out
*/
C_KZG_RET do_zero_poly_mul_partial(fr_t *dst, uint64_t len_dst, const uint64_t *indices, uint64_t len_indices,
uint64_t stride, const FFTSettings *fs) {
C_KZG_RET do_zero_poly_mul_partial(poly *dst, const uint64_t *indices, uint64_t len_indices, uint64_t stride,
const FFTSettings *fs) {
CHECK(len_dst >= len_indices + 1);
CHECK(dst->length >= len_indices + 1);
for (uint64_t i = 0; i < len_indices; i++) {
fr_t neg_di;
fr_negate(&neg_di, &fs->expanded_roots_of_unity[indices[i] * stride]);
dst[i] = neg_di;
dst->coeffs[i] = neg_di;
if (i > 0) {
fr_add(&dst[i], &dst[i], &dst[i - 1]);
fr_add(&dst->coeffs[i], &dst->coeffs[i], &dst->coeffs[i - 1]);
for (uint64_t j = i - 1; j > 0; j--) {
fr_mul(&dst[j], &dst[j], &neg_di);
fr_add(&dst[j], &dst[j], &dst[j - 1]);
fr_mul(&dst->coeffs[j], &dst->coeffs[j], &neg_di);
fr_add(&dst->coeffs[j], &dst->coeffs[j], &dst->coeffs[j - 1]);
}
fr_mul(&dst[0], &dst[0], &neg_di);
fr_mul(&dst->coeffs[0], &dst->coeffs[0], &neg_di);
}
}
dst[len_indices] = fr_one;
dst->coeffs[len_indices] = fr_one;
for (uint64_t i = len_indices + 1; i < len_dst; i++) {
dst[i] = fr_zero;
for (uint64_t i = len_indices + 1; i < dst->length; i++) {
dst->coeffs[i] = fr_zero;
}
dst->length = len_indices + 1;
return C_KZG_OK;
}
/**
* Copy @p p to @p out, padding to length @p p_len with zeros.
* Copy all of the coefficients of polynomial @p p to @p out, padding to length @p p_len with zeros.
*
* @param[out] out A copy of @p p padded to length @p n with zeros
* @param[out] out A copy of the coefficients of @p p padded to length @p out_len with zeros
* @param[in] out_len The length of the desired output data, @p out
* @param[in] p The data to be copied and padded, length @p p_len
* @param[in] p_len The length of the data to be copied and padded
* @param[in] p The polynomial containing the data to be copied and padded
* @retval C_CZK_OK All is well
* @retval C_CZK_BADARGS Invalid parameters were supplied
*/
@ -97,13 +98,12 @@ C_KZG_RET pad_p(fr_t *out, uint64_t out_len, const poly *p) {
* Pad the polynomials in @p ps, perform FFTs, point-wise multiply the results together, and apply an inverse FFT to the
* result.
*
* @param[out] dst The result of the convolution
* @param[in] len_dst Length of the output, a power of two
* @param scratch Scratch space of size at least 3 times the output size
* @param[in] len_scratch Length of @p scratch, at least 3 x @p len_dst
* @param[in] ps Array of polynomial coefficients ps[@p len_ps][@p len_p]
* @param[in] len_ps The number of polynomials
* @param[in] len_p Array of lengths of each polynomial, size @p len_ps
* @param[out] out Polynomial with @p len_out space allocated. The length will be set on return.
* @param[in] len_out Length of the domain of evaluation, a power of two
* @param scratch Scratch space of size at least 3 times the @p len_out
* @param[in] len_scratch Length of @p scratch, at least 3 times @p len_out
* @param[in] partials Array of polynomials to be multiplied together
* @param[in] partial_count The number of polynomials to be multiplied together
* @param[in] fs The FFT settings previously initialised with #new_fft_settings
* @retval C_CZK_OK All is well
* @retval C_CZK_BADARGS Invalid parameters were supplied
@ -148,25 +148,22 @@ C_KZG_RET reduce_partials(poly *out, uint64_t len_out, fr_t *scratch, uint64_t l
* Calculate the minimal polynomial that evaluates to zero for powers of roots of unity that correspond to missing
* indices.
*
* This is done by simply multiplying together `(x - r^i)` for all the `i` that are missing indices, using a combination
* of direct multiplication (#do_zero_poly_mul_partial) and multiplication via convolution (#reduce_partials).
* This is done simply by multiplying together `(x - r^i)` for all the `i` that are missing indices, using a combination
* of direct multiplication (#do_zero_poly_mul_partial) and iterated multiplication via convolution (#reduce_partials).
*
* Also calculates the FFT (the "evaluation polynomial").
*
* @remark Fails for very high numbers of missing indices. For example, with `fs.max_width = 256` and `length = 256`,
* this will fail for len_missing = 253 or more. In this case, `length` (and maybe `fs.max_width`) needs to be doubled.
* But this failure is probably OK for our use case. TODO: no longer true. But it does fail if the whole domain is
* missing. We know the answer for that case anyway.
* @remark This fails when all the indices in our domain are missing (@p len_missing == @p length), since the resulting
* polynomial exceeds the size allocated. But we know that the answer is `x^length - 1` in that case if we ever need it.
*
* @remark Note that @p zero_poly is used as workspace during calculation.
*
* @param[out] zero_eval Array length @p length (TODO: description)
* @param[out] zero_poly Array length @p length (TODO: description)
* @param[out] zero_poly_len The length of the resulting @p zero_poly
* @param[in] length Length of the output arrays
* @param[in] missing_indices Array length @p len_missing (TODO: description)
* @param[in] len_missing Length of @p missing_indices
* @param[in] fs The FFT settings previously initialised with #new_fft_settings
* @param[out] zero_eval The "evaluation polynomial": the coefficients are the values of @p zero_poly for each power of
* `r`. Space required is @p length.
* @param[out] zero_poly The zero polynomial. On return the length will be set to `len_missing + 1` and the remaining
* coefficients set to zero. Space required is @p length.
* @param[in] length Size of the domain of evaluation (number of powers of `r`)
* @param[in] missing_indices Array length @p len_missing containing the indices of the missing coefficients
* @param[in] len_missing Length of @p missing_indices
* @param[in] fs The FFT settings previously initialised with #new_fft_settings
* @retval C_CZK_OK All is well
* @retval C_CZK_BADARGS Invalid parameters were supplied
* @retval C_CZK_ERROR An internal error occurred
@ -174,18 +171,18 @@ C_KZG_RET reduce_partials(poly *out, uint64_t len_out, fr_t *scratch, uint64_t l
*
* @todo What is the performance impact of tuning `degree_of_partial` and `reduction factor`?
*/
C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, fr_t *zero_poly, uint64_t *zero_poly_len, uint64_t length,
C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, poly *zero_poly, uint64_t length,
const uint64_t *missing_indices, uint64_t len_missing,
const FFTSettings *fs) {
if (len_missing == 0) {
*zero_poly_len = 0;
zero_poly->length = 0;
for (uint64_t i = 0; i < length; i++) {
zero_eval[i] = fr_zero;
zero_poly[i] = fr_zero;
zero_poly->coeffs[i] = fr_zero;
}
return C_KZG_OK;
}
CHECK(len_missing < length); // The output would be larger than length otherwise, (TODO describe in docs)
CHECK(len_missing < length);
CHECK(length <= fs->max_width);
CHECK(is_power_of_two(length));
@ -197,16 +194,15 @@ C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, fr_t *zero_poly, u
if (n > length) n = length;
if (len_missing <= missing_per_partial) {
TRY(do_zero_poly_mul_partial(zero_poly, length, missing_indices, len_missing, domain_stride, fs));
TRY(fft_fr(zero_eval, zero_poly, false, length, fs));
*zero_poly_len = len_missing + 1;
TRY(do_zero_poly_mul_partial(zero_poly, missing_indices, len_missing, domain_stride, fs));
TRY(fft_fr(zero_eval, zero_poly->coeffs, false, length, fs));
} else {
// Work space for building and reducing the partials
fr_t *work;
TRY(new_fr_array(&work, next_power_of_two(partial_count * degree_of_partial)));
// Build the partials.
// Build the partials from the missing indices
// Just allocate pointers here since we're re-using `work` for the partial processing
// Combining partials can be done mostly in-place, using a scratchpad.
@ -218,15 +214,14 @@ C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, fr_t *zero_poly, u
if (end > max) end = max;
partials[i].coeffs = &work[out_offset];
partials[i].length = degree_of_partial;
TRY(do_zero_poly_mul_partial(partials[i].coeffs, degree_of_partial, &missing_indices[offset], end - offset,
domain_stride, fs));
TRY(do_zero_poly_mul_partial(&partials[i], &missing_indices[offset], end - offset, domain_stride, fs));
offset += missing_per_partial;
out_offset += degree_of_partial;
}
// Adjust the length of the last partial
partials[partial_count - 1].length = 1 + len_missing - (partial_count - 1) * missing_per_partial;
// Reduce all the partials to a single poly
// Reduce all the partials to a single polynomial
int reduction_factor = 4; // must be a power of 2 (for sake of the FFTs in reduce partials)
fr_t *scratch;
@ -257,10 +252,10 @@ C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, fr_t *zero_poly, u
// Process final output
TRY(pad_p(zero_poly, length, &partials[0]));
TRY(fft_fr(zero_eval, zero_poly, false, length, fs));
TRY(pad_p(zero_poly->coeffs, length, &partials[0]));
TRY(fft_fr(zero_eval, zero_poly->coeffs, false, length, fs));
*zero_poly_len = partials[0].length;
zero_poly->length = partials[0].length;
free(work);
free(partials);

View File

@ -24,10 +24,10 @@
#include "fft_common.h"
#include "poly.h"
C_KZG_RET do_zero_poly_mul_partial(fr_t *dst, uint64_t len_dst, const uint64_t *indices, uint64_t len_indices,
uint64_t stride, const FFTSettings *fs);
C_KZG_RET do_zero_poly_mul_partial(poly *dst, const uint64_t *indices, uint64_t len_indices, uint64_t stride,
const FFTSettings *fs);
C_KZG_RET reduce_partials(poly *dst, uint64_t len_dst, fr_t *scratch, uint64_t len_scratch, const poly *partials,
uint64_t partial_count, const FFTSettings *fs);
C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, fr_t *zero_poly, uint64_t *zero_poly_len, uint64_t length,
C_KZG_RET zero_polynomial_via_multiplication(fr_t *zero_eval, poly *zero_poly, uint64_t width,
const uint64_t *missing_indices, uint64_t len_missing,
const FFTSettings *fs);

View File

@ -63,9 +63,12 @@ uint64_t expected_poly_u64[16][4] = {
void test_reduce_partials(void) {
FFTSettings fs;
TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 4));
fr_t from_tree_reduction_coeffs[16], from_direct[9], scratch[48];
poly from_tree_reduction;
fr_t from_tree_reduction_coeffs[16], from_direct_coeffs[9], scratch[48];
poly from_tree_reduction, from_direct;
from_tree_reduction.coeffs = from_tree_reduction_coeffs;
from_tree_reduction.length = 16;
from_direct.coeffs = from_direct_coeffs;
from_direct.length = 9;
// Via reduce_partials
@ -77,17 +80,17 @@ void test_reduce_partials(void) {
partials[3].coeffs = partial3, partials[3].length = 3;
const uint64_t partial_indices[4][2] = {{1, 3}, {7, 8}, {9, 10}, {12, 13}};
for (int i = 0; i < 4; i++) {
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(partials[i].coeffs, 3, partial_indices[i], 2, 1, &fs));
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(&partials[i], partial_indices[i], 2, 1, &fs));
}
TEST_CHECK(C_KZG_OK == reduce_partials(&from_tree_reduction, 16, scratch, 48, partials, 4, &fs));
// Direct
uint64_t indices[] = {1, 3, 7, 8, 9, 10, 12, 13};
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(from_direct, 9, indices, 8, 1, &fs));
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(&from_direct, indices, 8, 1, &fs));
// Compare
for (int i = 0; i < 9; i++) {
TEST_CHECK(fr_equal(&from_tree_reduction.coeffs[i], &from_direct[i]));
TEST_CHECK(fr_equal(&from_tree_reduction.coeffs[i], &from_direct.coeffs[i]));
}
free_fft_settings(&fs);
@ -126,8 +129,7 @@ void reduce_partials_random(void) {
indices[j] = missing[i * missing_per_partial + j];
}
partials[i].length = partial_size + 1;
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(partials[i].coeffs, partials[i].length, indices,
partial_size, 1, &fs));
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(&partials[i], indices, partial_size, 1, &fs));
}
// From tree reduction
@ -139,17 +141,17 @@ void reduce_partials_random(void) {
partials, partial_count, &fs));
// From direct
fr_t *from_direct;
TEST_CHECK(C_KZG_OK == new_fr_array(&from_direct, missing_count + 1));
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(from_direct, missing_count + 1, missing, missing_count,
fs.max_width / point_count, &fs));
poly from_direct;
TEST_CHECK(C_KZG_OK == new_poly(&from_direct, missing_count + 1));
TEST_CHECK(C_KZG_OK ==
do_zero_poly_mul_partial(&from_direct, missing, missing_count, fs.max_width / point_count, &fs));
for (uint64_t i = 0; i < missing_count + 1; i++) {
TEST_CHECK(fr_equal(&from_tree_reduction.coeffs[i], &from_direct[i]));
TEST_CHECK(fr_equal(&from_tree_reduction.coeffs[i], &from_direct.coeffs[i]));
}
free_poly(&from_tree_reduction);
free(from_direct);
free_poly(&from_direct);
free(scratch);
for (uint64_t i = 0; i < partial_count; i++) {
free_poly(&partials[i]);
@ -210,7 +212,6 @@ void zero_poly_known(void) {
poly expected_eval, expected_poly, zero_eval, zero_poly;
uint64_t missing[16];
uint64_t len_missing = 0;
uint64_t zero_poly_len;
new_poly(&expected_eval, 16);
new_poly(&expected_poly, 16);
new_poly(&zero_eval, 16);
@ -225,11 +226,11 @@ void zero_poly_known(void) {
}
}
TEST_CHECK(C_KZG_OK == zero_polynomial_via_multiplication(zero_eval.coeffs, zero_poly.coeffs, &zero_poly_len,
zero_eval.length, missing, len_missing, &fs));
TEST_CHECK(C_KZG_OK == zero_polynomial_via_multiplication(zero_eval.coeffs, &zero_poly, zero_eval.length, missing,
len_missing, &fs));
TEST_CHECK(len_missing + 1 == zero_poly_len);
TEST_MSG("Expected %lu, got %lu", len_missing + 1, zero_poly_len);
TEST_CHECK(len_missing + 1 == zero_poly.length);
TEST_MSG("Expected %lu, got %lu", len_missing + 1, zero_poly.length);
for (int i = 0; i < expected_eval.length; i++) {
TEST_CHECK(fr_equal(&expected_eval.coeffs[i], &zero_eval.coeffs[i]));
@ -266,23 +267,20 @@ void zero_poly_random(void) {
continue;
}
fr_t *zero_eval, *zero_poly;
uint64_t zero_poly_len;
fr_t *zero_eval;
poly zero_poly;
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval, fs.max_width));
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_poly, fs.max_width));
TEST_CHECK(C_KZG_OK == zero_polynomial_via_multiplication(zero_eval, zero_poly, &zero_poly_len,
fs.max_width, missing, len_missing, &fs));
TEST_CHECK(C_KZG_OK == new_poly(&zero_poly, fs.max_width));
TEST_CHECK(C_KZG_OK == zero_polynomial_via_multiplication(zero_eval, &zero_poly, fs.max_width, missing,
len_missing, &fs));
TEST_CHECK(len_missing + 1 == zero_poly_len);
TEST_MSG("ZeroPolyLen: expected %d, got %lu", len_missing + 1, zero_poly_len);
TEST_CHECK(len_missing + 1 == zero_poly.length);
TEST_MSG("ZeroPolyLen: expected %d, got %lu", len_missing + 1, zero_poly.length);
poly p;
p.length = zero_poly_len;
p.coeffs = zero_poly;
int ret = 0;
for (int i = 0; i < len_missing; i++) {
fr_t out;
eval_poly(&out, &p, &fs.expanded_roots_of_unity[missing[i]]);
eval_poly(&out, &zero_poly, &fs.expanded_roots_of_unity[missing[i]]);
ret = TEST_CHECK(fr_is_zero(&out));
TEST_MSG("Failed for missing[%d] = %lu", i, missing[i]);
}
@ -290,15 +288,15 @@ void zero_poly_random(void) {
fr_t *zero_eval_fft;
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval_fft, fs.max_width));
TEST_CHECK(C_KZG_OK == fft_fr(zero_eval_fft, zero_eval, true, fs.max_width, &fs));
for (uint64_t i = 0; i < zero_poly_len; i++) {
TEST_CHECK(fr_equal(&zero_poly[i], &zero_eval_fft[i]));
for (uint64_t i = 0; i < zero_poly.length; i++) {
TEST_CHECK(fr_equal(&zero_poly.coeffs[i], &zero_eval_fft[i]));
}
for (uint64_t i = zero_poly_len; i < fs.max_width; i++) {
for (uint64_t i = zero_poly.length; i < fs.max_width; i++) {
TEST_CHECK(fr_is_zero(&zero_eval_fft[i]));
}
free(missing);
free(zero_poly);
free_poly(&zero_poly);
free(zero_eval);
free(zero_eval_fft);
free_fft_settings(&fs);
@ -306,6 +304,7 @@ void zero_poly_random(void) {
}
}
// This didn't work in the original version (ported from Go), but ought now be fine
void zero_poly_all_but_one(void) {
FFTSettings fs;
TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 8));
@ -319,23 +318,20 @@ void zero_poly_all_but_one(void) {
}
int len_missing = fs.max_width - 1;
fr_t *zero_eval, *zero_poly;
uint64_t zero_poly_len;
fr_t *zero_eval;
poly zero_poly;
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval, fs.max_width));
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_poly, fs.max_width));
TEST_CHECK(C_KZG_OK == zero_polynomial_via_multiplication(zero_eval, zero_poly, &zero_poly_len, fs.max_width,
missing, len_missing, &fs));
TEST_CHECK(C_KZG_OK == new_poly(&zero_poly, fs.max_width));
TEST_CHECK(C_KZG_OK ==
zero_polynomial_via_multiplication(zero_eval, &zero_poly, fs.max_width, missing, len_missing, &fs));
TEST_CHECK(len_missing + 1 == zero_poly_len);
TEST_MSG("ZeroPolyLen: expected %d, got %lu", len_missing + 1, zero_poly_len);
TEST_CHECK(len_missing + 1 == zero_poly.length);
TEST_MSG("ZeroPolyLen: expected %d, got %lu", len_missing + 1, zero_poly.length);
poly p;
p.length = zero_poly_len;
p.coeffs = zero_poly;
int ret = 0;
for (int i = 0; i < len_missing; i++) {
fr_t out;
eval_poly(&out, &p, &fs.expanded_roots_of_unity[missing[i]]);
eval_poly(&out, &zero_poly, &fs.expanded_roots_of_unity[missing[i]]);
ret = TEST_CHECK(fr_is_zero(&out));
TEST_MSG("Failed for missing[%d] = %lu", i, missing[i]);
}
@ -343,20 +339,21 @@ void zero_poly_all_but_one(void) {
fr_t *zero_eval_fft;
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval_fft, fs.max_width));
TEST_CHECK(C_KZG_OK == fft_fr(zero_eval_fft, zero_eval, true, fs.max_width, &fs));
for (uint64_t i = 0; i < zero_poly_len; i++) {
TEST_CHECK(fr_equal(&zero_poly[i], &zero_eval_fft[i]));
for (uint64_t i = 0; i < zero_poly.length; i++) {
TEST_CHECK(fr_equal(&zero_poly.coeffs[i], &zero_eval_fft[i]));
}
for (uint64_t i = zero_poly_len; i < fs.max_width; i++) {
for (uint64_t i = zero_poly.length; i < fs.max_width; i++) {
TEST_CHECK(fr_is_zero(&zero_eval_fft[i]));
}
free(missing);
free(zero_poly);
free_poly(&zero_poly);
free(zero_eval);
free(zero_eval_fft);
free_fft_settings(&fs);
}
// This is to prevent regressions - 252 missing at width 8 is an edge case which has 4 full partials
void zero_poly_252(void) {
FFTSettings fs;
TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 8));
@ -364,29 +361,25 @@ void zero_poly_252(void) {
uint64_t *missing;
TEST_CHECK(C_KZG_OK == new_uint64_array(&missing, fs.max_width));
// 252 are missing
int len_missing = 252;
for (int i = 0; i < len_missing; i++) {
missing[i] = i;
}
fr_t *zero_eval, *zero_poly;
uint64_t zero_poly_len;
fr_t *zero_eval;
poly zero_poly;
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval, fs.max_width));
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_poly, fs.max_width));
TEST_CHECK(C_KZG_OK == zero_polynomial_via_multiplication(zero_eval, zero_poly, &zero_poly_len, fs.max_width,
missing, len_missing, &fs));
TEST_CHECK(C_KZG_OK == new_poly(&zero_poly, fs.max_width));
TEST_CHECK(C_KZG_OK ==
zero_polynomial_via_multiplication(zero_eval, &zero_poly, fs.max_width, missing, len_missing, &fs));
TEST_CHECK(len_missing + 1 == zero_poly_len);
TEST_MSG("ZeroPolyLen: expected %d, got %lu", len_missing + 1, zero_poly_len);
TEST_CHECK(len_missing + 1 == zero_poly.length);
TEST_MSG("ZeroPolyLen: expected %d, got %lu", len_missing + 1, zero_poly.length);
poly p;
p.length = zero_poly_len;
p.coeffs = zero_poly;
int ret = 0;
for (int i = 0; i < len_missing; i++) {
fr_t out;
eval_poly(&out, &p, &fs.expanded_roots_of_unity[missing[i]]);
eval_poly(&out, &zero_poly, &fs.expanded_roots_of_unity[missing[i]]);
ret = TEST_CHECK(fr_is_zero(&out));
TEST_MSG("Failed for missing[%d] = %lu", i, missing[i]);
}
@ -394,15 +387,15 @@ void zero_poly_252(void) {
fr_t *zero_eval_fft;
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval_fft, fs.max_width));
TEST_CHECK(C_KZG_OK == fft_fr(zero_eval_fft, zero_eval, true, fs.max_width, &fs));
for (uint64_t i = 0; i < zero_poly_len; i++) {
TEST_CHECK(fr_equal(&zero_poly[i], &zero_eval_fft[i]));
for (uint64_t i = 0; i < zero_poly.length; i++) {
TEST_CHECK(fr_equal(&zero_poly.coeffs[i], &zero_eval_fft[i]));
}
for (uint64_t i = zero_poly_len; i < fs.max_width; i++) {
for (uint64_t i = zero_poly.length; i < fs.max_width; i++) {
TEST_CHECK(fr_is_zero(&zero_eval_fft[i]));
}
free(missing);
free(zero_poly);
free_poly(&zero_poly);
free(zero_eval);
free(zero_eval_fft);
free_fft_settings(&fs);