#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::iter;
use itertools::Itertools;
use crate::field::extension::Extendable;
use crate::field::types::Field;
use crate::hash::hash_types::RichField;
use crate::iop::ext_target::ExtensionTarget;
use crate::plonk::circuit_builder::CircuitBuilder;
pub(crate) fn quotient_chunk_products<F: Field>(
quotient_values: &[F],
max_degree: usize,
) -> Vec<F> {
debug_assert!(max_degree > 1);
assert!(!quotient_values.is_empty());
let chunk_size = max_degree;
quotient_values
.chunks(chunk_size)
.map(|chunk| chunk.iter().copied().product())
.collect()
}
pub(crate) fn partial_products_and_z_gx<F: Field>(z_x: F, quotient_chunk_products: &[F]) -> Vec<F> {
assert!(!quotient_chunk_products.is_empty());
let mut res = Vec::with_capacity(quotient_chunk_products.len());
let mut acc = z_x;
for "ient_chunk_product in quotient_chunk_products {
acc *= quotient_chunk_product;
res.push(acc);
}
res
}
pub(crate) fn num_partial_products(n: usize, max_degree: usize) -> usize {
debug_assert!(max_degree > 1);
let chunk_size = max_degree;
n.div_ceil(chunk_size) - 1
}
pub(crate) fn check_partial_products<F: Field>(
numerators: &[F],
denominators: &[F],
partials: &[F],
z_x: F,
z_gx: F,
max_degree: usize,
) -> Vec<F> {
debug_assert!(max_degree > 1);
let product_accs = iter::once(&z_x)
.chain(partials.iter())
.chain(iter::once(&z_gx));
let chunk_size = max_degree;
let numerator_chunks = numerators.chunks(chunk_size);
let denominator_chunks = denominators.chunks(chunk_size);
let num_numerator_chunks = numerator_chunks.len();
let num_denominator_chunks = denominator_chunks.len();
let num_product_windows = partials.len() + 1;
assert_eq!(
num_numerator_chunks, num_denominator_chunks,
"numerator and denominator chunk counts disagree: {num_numerator_chunks} vs {num_denominator_chunks}"
);
assert_eq!(
num_numerator_chunks, num_product_windows,
"partial product window count disagrees with chunk count: chunks={num_numerator_chunks}, partials={}, max_degree={}, numerators={}, denominators={}",
partials.len(),
max_degree,
numerators.len(),
denominators.len(),
);
numerators
.chunks(chunk_size)
.zip_eq(denominators.chunks(chunk_size))
.zip_eq(product_accs.tuple_windows())
.map(|((nume_chunk, deno_chunk), (&prev_acc, &next_acc))| {
let num_chunk_product = nume_chunk.iter().copied().product();
let den_chunk_product = deno_chunk.iter().copied().product();
prev_acc * num_chunk_product - next_acc * den_chunk_product
})
.collect()
}
pub(crate) fn check_partial_products_circuit<F: RichField + Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
numerators: &[ExtensionTarget<D>],
denominators: &[ExtensionTarget<D>],
partials: &[ExtensionTarget<D>],
z_x: ExtensionTarget<D>,
z_gx: ExtensionTarget<D>,
max_degree: usize,
) -> Vec<ExtensionTarget<D>> {
debug_assert!(max_degree > 1);
let product_accs = iter::once(&z_x)
.chain(partials.iter())
.chain(iter::once(&z_gx));
let chunk_size = max_degree;
numerators
.chunks(chunk_size)
.zip_eq(denominators.chunks(chunk_size))
.zip_eq(product_accs.tuple_windows())
.map(|((nume_chunk, deno_chunk), (&prev_acc, &next_acc))| {
let nume_product = builder.mul_many_extension(nume_chunk);
let deno_product = builder.mul_many_extension(deno_chunk);
let next_acc_deno = builder.mul_extension(next_acc, deno_product);
builder.mul_sub_extension(prev_acc, nume_product, next_acc_deno)
})
.collect()
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "std"))]
use alloc::vec;
use super::*;
use crate::field::goldilocks_field::GoldilocksField;
#[test]
fn test_partial_products() {
type F = GoldilocksField;
let denominators = vec![F::ONE; 6];
let z_x = F::ONE;
let v = field_vec(&[1, 2, 3, 4, 5, 6]);
let z_gx = F::from_canonical_u64(720);
let quotient_chunks_prods = quotient_chunk_products(&v, 2);
assert_eq!(quotient_chunks_prods, field_vec(&[2, 12, 30]));
let pps_and_z_gx = partial_products_and_z_gx(z_x, "ient_chunks_prods);
let pps = &pps_and_z_gx[..pps_and_z_gx.len() - 1];
assert_eq!(pps_and_z_gx, field_vec(&[2, 24, 720]));
let nums = num_partial_products(v.len(), 2);
assert_eq!(pps.len(), nums);
assert!(check_partial_products(&v, &denominators, pps, z_x, z_gx, 2)
.iter()
.all(|x| x.is_zero()));
let quotient_chunks_prods = quotient_chunk_products(&v, 3);
assert_eq!(quotient_chunks_prods, field_vec(&[6, 120]));
let pps_and_z_gx = partial_products_and_z_gx(z_x, "ient_chunks_prods);
let pps = &pps_and_z_gx[..pps_and_z_gx.len() - 1];
assert_eq!(pps_and_z_gx, field_vec(&[6, 720]));
let nums = num_partial_products(v.len(), 3);
assert_eq!(pps.len(), nums);
assert!(check_partial_products(&v, &denominators, pps, z_x, z_gx, 3)
.iter()
.all(|x| x.is_zero()));
}
fn field_vec<F: Field>(xs: &[usize]) -> Vec<F> {
xs.iter().map(|&x| F::from_canonical_usize(x)).collect()
}
}