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:
parent
f09d1a70b2
commit
9d1b622f21
|
@ -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;
|
fr_t *scratch2 = scratch1 + len_samples;
|
||||||
|
|
||||||
// Assign meaningful names to scratch spaces
|
// Assign meaningful names to scratch spaces
|
||||||
|
poly zero_poly;
|
||||||
fr_t *zero_eval = scratch0;
|
fr_t *zero_eval = scratch0;
|
||||||
fr_t *zero_poly = scratch1;
|
zero_poly.coeffs = scratch1;
|
||||||
fr_t *poly_evaluations_with_zero = scratch2;
|
fr_t *poly_evaluations_with_zero = scratch2;
|
||||||
fr_t *poly_with_zero = scratch0;
|
fr_t *poly_with_zero = scratch0;
|
||||||
fr_t *eval_shifted_poly_with_zero = scratch2;
|
fr_t *eval_shifted_poly_with_zero = scratch2;
|
||||||
fr_t *eval_shifted_zero_poly = scratch0;
|
fr_t *eval_shifted_zero_poly = scratch0;
|
||||||
fr_t *shifted_reconstructed_poly = scratch1;
|
fr_t *shifted_reconstructed_poly = scratch1;
|
||||||
|
|
||||||
uint64_t zero_poly_len;
|
// Calculate `Z_r,I`
|
||||||
TRY(zero_polynomial_via_multiplication(zero_eval, zero_poly, &zero_poly_len, len_samples, missing, len_missing,
|
TRY(zero_polynomial_via_multiplication(zero_eval, &zero_poly, len_samples, missing, len_missing, fs));
|
||||||
fs));
|
|
||||||
|
|
||||||
// Check all is well
|
// Check all is well
|
||||||
for (uint64_t i = 0; i < len_samples; i++) {
|
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));
|
TRY(fft_fr(poly_with_zero, poly_evaluations_with_zero, true, len_samples, fs));
|
||||||
shift_poly(poly_with_zero, len_samples);
|
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; // Renaming
|
||||||
fr_t *shifted_poly_with_zero = poly_with_zero;
|
fr_t *shifted_zero_poly = zero_poly.coeffs; // Renaming
|
||||||
fr_t *shifted_zero_poly = zero_poly;
|
|
||||||
|
|
||||||
TRY(fft_fr(eval_shifted_poly_with_zero, shifted_poly_with_zero, false, len_samples, fs));
|
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));
|
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);
|
unshift_poly(shifted_reconstructed_poly, len_samples);
|
||||||
|
|
||||||
// Renaming:
|
fr_t *reconstructed_poly = shifted_reconstructed_poly; // Renaming
|
||||||
fr_t *reconstructed_poly = shifted_reconstructed_poly;
|
|
||||||
|
|
||||||
TRY(fft_fr(reconstructed_data, reconstructed_poly, false, len_samples, fs));
|
TRY(fft_fr(reconstructed_data, reconstructed_poly, false, len_samples, fs));
|
||||||
|
|
||||||
// Check all is well
|
// Check all is well
|
||||||
|
|
|
@ -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
|
* 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.
|
* 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,out] dst The zero polynomial for @p indices. The space allocated for coefficients must be at least @p
|
||||||
* @param[in] len_dst Length of the output polynomial, @p dst
|
* 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] indices Array of missing indices of length @p len_indices
|
||||||
* @param[in] len_indices Length of the missing indices array, @p 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
|
* @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
|
* @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,
|
C_KZG_RET do_zero_poly_mul_partial(poly *dst, const uint64_t *indices, uint64_t len_indices, uint64_t stride,
|
||||||
uint64_t stride, const FFTSettings *fs) {
|
const FFTSettings *fs) {
|
||||||
|
|
||||||
CHECK(len_dst >= len_indices + 1);
|
CHECK(dst->length >= len_indices + 1);
|
||||||
|
|
||||||
for (uint64_t i = 0; i < len_indices; i++) {
|
for (uint64_t i = 0; i < len_indices; i++) {
|
||||||
fr_t neg_di;
|
fr_t neg_di;
|
||||||
fr_negate(&neg_di, &fs->expanded_roots_of_unity[indices[i] * stride]);
|
fr_negate(&neg_di, &fs->expanded_roots_of_unity[indices[i] * stride]);
|
||||||
dst[i] = neg_di;
|
dst->coeffs[i] = neg_di;
|
||||||
if (i > 0) {
|
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--) {
|
for (uint64_t j = i - 1; j > 0; j--) {
|
||||||
fr_mul(&dst[j], &dst[j], &neg_di);
|
fr_mul(&dst->coeffs[j], &dst->coeffs[j], &neg_di);
|
||||||
fr_add(&dst[j], &dst[j], &dst[j - 1]);
|
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++) {
|
for (uint64_t i = len_indices + 1; i < dst->length; i++) {
|
||||||
dst[i] = fr_zero;
|
dst->coeffs[i] = fr_zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dst->length = len_indices + 1;
|
||||||
|
|
||||||
return C_KZG_OK;
|
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] 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 The polynomial containing the data to be copied and padded
|
||||||
* @param[in] p_len The length of the data to be copied and padded
|
|
||||||
* @retval C_CZK_OK All is well
|
* @retval C_CZK_OK All is well
|
||||||
* @retval C_CZK_BADARGS Invalid parameters were supplied
|
* @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
|
* Pad the polynomials in @p ps, perform FFTs, point-wise multiply the results together, and apply an inverse FFT to the
|
||||||
* result.
|
* result.
|
||||||
*
|
*
|
||||||
* @param[out] dst The result of the convolution
|
* @param[out] out Polynomial with @p len_out space allocated. The length will be set on return.
|
||||||
* @param[in] len_dst Length of the output, a power of two
|
* @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 output size
|
* @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 x @p len_dst
|
* @param[in] len_scratch Length of @p scratch, at least 3 times @p len_out
|
||||||
* @param[in] ps Array of polynomial coefficients ps[@p len_ps][@p len_p]
|
* @param[in] partials Array of polynomials to be multiplied together
|
||||||
* @param[in] len_ps The number of polynomials
|
* @param[in] partial_count The number of polynomials to be multiplied together
|
||||||
* @param[in] len_p Array of lengths of each polynomial, size @p len_ps
|
|
||||||
* @param[in] fs The FFT settings previously initialised with #new_fft_settings
|
* @param[in] fs The FFT settings previously initialised with #new_fft_settings
|
||||||
* @retval C_CZK_OK All is well
|
* @retval C_CZK_OK All is well
|
||||||
* @retval C_CZK_BADARGS Invalid parameters were supplied
|
* @retval C_CZK_BADARGS Invalid parameters were supplied
|
||||||
|
@ -148,23 +148,20 @@ 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
|
* Calculate the minimal polynomial that evaluates to zero for powers of roots of unity that correspond to missing
|
||||||
* indices.
|
* indices.
|
||||||
*
|
*
|
||||||
* This is done by simply multiplying together `(x - r^i)` for all the `i` that are missing indices, using a combination
|
* 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 multiplication via convolution (#reduce_partials).
|
* of direct multiplication (#do_zero_poly_mul_partial) and iterated multiplication via convolution (#reduce_partials).
|
||||||
*
|
*
|
||||||
* Also calculates the FFT (the "evaluation polynomial").
|
* 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`,
|
* @remark This fails when all the indices in our domain are missing (@p len_missing == @p length), since the resulting
|
||||||
* this will fail for len_missing = 253 or more. In this case, `length` (and maybe `fs.max_width`) needs to be doubled.
|
* polynomial exceeds the size allocated. But we know that the answer is `x^length - 1` in that case if we ever need it.
|
||||||
* 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 Note that @p zero_poly is used as workspace during calculation.
|
* @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_eval Array length @p length (TODO: description)
|
* @param[out] zero_poly The zero polynomial. On return the length will be set to `len_missing + 1` and the remaining
|
||||||
* @param[out] zero_poly Array length @p length (TODO: description)
|
* coefficients set to zero. Space required is @p length.
|
||||||
* @param[out] zero_poly_len The length of the resulting @p zero_poly
|
* @param[in] length Size of the domain of evaluation (number of powers of `r`)
|
||||||
* @param[in] length Length of the output arrays
|
* @param[in] missing_indices Array length @p len_missing containing the indices of the missing coefficients
|
||||||
* @param[in] missing_indices Array length @p len_missing (TODO: description)
|
|
||||||
* @param[in] len_missing Length of @p missing_indices
|
* @param[in] len_missing Length of @p missing_indices
|
||||||
* @param[in] fs The FFT settings previously initialised with #new_fft_settings
|
* @param[in] fs The FFT settings previously initialised with #new_fft_settings
|
||||||
* @retval C_CZK_OK All is well
|
* @retval C_CZK_OK All is well
|
||||||
|
@ -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`?
|
* @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 uint64_t *missing_indices, uint64_t len_missing,
|
||||||
const FFTSettings *fs) {
|
const FFTSettings *fs) {
|
||||||
if (len_missing == 0) {
|
if (len_missing == 0) {
|
||||||
*zero_poly_len = 0;
|
zero_poly->length = 0;
|
||||||
for (uint64_t i = 0; i < length; i++) {
|
for (uint64_t i = 0; i < length; i++) {
|
||||||
zero_eval[i] = fr_zero;
|
zero_eval[i] = fr_zero;
|
||||||
zero_poly[i] = fr_zero;
|
zero_poly->coeffs[i] = fr_zero;
|
||||||
}
|
}
|
||||||
return C_KZG_OK;
|
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(length <= fs->max_width);
|
||||||
CHECK(is_power_of_two(length));
|
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 (n > length) n = length;
|
||||||
|
|
||||||
if (len_missing <= missing_per_partial) {
|
if (len_missing <= missing_per_partial) {
|
||||||
TRY(do_zero_poly_mul_partial(zero_poly, length, missing_indices, len_missing, domain_stride, fs));
|
TRY(do_zero_poly_mul_partial(zero_poly, missing_indices, len_missing, domain_stride, fs));
|
||||||
TRY(fft_fr(zero_eval, zero_poly, false, length, fs));
|
TRY(fft_fr(zero_eval, zero_poly->coeffs, false, length, fs));
|
||||||
*zero_poly_len = len_missing + 1;
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Work space for building and reducing the partials
|
// Work space for building and reducing the partials
|
||||||
fr_t *work;
|
fr_t *work;
|
||||||
TRY(new_fr_array(&work, next_power_of_two(partial_count * degree_of_partial)));
|
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
|
// 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.
|
// 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;
|
if (end > max) end = max;
|
||||||
partials[i].coeffs = &work[out_offset];
|
partials[i].coeffs = &work[out_offset];
|
||||||
partials[i].length = degree_of_partial;
|
partials[i].length = degree_of_partial;
|
||||||
TRY(do_zero_poly_mul_partial(partials[i].coeffs, degree_of_partial, &missing_indices[offset], end - offset,
|
TRY(do_zero_poly_mul_partial(&partials[i], &missing_indices[offset], end - offset, domain_stride, fs));
|
||||||
domain_stride, fs));
|
|
||||||
offset += missing_per_partial;
|
offset += missing_per_partial;
|
||||||
out_offset += degree_of_partial;
|
out_offset += degree_of_partial;
|
||||||
}
|
}
|
||||||
// Adjust the length of the last partial
|
// Adjust the length of the last partial
|
||||||
partials[partial_count - 1].length = 1 + len_missing - (partial_count - 1) * missing_per_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)
|
int reduction_factor = 4; // must be a power of 2 (for sake of the FFTs in reduce partials)
|
||||||
fr_t *scratch;
|
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
|
// Process final output
|
||||||
|
|
||||||
TRY(pad_p(zero_poly, length, &partials[0]));
|
TRY(pad_p(zero_poly->coeffs, length, &partials[0]));
|
||||||
TRY(fft_fr(zero_eval, zero_poly, false, length, fs));
|
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(work);
|
||||||
free(partials);
|
free(partials);
|
||||||
|
|
|
@ -24,10 +24,10 @@
|
||||||
#include "fft_common.h"
|
#include "fft_common.h"
|
||||||
#include "poly.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,
|
C_KZG_RET do_zero_poly_mul_partial(poly *dst, const uint64_t *indices, uint64_t len_indices, uint64_t stride,
|
||||||
uint64_t stride, const FFTSettings *fs);
|
const FFTSettings *fs);
|
||||||
C_KZG_RET reduce_partials(poly *dst, uint64_t len_dst, fr_t *scratch, uint64_t len_scratch, const poly *partials,
|
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);
|
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 uint64_t *missing_indices, uint64_t len_missing,
|
||||||
const FFTSettings *fs);
|
const FFTSettings *fs);
|
||||||
|
|
|
@ -63,9 +63,12 @@ uint64_t expected_poly_u64[16][4] = {
|
||||||
void test_reduce_partials(void) {
|
void test_reduce_partials(void) {
|
||||||
FFTSettings fs;
|
FFTSettings fs;
|
||||||
TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 4));
|
TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 4));
|
||||||
fr_t from_tree_reduction_coeffs[16], from_direct[9], scratch[48];
|
fr_t from_tree_reduction_coeffs[16], from_direct_coeffs[9], scratch[48];
|
||||||
poly from_tree_reduction;
|
poly from_tree_reduction, from_direct;
|
||||||
from_tree_reduction.coeffs = from_tree_reduction_coeffs;
|
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
|
// Via reduce_partials
|
||||||
|
|
||||||
|
@ -77,17 +80,17 @@ void test_reduce_partials(void) {
|
||||||
partials[3].coeffs = partial3, partials[3].length = 3;
|
partials[3].coeffs = partial3, partials[3].length = 3;
|
||||||
const uint64_t partial_indices[4][2] = {{1, 3}, {7, 8}, {9, 10}, {12, 13}};
|
const uint64_t partial_indices[4][2] = {{1, 3}, {7, 8}, {9, 10}, {12, 13}};
|
||||||
for (int i = 0; i < 4; i++) {
|
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));
|
TEST_CHECK(C_KZG_OK == reduce_partials(&from_tree_reduction, 16, scratch, 48, partials, 4, &fs));
|
||||||
|
|
||||||
// Direct
|
// Direct
|
||||||
uint64_t indices[] = {1, 3, 7, 8, 9, 10, 12, 13};
|
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
|
// Compare
|
||||||
for (int i = 0; i < 9; i++) {
|
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);
|
free_fft_settings(&fs);
|
||||||
|
@ -126,8 +129,7 @@ void reduce_partials_random(void) {
|
||||||
indices[j] = missing[i * missing_per_partial + j];
|
indices[j] = missing[i * missing_per_partial + j];
|
||||||
}
|
}
|
||||||
partials[i].length = partial_size + 1;
|
partials[i].length = partial_size + 1;
|
||||||
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(partials[i].coeffs, partials[i].length, indices,
|
TEST_CHECK(C_KZG_OK == do_zero_poly_mul_partial(&partials[i], indices, partial_size, 1, &fs));
|
||||||
partial_size, 1, &fs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// From tree reduction
|
// From tree reduction
|
||||||
|
@ -139,17 +141,17 @@ void reduce_partials_random(void) {
|
||||||
partials, partial_count, &fs));
|
partials, partial_count, &fs));
|
||||||
|
|
||||||
// From direct
|
// From direct
|
||||||
fr_t *from_direct;
|
poly from_direct;
|
||||||
TEST_CHECK(C_KZG_OK == new_fr_array(&from_direct, missing_count + 1));
|
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_count + 1, missing, missing_count,
|
TEST_CHECK(C_KZG_OK ==
|
||||||
fs.max_width / point_count, &fs));
|
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++) {
|
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_poly(&from_tree_reduction);
|
||||||
free(from_direct);
|
free_poly(&from_direct);
|
||||||
free(scratch);
|
free(scratch);
|
||||||
for (uint64_t i = 0; i < partial_count; i++) {
|
for (uint64_t i = 0; i < partial_count; i++) {
|
||||||
free_poly(&partials[i]);
|
free_poly(&partials[i]);
|
||||||
|
@ -210,7 +212,6 @@ void zero_poly_known(void) {
|
||||||
poly expected_eval, expected_poly, zero_eval, zero_poly;
|
poly expected_eval, expected_poly, zero_eval, zero_poly;
|
||||||
uint64_t missing[16];
|
uint64_t missing[16];
|
||||||
uint64_t len_missing = 0;
|
uint64_t len_missing = 0;
|
||||||
uint64_t zero_poly_len;
|
|
||||||
new_poly(&expected_eval, 16);
|
new_poly(&expected_eval, 16);
|
||||||
new_poly(&expected_poly, 16);
|
new_poly(&expected_poly, 16);
|
||||||
new_poly(&zero_eval, 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,
|
TEST_CHECK(C_KZG_OK == zero_polynomial_via_multiplication(zero_eval.coeffs, &zero_poly, zero_eval.length, missing,
|
||||||
zero_eval.length, missing, len_missing, &fs));
|
len_missing, &fs));
|
||||||
|
|
||||||
TEST_CHECK(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_len);
|
TEST_MSG("Expected %lu, got %lu", len_missing + 1, zero_poly.length);
|
||||||
|
|
||||||
for (int i = 0; i < expected_eval.length; i++) {
|
for (int i = 0; i < expected_eval.length; i++) {
|
||||||
TEST_CHECK(fr_equal(&expected_eval.coeffs[i], &zero_eval.coeffs[i]));
|
TEST_CHECK(fr_equal(&expected_eval.coeffs[i], &zero_eval.coeffs[i]));
|
||||||
|
@ -266,23 +267,20 @@ void zero_poly_random(void) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fr_t *zero_eval, *zero_poly;
|
fr_t *zero_eval;
|
||||||
uint64_t zero_poly_len;
|
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_eval, fs.max_width));
|
||||||
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_poly, fs.max_width));
|
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, &zero_poly_len,
|
TEST_CHECK(C_KZG_OK == zero_polynomial_via_multiplication(zero_eval, &zero_poly, fs.max_width, missing,
|
||||||
fs.max_width, missing, len_missing, &fs));
|
len_missing, &fs));
|
||||||
|
|
||||||
TEST_CHECK(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_len);
|
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;
|
int ret = 0;
|
||||||
for (int i = 0; i < len_missing; i++) {
|
for (int i = 0; i < len_missing; i++) {
|
||||||
fr_t out;
|
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));
|
ret = TEST_CHECK(fr_is_zero(&out));
|
||||||
TEST_MSG("Failed for missing[%d] = %lu", i, missing[i]);
|
TEST_MSG("Failed for missing[%d] = %lu", i, missing[i]);
|
||||||
}
|
}
|
||||||
|
@ -290,15 +288,15 @@ void zero_poly_random(void) {
|
||||||
fr_t *zero_eval_fft;
|
fr_t *zero_eval_fft;
|
||||||
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval_fft, fs.max_width));
|
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));
|
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++) {
|
for (uint64_t i = 0; i < zero_poly.length; i++) {
|
||||||
TEST_CHECK(fr_equal(&zero_poly[i], &zero_eval_fft[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]));
|
TEST_CHECK(fr_is_zero(&zero_eval_fft[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
free(missing);
|
free(missing);
|
||||||
free(zero_poly);
|
free_poly(&zero_poly);
|
||||||
free(zero_eval);
|
free(zero_eval);
|
||||||
free(zero_eval_fft);
|
free(zero_eval_fft);
|
||||||
free_fft_settings(&fs);
|
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) {
|
void zero_poly_all_but_one(void) {
|
||||||
FFTSettings fs;
|
FFTSettings fs;
|
||||||
TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 8));
|
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;
|
int len_missing = fs.max_width - 1;
|
||||||
|
|
||||||
fr_t *zero_eval, *zero_poly;
|
fr_t *zero_eval;
|
||||||
uint64_t zero_poly_len;
|
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_eval, fs.max_width));
|
||||||
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_poly, fs.max_width));
|
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, &zero_poly_len, fs.max_width,
|
TEST_CHECK(C_KZG_OK ==
|
||||||
missing, len_missing, &fs));
|
zero_polynomial_via_multiplication(zero_eval, &zero_poly, fs.max_width, missing, len_missing, &fs));
|
||||||
|
|
||||||
TEST_CHECK(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_len);
|
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;
|
int ret = 0;
|
||||||
for (int i = 0; i < len_missing; i++) {
|
for (int i = 0; i < len_missing; i++) {
|
||||||
fr_t out;
|
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));
|
ret = TEST_CHECK(fr_is_zero(&out));
|
||||||
TEST_MSG("Failed for missing[%d] = %lu", i, missing[i]);
|
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;
|
fr_t *zero_eval_fft;
|
||||||
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval_fft, fs.max_width));
|
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));
|
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++) {
|
for (uint64_t i = 0; i < zero_poly.length; i++) {
|
||||||
TEST_CHECK(fr_equal(&zero_poly[i], &zero_eval_fft[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]));
|
TEST_CHECK(fr_is_zero(&zero_eval_fft[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
free(missing);
|
free(missing);
|
||||||
free(zero_poly);
|
free_poly(&zero_poly);
|
||||||
free(zero_eval);
|
free(zero_eval);
|
||||||
free(zero_eval_fft);
|
free(zero_eval_fft);
|
||||||
free_fft_settings(&fs);
|
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) {
|
void zero_poly_252(void) {
|
||||||
FFTSettings fs;
|
FFTSettings fs;
|
||||||
TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 8));
|
TEST_CHECK(C_KZG_OK == new_fft_settings(&fs, 8));
|
||||||
|
@ -364,29 +361,25 @@ void zero_poly_252(void) {
|
||||||
uint64_t *missing;
|
uint64_t *missing;
|
||||||
TEST_CHECK(C_KZG_OK == new_uint64_array(&missing, fs.max_width));
|
TEST_CHECK(C_KZG_OK == new_uint64_array(&missing, fs.max_width));
|
||||||
|
|
||||||
// 252 are missing
|
|
||||||
int len_missing = 252;
|
int len_missing = 252;
|
||||||
for (int i = 0; i < len_missing; i++) {
|
for (int i = 0; i < len_missing; i++) {
|
||||||
missing[i] = i;
|
missing[i] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
fr_t *zero_eval, *zero_poly;
|
fr_t *zero_eval;
|
||||||
uint64_t zero_poly_len;
|
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_eval, fs.max_width));
|
||||||
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_poly, fs.max_width));
|
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, &zero_poly_len, fs.max_width,
|
TEST_CHECK(C_KZG_OK ==
|
||||||
missing, len_missing, &fs));
|
zero_polynomial_via_multiplication(zero_eval, &zero_poly, fs.max_width, missing, len_missing, &fs));
|
||||||
|
|
||||||
TEST_CHECK(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_len);
|
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;
|
int ret = 0;
|
||||||
for (int i = 0; i < len_missing; i++) {
|
for (int i = 0; i < len_missing; i++) {
|
||||||
fr_t out;
|
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));
|
ret = TEST_CHECK(fr_is_zero(&out));
|
||||||
TEST_MSG("Failed for missing[%d] = %lu", i, missing[i]);
|
TEST_MSG("Failed for missing[%d] = %lu", i, missing[i]);
|
||||||
}
|
}
|
||||||
|
@ -394,15 +387,15 @@ void zero_poly_252(void) {
|
||||||
fr_t *zero_eval_fft;
|
fr_t *zero_eval_fft;
|
||||||
TEST_CHECK(C_KZG_OK == new_fr_array(&zero_eval_fft, fs.max_width));
|
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));
|
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++) {
|
for (uint64_t i = 0; i < zero_poly.length; i++) {
|
||||||
TEST_CHECK(fr_equal(&zero_poly[i], &zero_eval_fft[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]));
|
TEST_CHECK(fr_is_zero(&zero_eval_fft[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
free(missing);
|
free(missing);
|
||||||
free(zero_poly);
|
free_poly(&zero_poly);
|
||||||
free(zero_eval);
|
free(zero_eval);
|
||||||
free(zero_eval_fft);
|
free(zero_eval_fft);
|
||||||
free_fft_settings(&fs);
|
free_fft_settings(&fs);
|
||||||
|
|
Loading…
Reference in New Issue