#include "eip7594/fk20.h"
#include "common/alloc.h"
#include "common/lincomb.h"
#include "eip7594/cell.h"
#include "eip7594/fft.h"
#include <assert.h>
#include <stdlib.h>
static void circulant_coeffs_stride(fr_t *out, const fr_t *in, size_t offset) {
const size_t r = CELLS_PER_BLOB;
const size_t l = FIELD_ELEMENTS_PER_CELL;
const size_t d = FIELD_ELEMENTS_PER_BLOB - 1;
const size_t d_minus_i = d - offset;
assert(d >= offset);
for (size_t j = 0; j < 2 * r; j++) {
out[j] = FR_ZERO;
}
out[0] = in[d_minus_i];
for (size_t j = 1; j < r - 1; j++) {
out[2 * r - j] = in[d_minus_i - j * l];
}
}
C_KZG_RET compute_fk20_cell_proofs(g1_t *out, const fr_t *poly, const KZGSettings *s) {
C_KZG_RET ret;
size_t circulant_domain_size;
fr_t inv_domain_size;
blst_scalar *scalars = NULL;
fr_t **coeffs = NULL;
fr_t *circulant_coeffs = NULL;
fr_t *circulant_coeffs_fft = NULL;
g1_t *v = NULL;
g1_t *u = NULL;
limb_t *scratch = NULL;
bool precompute = s->wbits != 0;
circulant_domain_size = CELLS_PER_BLOB * 2;
ret = new_fr_array(&circulant_coeffs, circulant_domain_size);
if (ret != C_KZG_OK) goto out;
ret = new_fr_array(&circulant_coeffs_fft, circulant_domain_size);
if (ret != C_KZG_OK) goto out;
ret = new_g1_array(&u, circulant_domain_size);
if (ret != C_KZG_OK) goto out;
ret = new_g1_array(&v, circulant_domain_size);
if (ret != C_KZG_OK) goto out;
if (precompute) {
ret = c_kzg_malloc((void **)&scratch, s->scratch_size);
if (ret != C_KZG_OK) goto out;
ret = c_kzg_calloc((void **)&scalars, FIELD_ELEMENTS_PER_CELL, sizeof(blst_scalar));
if (ret != C_KZG_OK) goto out;
}
ret = c_kzg_calloc((void **)&coeffs, circulant_domain_size, sizeof(void *));
if (ret != C_KZG_OK) goto out;
for (size_t i = 0; i < circulant_domain_size; i++) {
ret = new_fr_array(&coeffs[i], FIELD_ELEMENTS_PER_CELL);
if (ret != C_KZG_OK) goto out;
}
for (size_t i = 0; i < circulant_domain_size; i++) {
u[i] = G1_IDENTITY;
}
fr_from_uint64(&inv_domain_size, circulant_domain_size);
fr_inv(&inv_domain_size, &inv_domain_size);
for (size_t i = 0; i < FIELD_ELEMENTS_PER_CELL; i++) {
circulant_coeffs_stride(circulant_coeffs, poly, i);
ret = fr_fft(circulant_coeffs_fft, circulant_coeffs, circulant_domain_size, s);
if (ret != C_KZG_OK) goto out;
for (size_t j = 0; j < circulant_domain_size; j++) {
fr_mul(&coeffs[j][i], &circulant_coeffs_fft[j], &inv_domain_size);
}
}
for (size_t i = 0; i < circulant_domain_size; i++) {
if (precompute) {
for (size_t j = 0; j < FIELD_ELEMENTS_PER_CELL; j++) {
blst_scalar_from_fr(&scalars[j], &coeffs[i][j]);
}
const byte *scalars_arg[2] = {(byte *)scalars, NULL};
blst_p1s_mult_wbits(
&u[i],
s->tables[i],
s->wbits,
FIELD_ELEMENTS_PER_CELL,
scalars_arg,
BITS_PER_FIELD_ELEMENT,
scratch
);
} else {
ret = g1_lincomb_fast(
&u[i], s->x_ext_fft_columns[i], coeffs[i], FIELD_ELEMENTS_PER_CELL
);
if (ret != C_KZG_OK) goto out;
}
}
ret = g1_ifft_unscaled(v, u, circulant_domain_size, s);
if (ret != C_KZG_OK) goto out;
for (size_t i = CELLS_PER_BLOB; i < circulant_domain_size; i++) {
v[i] = G1_IDENTITY;
}
ret = g1_fft(out, v, CELLS_PER_EXT_BLOB, s);
if (ret != C_KZG_OK) goto out;
out:
c_kzg_free(scalars);
if (coeffs != NULL) {
for (size_t i = 0; i < circulant_domain_size; i++) {
c_kzg_free(coeffs[i]);
}
c_kzg_free(coeffs);
}
c_kzg_free(circulant_coeffs);
c_kzg_free(circulant_coeffs_fft);
c_kzg_free(v);
c_kzg_free(u);
c_kzg_free(scratch);
return ret;
}