temporary commit

This commit is contained in:
Alex Vlasov 2019-03-15 18:00:52 +03:00
parent 2a5dc08cf6
commit 500f33d1e6
8 changed files with 793 additions and 11 deletions

BIN
src/sonic/S_poly.pdf Normal file

Binary file not shown.

@ -74,11 +74,11 @@ impl<E: Engine> SxEval<E> {
let mut acc = E::Fr::zero();
let mut tmp = x_inv;
let tmp = x_inv;
acc.add_assign(&evaluate_at_consequitive_powers(& self.u[..], tmp, tmp));
let mut tmp = x;
let tmp = x;
acc.add_assign(&evaluate_at_consequitive_powers(& self.v[..], tmp, tmp));
let mut tmp = x.pow(&[(self.v.len()+1) as u64]);
let tmp = x.pow(&[(self.v.len()+1) as u64]);
acc.add_assign(&evaluate_at_consequitive_powers(& self.w[..], tmp, x));
// let mut tmp = x_inv;

@ -240,7 +240,6 @@ pub fn create_proof_on_srs<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
rx1.push(E::Fr::zero());
rx1.extend(wires.a);
let mut rxy = rx1.clone();
let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?;

@ -537,4 +537,102 @@ fn test_high_level_sonic_api() {
println!("done in {:?}", start.elapsed());
}
}
}
#[test]
fn test_constraints_info() {
use pairing::bn256::{Bn256};
use std::time::{Instant};
use crate::sonic::unhelped::padding::{constraints_info};
{
// This may not be cryptographically safe, use
// `OsRng` (for example) in production software.
let mut rng = &mut thread_rng();
// Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
let xl = rng.gen();
let xr = rng.gen();
let image = mimc::<Bn256>(xl, xr, &constants);
// Create an instance of our circuit (with the
// witness)
let circuit = MiMCDemo {
xl: Some(xl),
xr: Some(xr),
constants: &constants
};
constraints_info::<Bn256, _>(circuit.clone());
}
}
#[test]
fn test_padding_using_mimc() {
use pairing::ff::{Field, PrimeField};
use pairing::{Engine, CurveAffine, CurveProjective};
use pairing::bls12_381::{Bls12, Fr};
use std::time::{Instant};
use crate::sonic::srs::SRS;
let srs_x = Fr::from_str("23923").unwrap();
let srs_alpha = Fr::from_str("23728792").unwrap();
println!("making srs");
let start = Instant::now();
let srs = SRS::<Bls12>::dummy(830564, srs_x, srs_alpha);
println!("done in {:?}", start.elapsed());
{
// This may not be cryptographically safe, use
// `OsRng` (for example) in production software.
let rng = &mut thread_rng();
// Generate the MiMC round constants
let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::<Vec<_>>();
let samples: usize = 100;
let xl = rng.gen();
let xr = rng.gen();
let image = mimc::<Bls12>(xl, xr, &constants);
// Create an instance of our circuit (with the
// witness)
let circuit = MiMCDemoNoInputs {
xl: Some(xl),
xr: Some(xr),
image: Some(image),
constants: &constants
};
use crate::sonic::cs::Basic;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs};
use crate::sonic::helped::{MultiVerifier, get_circuit_parameters};
use crate::sonic::helped::helper::{create_aggregate_on_srs};
use crate::sonic::unhelped::padding::Padding;
let info = get_circuit_parameters::<Bls12, _>(circuit.clone()).expect("Must get circuit info");
println!("{:?}", info);
println!("creating proof");
let start = Instant::now();
let proof = create_proof_on_srs::<Bls12, _, Padding>(&AdaptorCircuit(circuit.clone()), &srs).unwrap();
println!("done in {:?}", start.elapsed());
{
let rng = thread_rng();
let mut verifier = MultiVerifier::<Bls12, _, Padding, _>::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap();
println!("K map = {:?}", verifier.get_k_map());
println!("verifying 1 proof without advice");
let start = Instant::now();
{
for _ in 0..1 {
verifier.add_proof(&proof, &[], |_, _| None);
}
assert_eq!(verifier.check_all(), true); // TODO
}
println!("done in {:?}", start.elapsed());
}
}
}

@ -7,5 +7,6 @@ mod s2_proof;
mod wellformed_argument;
mod grand_product_argument;
mod permutation_argument;
pub mod padding;
pub use self::wellformed_argument::{WellformednessArgument, WellformednessProof};

@ -0,0 +1,686 @@
use pairing::ff::{Field};
use pairing::{Engine, CurveProjective};
use std::marker::PhantomData;
use crate::sonic::cs::{Backend};
use crate::sonic::cs::{Coeff, Variable, LinearCombination};
use crate::sonic::util::*;
use crate::sonic::util::*;
use crate::sonic::cs::{SynthesisDriver};
use crate::Circuit as BellmanCircuit;
use crate::sonic::sonic::AdaptorCircuit;
use crate::sonic::cs::Circuit;
use crate::sonic::cs::ConstraintSystem;
use crate::sonic::cs::Nonassigning;
use crate::SynthesisError;
/*
s_1(X, Y) = \sum\limits_{i=1}^N u_i(Y) X^{N + 1 - i}
+ \sum\limits_{i=1}^N v_i(Y) X^{N + 1 + i}
+ \sum\limits_{i=1}^N w_i(Y) X^{2N + 1 + i}
where
u_i(Y) = \sum\limits_{q=1}^Q Y^{q} u_{i,q}
v_i(Y) = \sum\limits_{q=1}^Q Y^{q} v_{i,q}
w_i(Y) = \sum\limits_{q=1}^Q Y^{q} w_{i,q}
s_1(X, Y) = \sum\limits_{i=1}^(3N + 1) [u_{N + 1 - i}(Y), v_{i - N - 1}(Y), w_{i - 2N - 1}(Y)] X^{i}
where [] means concatenation
if we open up both sums a little it would look like
// q = 1,
Y * ( X * u_{N, 1} + X^{N + 1} * v_{1, 1} + X^{2N + 1} * w{1, 1}) = Y * (k_0 * X + k_1 * X^{N + 1} + k_2 * X^{2N + 1})
and for permutation where should exist another term over Y that would have the same structure, but with coefficients permuted, e.g.
Y^{p_1} * (k_1 * X + k_2 * X^{N + 1} + k_0 * X^{2N + 1}) and Y^{p_2} * (k_2 * X + k_0 * X^{N + 1} + k_1 * X^{2N + 1})
that would result in a sum
X * (k_0 * Y + k_1 * Y^{p_1} + k_2 * Y^{p_2})
+ X^{N + 1} * (k_1 * Y + k_2 * Y^{p_1} + k_0 * Y^{p_2})
+ X^{2N + 1} * (k_2 * Y + k_0 * Y^{p_1} + k_1 * Y^{p_2})
and permutations would look like
[k_0, k_1, k_2]
[1 , p_1, p_2]
[k_0, k_1, k_2]
[p_2, 1 , p_1]
[k_0, k_1, k_2]
[p_1, p_2, 1 ]
that would naively mean that k_0 should appear in constraint number 1 for variable number 1
constraint number p_1 for variable number N + 1
constraint number p_2 for variable number 2N + 1
restructuring strategy:
where u_{i, q} is a coefficient in a linear constraint for an A type variable number i
that corresponds to the qth multiplication gate
to make s_1 representable as a permutation we first must synthesize all the normal constraints,
then make what would look like a cyclic shift + expansion
- imagine that there were originally N variables
- variable A(i) in linear constraint number q had a coefficient of u{i, q}
- add a variable B(i+n) that would have a number
*/
pub struct Debugging<E> {
constraint_num: usize,
u: Vec<String>,
v: Vec<String>,
w: Vec<String>,
_marker: std::marker::PhantomData<E>
}
impl<'a, E: Engine> Backend<E> for &'a mut Debugging<E> {
fn new_linear_constraint(&mut self) {
self.constraint_num += 1;
self.u.push("".to_string());
self.v.push("".to_string());
self.w.push("".to_string());
}
fn insert_coefficient(&mut self, var: Variable, coeff: Coeff<E>) {
let one = E::Fr::one();
let mut minus_one = one;
minus_one.negate();
match var {
Variable::A(index) => {
let acc = &mut self.u[self.constraint_num - 1];
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.push_str(&format!(" + A{}", index));
},
Coeff::NegativeOne => {
acc.push_str(&format!(" - A{}", index));
},
Coeff::Full(val) => {
if val == one {
acc.push_str(&format!(" + A{}", index));
} else if val == minus_one {
acc.push_str(&format!(" - A{}", index));
} else {
acc.push_str(&format!(" + {}*A{}", val, index));
}
}
}
}
Variable::B(index) => {
let acc = &mut self.v[self.constraint_num - 1];
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.push_str(&format!(" + B{}", index));
},
Coeff::NegativeOne => {
acc.push_str(&format!(" - B{}", index));
},
Coeff::Full(val) => {
if val == one {
acc.push_str(&format!(" + B{}", index));
} else if val == minus_one {
acc.push_str(&format!(" - B{}", index));
} else {
acc.push_str(&format!(" + {}*B{}", val, index));
}
}
}
}
Variable::C(index) => {
let acc = &mut self.w[self.constraint_num - 1];
match coeff {
Coeff::Zero => { },
Coeff::One => {
acc.push_str(&format!(" + C{}", index));
},
Coeff::NegativeOne => {
acc.push_str(&format!(" - C{}", index));
},
Coeff::Full(val) => {
if val == one {
acc.push_str(&format!(" + C{}", index));
} else if val == minus_one {
acc.push_str(&format!(" - C{}", index));
} else {
acc.push_str(&format!(" + {}*C{}", val, index));
}
}
}
}
};
}
}
pub struct Padding;
impl SynthesisDriver for Padding {
fn synthesize<E: Engine, C: Circuit<E>, B: Backend<E>>(backend: B, circuit: &C) -> Result<(), SynthesisError> {
struct Synthesizer<E: Engine, B: Backend<E>> {
backend: B,
current_variable: Option<usize>,
_marker: PhantomData<E>,
q: usize,
n: usize,
}
impl<E: Engine, B: Backend<E>>Synthesizer<E, B> {
fn purge_current_var(&mut self) {
match self.current_variable.take() {
Some(index) => {
let var_a = Variable::A(index);
let var_b = Variable::B(index);
let var_c = Variable::C(index);
let mut product = None;
let value_a = self.backend.get_var(var_a);
self.backend.set_var(var_b, || {
let value_b = E::Fr::one();
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
product.as_mut().map(|product| product.mul_assign(&value_b));
Ok(value_b)
}).expect("should exist by now");
self.backend.set_var(var_c, || {
product.ok_or(SynthesisError::AssignmentMissing)
}).expect("should exist by now");
self.current_variable = None;
},
_ => {}
}
}
fn alloc_one(&mut self) -> Variable {
self.n += 1;
let index = self.n;
assert_eq!(index, 1);
self.backend.new_multiplication_gate();
let var_a = Variable::A(1);
let var_b = Variable::B(1);
let var_c = Variable::C(1);
self.backend.set_var(var_a, || {
Ok(E::Fr::one())
}).expect("should exist by now");
self.backend.set_var(var_b, || {
Ok(E::Fr::one())
}).expect("should exist by now");
self.backend.set_var(var_c, || {
Ok(E::Fr::one())
}).expect("should exist by now");
self.q += 1;
self.backend.new_linear_constraint();
self.backend.insert_coefficient(var_a, Coeff::One);
self.backend.insert_coefficient(var_b, Coeff::One);
self.backend.insert_coefficient(var_c, Coeff::NegativeOne);
self.backend.new_k_power(self.q);
var_a
}
}
impl<E: Engine, B: Backend<E>> ConstraintSystem<E> for Synthesizer<E, B> {
const ONE: Variable = Variable::A(1);
fn alloc<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
match self.current_variable.take() {
Some(index) => {
let var_a = Variable::A(index);
let var_b = Variable::B(index);
let var_c = Variable::C(index);
let mut product = None;
let value_a = self.backend.get_var(var_a);
self.backend.set_var(var_b, || {
let value_b = value()?;
product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?);
product.as_mut().map(|product| product.mul_assign(&value_b));
Ok(value_b)
})?;
self.backend.set_var(var_c, || {
product.ok_or(SynthesisError::AssignmentMissing)
})?;
self.current_variable = None;
Ok(var_b)
},
None => {
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let var_a = Variable::A(index);
self.backend.set_var(var_a, value)?;
self.current_variable = Some(index);
Ok(var_a)
}
}
}
// TODO: allocate input without spawning extra constraints
fn alloc_input<F>(&mut self, value: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>
{
// self.purge_current_var();
// self.n += 1;
// self.backend.new_multiplication_gate();
// let index = self.n;
// let var = Variable::A::(index);
// self.q += 1;
// self.backend.new_k_power(self.q);
// self.backend.self.backend.insert_coefficient(new_var, Coeff::One);
// it's always going to be
let input_var = self.alloc(value)?;
self.enforce_zero(LinearCombination::zero() + input_var);
self.backend.new_k_power(self.q-2);
self.backend.new_k_power(self.q-1);
self.backend.new_k_power(self.q);
Ok(input_var)
}
fn enforce_zero(&mut self, lc: LinearCombination<E>)
{
self.q += 1;
self.backend.new_linear_constraint();
for (var, coeff) in lc.as_ref() {
self.backend.insert_coefficient(*var, *coeff);
}
// now we need to "rotate" a linear constraint by allocating more dummy variables, so ensuring
// that if for some q (index of LC) there is a coefficient C in front of a variable A(i) (that will result in a term ~ C*Y^{q}*X^{i})
// then there will be some other q' where there is a coefficient C in front of the variable B(i)
// (that will result in a term ~ C*Y^{q'}*X^{i+N}) and another q'' with C in front of C(i)
// (that will result in a term ~ C*Y^{q''}*X^{i+2N}), so S polynomial is indeed a permutation
// allocate at max 1 variable to later work with whole gates directly
self.purge_current_var();
use std::collections::HashMap;
// A -> B, B -> C, C -> A
{
self.q += 1;
self.backend.new_linear_constraint();
let mut allocation_map = HashMap::with_capacity(lc.as_ref().len());
let mut expected_new_index = self.n + 1;
// determine size of the map
for (var, _) in lc.as_ref() {
match var {
Variable::A(index) => {
if allocation_map.get(index).is_none() && *index != 1 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("A{} -> B{}", index, expected_new_index);
}
},
Variable::B(index) => {
if allocation_map.get(index).is_none() && *index != 2 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("B{} -> C{}", index, expected_new_index);
}
},
Variable::C(index) => {
if allocation_map.get(index).is_none() && *index != 3 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("C{} -> A{}", index, expected_new_index);
}
}
}
}
for _ in 0..allocation_map.len() {
self.backend.new_multiplication_gate();
self.n += 1;
}
for (index, new_index) in allocation_map.iter() {
let var_a = Variable::A(*new_index);
let var_b = Variable::B(*new_index);
let var_c = Variable::C(*new_index);
// A -> B, B -> C, C -> A
let b_val = self.backend.get_var(Variable::A(*index));
let c_val = self.backend.get_var(Variable::B(*index));
let a_val = self.backend.get_var(Variable::C(*index));
self.backend.set_var(var_a, || {
let value = a_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
self.backend.set_var(var_b, || {
let value = b_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
self.backend.set_var(var_c, || {
let value = c_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
}
// A -> B, B -> C, C -> A
for (var, coeff) in lc.as_ref() {
let new_var = match var {
Variable::A(index) => {
let var = if *index == 1 {
Variable::B(2)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::B(*new_index)
};
var
},
Variable::B(index) => {
let var = if *index == 2 {
Variable::C(3)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::C(*new_index)
};
var
},
Variable::C(index) => {
let var = if *index == 3 {
Variable::A(1)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::A(*new_index)
};
var
}
};
self.backend.insert_coefficient(new_var, *coeff);
}
}
// A -> C, B -> A, C -> B
{
self.q += 1;
self.backend.new_linear_constraint();
let mut allocation_map = HashMap::with_capacity(lc.as_ref().len());
let mut expected_new_index = self.n + 1;
// determine size of the map
for (var, _) in lc.as_ref() {
match var {
Variable::A(index) => {
if allocation_map.get(index).is_none() && *index != 1 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("A{} -> C{}", index, expected_new_index);
}
},
Variable::B(index) => {
if allocation_map.get(index).is_none() && *index != 2 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("B{} -> A{}", index, expected_new_index);
}
},
Variable::C(index) => {
if allocation_map.get(index).is_none() && *index != 3 {
allocation_map.insert(*index, expected_new_index);
expected_new_index += 1;
println!("C{} -> B{}", index, expected_new_index);
}
}
}
}
for _ in 0..allocation_map.len() {
self.backend.new_multiplication_gate();
self.n += 1;
}
// A -> C, B -> A, C -> B
for (index, new_index) in allocation_map.iter() {
let var_a = Variable::A(*new_index);
let var_b = Variable::B(*new_index);
let var_c = Variable::C(*new_index);
let b_val = self.backend.get_var(Variable::C(*index));
let c_val = self.backend.get_var(Variable::A(*index));
let a_val = self.backend.get_var(Variable::B(*index));
self.backend.set_var(var_a, || {
let value = a_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
self.backend.set_var(var_b, || {
let value = b_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
self.backend.set_var(var_c, || {
let value = c_val.ok_or(SynthesisError::AssignmentMissing)?;
Ok(value)
}).expect("should exist by now");
}
// A -> C, B -> A, C -> B
for (var, coeff) in lc.as_ref() {
let new_var = match var {
Variable::A(index) => {
let var = if *index == 1 {
Variable::C(3)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::C(*new_index)
};
var
},
Variable::B(index) => {
let var = if *index == 2 {
Variable::A(1)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::A(*new_index)
};
var
},
Variable::C(index) => {
let var = if *index == 3 {
Variable::B(2)
} else {
let new_index = allocation_map.get(index).unwrap();
Variable::B(*new_index)
};
var
}
};
self.backend.insert_coefficient(new_var, *coeff);
}
}
}
fn multiply<F>(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError>
where
F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>
{
self.n += 1;
let index = self.n;
self.backend.new_multiplication_gate();
let a = Variable::A(index);
let b = Variable::B(index);
let c = Variable::C(index);
let mut b_val = None;
let mut c_val = None;
self.backend.set_var(a, || {
let (a, b, c) = values()?;
b_val = Some(b);
c_val = Some(c);
Ok(a)
})?;
self.backend.set_var(b, || {
b_val.ok_or(SynthesisError::AssignmentMissing)
})?;
self.backend.set_var(c, || {
c_val.ok_or(SynthesisError::AssignmentMissing)
})?;
Ok((a, b, c))
}
fn get_value(&self, var: Variable) -> Result<E::Fr, ()> {
self.backend.get_var(var).ok_or(())
}
}
let mut tmp: Synthesizer<E, B> = Synthesizer {
backend: backend,
current_variable: None,
_marker: PhantomData,
q: 0,
n: 0,
};
let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues");
match (one, <Synthesizer<E, B> as ConstraintSystem<E>>::ONE) {
(Variable::A(1), Variable::A(1)) => {},
_ => panic!("one variable is incorrect")
}
circuit.synthesize(&mut tmp)?;
println!("Done synthesizing, N = {}, Q = {}", tmp.n, tmp.q);
Ok(())
}
}
pub fn constraints_info<E: Engine, C: BellmanCircuit<E> + Clone>(
circuit: C,
)
{
let adapted_circuit = AdaptorCircuit(circuit);
create_constraints_info::<_, _, Nonassigning>(&adapted_circuit)
}
pub fn constraints_padding_info<E: Engine, C: BellmanCircuit<E> + Clone>(
circuit: C,
)
{
let adapted_circuit = AdaptorCircuit(circuit);
create_constraints_info::<_, _, Padding>(&adapted_circuit)
}
pub fn create_constraints_info<E: Engine, C: Circuit<E>, S: SynthesisDriver>(
circuit: &C,
)
{
let mut backend = Debugging::<E> {
constraint_num: 0,
u: vec![],
v: vec![],
w: vec![],
_marker: std::marker::PhantomData
};
S::synthesize(&mut backend, circuit).unwrap();
for (i, ((u, v), w)) in backend.u.iter()
.zip(backend.v.iter())
.zip(backend.w.iter())
.enumerate()
{
println!("Constraint {}: 0 = {}{}{}", i, u, v, w);
}
}
#[test]
fn my_fun_circuit_test() {
use pairing::ff::PrimeField;
use pairing::bls12_381::{Bls12, Fr};
struct MyCircuit;
impl<E: Engine> Circuit<E> for MyCircuit {
fn synthesize<CS: ConstraintSystem<E>>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
let (a, b, _) = cs.multiply(|| {
Ok((
E::Fr::from_str("10").unwrap(),
E::Fr::from_str("20").unwrap(),
E::Fr::from_str("200").unwrap(),
))
})?;
cs.enforce_zero(LinearCombination::from(a) + a - b);
let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?;
cs.enforce_zero(LinearCombination::from(b) - multiplier);
Ok(())
}
}
create_constraints_info::<Bls12, _, Nonassigning>(&MyCircuit);
println!("---------------");
create_constraints_info::<Bls12, _, Padding>(&MyCircuit);
}

@ -43,8 +43,6 @@ pub struct Proof<E: Engine> {
s_zy: E::Fr
}
fn permute<F: Field>(coeffs: &[F], permutation: & [usize]) -> Vec<F>{
assert_eq!(coeffs.len(), permutation.len());
let mut result: Vec<F> = vec![F::zero(); coeffs.len()];

@ -123,12 +123,12 @@ pub fn polynomial_commitment_opening<
) -> E::G1Affine
where I::IntoIter: DoubleEndedIterator + ExactSizeIterator,
{
let poly = parallel_kate_divison::<E, _>(polynomial_coefficients, point);
// let poly = parallel_kate_divison::<E, _>(polynomial_coefficients, point);
// let poly = kate_divison(
// polynomial_coefficients,
// point,
// );
let poly = kate_divison(
polynomial_coefficients,
point,
);
let negative_poly = poly[0..largest_negative_power].iter().rev();
let positive_poly = poly[largest_negative_power..].iter();