2021-06-30 16:48:41 +02:00
|
|
|
use std::iter::Product;
|
2021-07-01 15:20:16 +02:00
|
|
|
use std::ops::Sub;
|
2021-06-30 16:48:41 +02:00
|
|
|
|
2021-07-01 15:41:01 +02:00
|
|
|
use crate::util::ceil_div_usize;
|
|
|
|
|
|
2021-07-01 17:28:30 +02:00
|
|
|
/// Compute partial products of the original vector `v` such that no products are of `max_degree` or
|
|
|
|
|
/// less elements. This is done until we've computed the product `P` of all elements in the vector.
|
|
|
|
|
/// The final product resulting in `P` has `max_degree-1` elements at most since `P` is multiplied
|
|
|
|
|
/// by the `Z` polynomial in the Plonk check.
|
2021-07-01 15:41:01 +02:00
|
|
|
pub fn partial_products<T: Product + Copy>(v: &[T], max_degree: usize) -> Vec<T> {
|
2021-06-30 16:48:41 +02:00
|
|
|
let mut res = Vec::new();
|
2021-07-01 15:20:16 +02:00
|
|
|
let mut remainder = v.to_vec();
|
2021-06-30 18:54:28 +02:00
|
|
|
while remainder.len() >= max_degree {
|
2021-06-30 16:48:41 +02:00
|
|
|
let new_partials = remainder
|
|
|
|
|
.chunks(max_degree)
|
2021-07-01 17:28:30 +02:00
|
|
|
// No need to compute the product if the chunk has size 1.
|
2021-07-01 15:57:55 +02:00
|
|
|
.filter(|chunk| chunk.len() != 1)
|
2021-06-30 16:48:41 +02:00
|
|
|
.map(|chunk| chunk.iter().copied().product())
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
res.extend_from_slice(&new_partials);
|
2021-07-01 15:57:55 +02:00
|
|
|
let addendum = if remainder.len() % max_degree == 1 {
|
|
|
|
|
vec![*remainder.last().unwrap()]
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
};
|
2021-06-30 16:48:41 +02:00
|
|
|
remainder = new_partials;
|
2021-07-01 17:28:30 +02:00
|
|
|
// If there were a chunk of size 1, add it back to the remainder.
|
2021-07-01 15:57:55 +02:00
|
|
|
remainder.extend(addendum);
|
2021-06-30 16:48:41 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-01 15:41:01 +02:00
|
|
|
res
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-01 17:28:30 +02:00
|
|
|
/// Returns a tuple `(a,b)`, where `a` is the length of the output of `partial_products()` on a
|
|
|
|
|
/// vector of length `n`, and `b` is the number of elements needed to compute the final product.
|
2021-07-01 15:41:01 +02:00
|
|
|
pub fn num_partial_products(n: usize, max_degree: usize) -> (usize, usize) {
|
|
|
|
|
let mut res = 0;
|
|
|
|
|
let mut remainder = n;
|
|
|
|
|
while remainder >= max_degree {
|
|
|
|
|
let new_partials_len = ceil_div_usize(remainder, max_degree);
|
2021-07-01 15:57:55 +02:00
|
|
|
let addendum = if remainder % max_degree == 1 { 1 } else { 0 };
|
|
|
|
|
res += new_partials_len - addendum;
|
2021-07-01 15:41:01 +02:00
|
|
|
remainder = new_partials_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(res, remainder)
|
2021-07-01 15:20:16 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-01 17:28:30 +02:00
|
|
|
/// Checks that the partial products of `v` are coherent with those in `partials` by only computing
|
|
|
|
|
/// products of size `max_degree` or less.
|
2021-07-01 15:20:16 +02:00
|
|
|
pub fn check_partial_products<T: Product + Copy + Sub<Output = T>>(
|
|
|
|
|
v: &[T],
|
|
|
|
|
partials: &[T],
|
|
|
|
|
max_degree: usize,
|
|
|
|
|
) -> Vec<T> {
|
|
|
|
|
let mut res = Vec::new();
|
|
|
|
|
let mut remainder = v.to_vec();
|
|
|
|
|
let mut partials = partials.to_vec();
|
|
|
|
|
while remainder.len() >= max_degree {
|
|
|
|
|
let products = remainder
|
|
|
|
|
.chunks(max_degree)
|
2021-07-01 15:57:55 +02:00
|
|
|
.filter(|chunk| chunk.len() != 1)
|
2021-07-01 15:20:16 +02:00
|
|
|
.map(|chunk| chunk.iter().copied().product())
|
|
|
|
|
.collect::<Vec<T>>();
|
|
|
|
|
res.extend(products.iter().zip(&partials).map(|(&a, &b)| a - b));
|
2021-07-01 15:57:55 +02:00
|
|
|
let addendum = if remainder.len() % max_degree == 1 {
|
|
|
|
|
vec![*remainder.last().unwrap()]
|
|
|
|
|
} else {
|
|
|
|
|
vec![]
|
|
|
|
|
};
|
2021-07-01 15:20:16 +02:00
|
|
|
remainder = partials.drain(..products.len()).collect();
|
2021-07-01 15:57:55 +02:00
|
|
|
remainder.extend(addendum)
|
2021-07-01 15:20:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res
|
2021-06-30 16:48:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2021-07-01 15:20:16 +02:00
|
|
|
use num::Zero;
|
|
|
|
|
|
2021-06-30 16:48:41 +02:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_partial_products() {
|
2021-07-01 15:20:16 +02:00
|
|
|
let v = vec![1, 2, 3, 4, 5, 6];
|
|
|
|
|
let p = partial_products(&v, 2);
|
2021-07-01 15:57:55 +02:00
|
|
|
assert_eq!(p, vec![2, 12, 30, 24, 720]);
|
2021-07-02 10:20:44 +02:00
|
|
|
let nums = num_partial_products(v.len(), 2);
|
|
|
|
|
assert_eq!(p.len(), nums.0);
|
2021-07-01 15:41:01 +02:00
|
|
|
assert!(check_partial_products(&v, &p, 2)
|
2021-07-01 15:20:16 +02:00
|
|
|
.iter()
|
|
|
|
|
.all(|x| x.is_zero()));
|
2021-07-02 10:20:44 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
v.into_iter().product::<i32>(),
|
|
|
|
|
p[p.len() - nums.1..].iter().copied().product(),
|
|
|
|
|
);
|
2021-07-01 15:20:16 +02:00
|
|
|
|
|
|
|
|
let v = vec![1, 2, 3, 4, 5, 6];
|
|
|
|
|
let p = partial_products(&v, 3);
|
2021-07-01 15:41:01 +02:00
|
|
|
assert_eq!(p, vec![6, 120]);
|
2021-07-02 10:20:44 +02:00
|
|
|
let nums = num_partial_products(v.len(), 3);
|
|
|
|
|
assert_eq!(p.len(), nums.0);
|
2021-07-01 15:41:01 +02:00
|
|
|
assert!(check_partial_products(&v, &p, 3)
|
2021-07-01 15:20:16 +02:00
|
|
|
.iter()
|
|
|
|
|
.all(|x| x.is_zero()));
|
2021-07-02 10:20:44 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
v.into_iter().product::<i32>(),
|
|
|
|
|
p[p.len() - nums.1..].iter().copied().product(),
|
|
|
|
|
);
|
2021-06-30 16:48:41 +02:00
|
|
|
}
|
|
|
|
|
}
|