use crate::{
allocated_nonnative_field_mul_result_var::AllocatedNonNativeFieldMulResultVar,
params::{get_params, OptimizationType},
reduce::{bigint_to_basefield, limbs_to_bigint, Reducer},
};
use snarkvm_fields::{FieldParameters, PrimeField};
use snarkvm_gadgets::{
fields::FpGadget,
traits::{
fields::FieldGadget,
utilities::{
alloc::AllocGadget,
boolean::Boolean,
eq::EqGadget,
integer::Integer,
select::{CondSelectGadget, ThreeBitCondNegLookupGadget, TwoBitLookupGadget},
uint::UInt8,
ToBitsBEGadget,
ToBitsLEGadget,
ToBytesGadget,
},
},
};
use snarkvm_r1cs::{errors::SynthesisError, Assignment, ConstraintSystem};
use snarkvm_utilities::BigInteger;
use std::{
borrow::Borrow,
cmp::{max, min},
marker::PhantomData,
};
#[derive(Debug)]
pub struct AllocatedNonNativeFieldVar<TargetField: PrimeField, BaseField: PrimeField> {
pub limbs: Vec<FpGadget<BaseField>>,
pub num_of_additions_over_normal_form: BaseField,
pub is_in_the_normal_form: bool,
#[doc(hidden)]
pub target_phantom: PhantomData<TargetField>,
}
impl<TargetField: PrimeField, BaseField: PrimeField> AllocatedNonNativeFieldVar<TargetField, BaseField> {
pub fn limbs_to_value(limbs: Vec<BaseField>, optimization_type: OptimizationType) -> TargetField {
let params = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
optimization_type,
);
let mut base_repr: <TargetField as PrimeField>::BigInteger = TargetField::one().into_repr();
base_repr.muln((params.bits_per_limb - 1) as u32);
let mut base: TargetField = TargetField::from_repr(base_repr).unwrap();
base = base + &base;
let mut result = TargetField::zero();
let mut power = TargetField::one();
for limb in limbs.iter().rev() {
let mut val = TargetField::zero();
let mut cur = TargetField::one();
for bit in limb.into_repr().to_bits_be().iter().rev() {
if *bit {
val += &cur;
}
cur.double_in_place();
}
result += &(val * &power);
power *= &base;
}
result
}
pub fn value(&self) -> Result<TargetField, SynthesisError> {
let mut limbs = Vec::new();
for limb in self.limbs.iter() {
limbs.push(limb.get_value().get()?);
}
Ok(Self::limbs_to_value(limbs, self.get_optimization_type()))
}
pub fn constant<CS: ConstraintSystem<BaseField>>(cs: &mut CS, value: TargetField) -> Result<Self, SynthesisError> {
let optimization_type = OptimizationType::Weight;
let limbs_value = Self::get_limbs_representations(&value, optimization_type)?;
let mut limbs = Vec::new();
for (i, limb_value) in limbs_value.iter().enumerate() {
limbs.push(FpGadget::<BaseField>::alloc_constant(
cs.ns(|| format!("alloc_constant_limb_{}", i)),
|| Ok(limb_value),
)?);
}
Ok(Self {
limbs,
num_of_additions_over_normal_form: BaseField::zero(),
is_in_the_normal_form: true,
target_phantom: PhantomData,
})
}
pub fn one<CS: ConstraintSystem<BaseField>>(cs: &mut CS) -> Result<Self, SynthesisError> {
Self::constant(cs, TargetField::one())
}
pub fn zero<CS: ConstraintSystem<BaseField>>(cs: &mut CS) -> Result<Self, SynthesisError> {
Self::constant(cs, TargetField::zero())
}
pub fn add<CS: ConstraintSystem<BaseField>>(&self, cs: &mut CS, other: &Self) -> Result<Self, SynthesisError> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let mut limbs = Vec::new();
for (i, (this_limb, other_limb)) in self.limbs.iter().zip(other.limbs.iter()).enumerate() {
limbs.push(this_limb.add(cs.ns(|| format!("add_limb_{}", i)), &other_limb)?);
}
let mut res = Self {
limbs,
num_of_additions_over_normal_form: self
.num_of_additions_over_normal_form
.add(&other.num_of_additions_over_normal_form)
.add(&BaseField::one()),
is_in_the_normal_form: false,
target_phantom: PhantomData,
};
Reducer::<TargetField, BaseField>::post_add_reduce(cs, &mut res)?;
Ok(res)
}
pub fn add_constant<CS: ConstraintSystem<BaseField>>(
&self,
cs: &mut CS,
other: &TargetField,
) -> Result<Self, SynthesisError> {
let other_limbs = Self::get_limbs_representations(other, self.get_optimization_type())?;
let mut limbs = Vec::new();
for (i, (this_limb, other_limb)) in self.limbs.iter().zip(other_limbs.iter()).enumerate() {
limbs.push(this_limb.add_constant(cs.ns(|| format!("add_constant_{}", i)), other_limb)?);
}
let mut res = Self {
limbs,
num_of_additions_over_normal_form: self.num_of_additions_over_normal_form.add(&BaseField::one()),
is_in_the_normal_form: false,
target_phantom: PhantomData,
};
Reducer::<TargetField, BaseField>::post_add_reduce(cs, &mut res)?;
Ok(res)
}
pub fn sub_without_reduce<CS: ConstraintSystem<BaseField>>(
&self,
cs: &mut CS,
other: &Self,
) -> Result<Self, SynthesisError> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let params = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
self.get_optimization_type(),
);
let mut surfeit = overhead!(other.num_of_additions_over_normal_form + &BaseField::one()) + 1;
let mut other = other.clone();
if (surfeit + params.bits_per_limb > BaseField::size_in_bits() - 1)
|| (surfeit + (TargetField::size_in_bits() - params.bits_per_limb * (params.num_limbs - 1))
> BaseField::size_in_bits() - 1)
{
Reducer::reduce(cs, &mut other)?;
surfeit = overhead!(other.num_of_additions_over_normal_form + &BaseField::one()) + 1;
}
let mut pad_non_top_limb_repr: <BaseField as PrimeField>::BigInteger = BaseField::one().into_repr();
let mut pad_top_limb_repr: <BaseField as PrimeField>::BigInteger = pad_non_top_limb_repr;
pad_non_top_limb_repr.muln((surfeit + params.bits_per_limb) as u32);
let pad_non_top_limb = BaseField::from_repr(pad_non_top_limb_repr).unwrap();
pad_top_limb_repr
.muln((surfeit + (TargetField::size_in_bits() - params.bits_per_limb * (params.num_limbs - 1))) as u32);
let pad_top_limb = BaseField::from_repr(pad_top_limb_repr).unwrap();
let mut pad_limbs = Vec::with_capacity(self.limbs.len());
pad_limbs.push(pad_top_limb);
for _ in 0..self.limbs.len() - 1 {
pad_limbs.push(pad_non_top_limb);
}
let pad_to_kp_gap = Self::limbs_to_value(pad_limbs, self.get_optimization_type()).neg();
let pad_to_kp_limbs = Self::get_limbs_representations(&pad_to_kp_gap, self.get_optimization_type())?;
let mut limbs = Vec::new();
for (i, ((this_limb, other_limb), pad_to_kp_limb)) in self
.limbs
.iter()
.zip(other.limbs.iter())
.zip(pad_to_kp_limbs.iter())
.enumerate()
{
if i != 0 {
let temp = pad_non_top_limb + &*pad_to_kp_limb;
limbs.push(
this_limb
.add_constant(cs.ns(|| format!("add_constant_{}", i)), &temp)?
.sub(cs.ns(|| format!("sub_{}", i)), &other_limb)?,
);
} else {
let temp = pad_top_limb + &*pad_to_kp_limb;
limbs.push(
this_limb
.add_constant(cs.ns(|| format!("add_constant_{}", i)), &temp)?
.sub(cs.ns(|| format!("sub_{}", i)), &other_limb)?,
);
}
}
let result = AllocatedNonNativeFieldVar::<TargetField, BaseField> {
limbs,
num_of_additions_over_normal_form: self.num_of_additions_over_normal_form
+ &(other.num_of_additions_over_normal_form + &BaseField::one())
+ &(other.num_of_additions_over_normal_form + &BaseField::one()),
is_in_the_normal_form: false,
target_phantom: PhantomData,
};
Ok(result)
}
pub fn sub<CS: ConstraintSystem<BaseField>>(&self, cs: &mut CS, other: &Self) -> Result<Self, SynthesisError> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let mut result = self.sub_without_reduce(&mut cs.ns(|| "sub_without_reduce"), other)?;
Reducer::<TargetField, BaseField>::post_add_reduce(cs, &mut result)?;
Ok(result)
}
pub fn sub_constant<CS: ConstraintSystem<BaseField>>(
&self,
cs: &mut CS,
other: &TargetField,
) -> Result<Self, SynthesisError> {
let constant = Self::constant(&mut cs.ns(|| "constant"), *other)?;
self.sub(&mut cs.ns(|| "sub"), &constant)
}
pub fn mul<CS: ConstraintSystem<BaseField>>(&self, cs: &mut CS, other: &Self) -> Result<Self, SynthesisError> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type());
self.mul_without_reduce(cs, &other)?.reduce(&mut cs.ns(|| "reduce"))
}
pub fn mul_constant<CS: ConstraintSystem<BaseField>>(
&self,
cs: &mut CS,
other: &TargetField,
) -> Result<Self, SynthesisError> {
let constant = Self::constant(&mut cs.ns(|| "constant"), *other)?;
self.mul(&mut cs.ns(|| "mul"), &constant)
}
pub fn negate<CS: ConstraintSystem<BaseField>>(&self, cs: &mut CS) -> Result<Self, SynthesisError> {
let zero = Self::zero(&mut cs.ns(|| "zero"))?;
zero.sub(&mut cs.ns(|| "sub"), self)
}
pub fn inverse<CS: ConstraintSystem<BaseField>>(&self, cs: &mut CS) -> Result<Self, SynthesisError> {
let inverse = Self::alloc(&mut cs.ns(|| "alloc"), || {
Ok(self.value()?.inverse().unwrap_or_else(TargetField::zero))
})?;
let one = &Self::one(&mut cs.ns(|| "one"))?;
let actual_result = self.mul(&mut cs.ns(|| "mul"), &inverse)?;
actual_result.conditional_enforce_equal(
&mut cs.ns(|| "conditional_enforce_equal"),
&one,
&Boolean::Constant(true),
)?;
Ok(inverse)
}
pub fn get_limbs_representations(
elem: &TargetField,
optimization_type: OptimizationType,
) -> Result<Vec<BaseField>, SynthesisError> {
Self::get_limbs_representations_from_big_integer(&elem.into_repr(), optimization_type)
}
pub fn get_limbs_representations_from_big_integer(
elem: &<TargetField as PrimeField>::BigInteger,
optimization_type: OptimizationType,
) -> Result<Vec<BaseField>, SynthesisError> {
let params = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
optimization_type,
);
let mut limbs: Vec<BaseField> = Vec::new();
let mut cur = *elem;
for _ in 0..params.num_limbs {
let cur_bits = cur.to_bits_be(); let cur_mod_r = <BaseField as PrimeField>::BigInteger::from_bits_be(
cur_bits[cur_bits.len() - params.bits_per_limb..].to_vec(),
); limbs.push(BaseField::from_repr(cur_mod_r).unwrap());
cur.divn(params.bits_per_limb as u32);
}
limbs.reverse();
Ok(limbs)
}
pub fn mul_without_reduce<CS: ConstraintSystem<BaseField>>(
&self,
cs: &mut CS,
other: &Self,
) -> Result<AllocatedNonNativeFieldMulResultVar<TargetField, BaseField>, SynthesisError> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let params = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
self.get_optimization_type(),
);
let mut self_reduced = self.clone();
let mut other_reduced = other.clone();
Reducer::<TargetField, BaseField>::pre_mul_reduce(
&mut cs.ns(|| "pre_mul_reduce"),
&mut self_reduced,
&mut other_reduced,
)?;
let mut prod_limbs = Vec::new();
for z_index in 0..2 * params.num_limbs - 1 {
prod_limbs.push(FpGadget::alloc(cs.ns(|| format!("limb product_{}", z_index)), || {
let mut z_i = BaseField::zero();
for i in 0..=min(params.num_limbs - 1, z_index) {
let j = z_index - i;
if j < params.num_limbs {
z_i += &self_reduced.limbs[i]
.get_value()
.unwrap()
.mul(&other_reduced.limbs[j].get_value().unwrap());
}
}
Ok(z_i)
})?);
}
for c in 0..(2 * params.num_limbs - 1) {
let c_pows: Vec<_> = (0..(2 * params.num_limbs - 1))
.map(|i| BaseField::from((c + 1) as u128).pow(&vec![i as u64]))
.collect();
let mut x = FpGadget::zero(&mut cs.ns(|| format!("zero_x_{}", c)))?;
let mut y = FpGadget::zero(&mut cs.ns(|| format!("zero_y_{}", c)))?;
let mut z = FpGadget::zero(&mut cs.ns(|| format!("zero_z_{}", c)))?;
for (i, (var, c_pow)) in self_reduced.limbs.iter().zip(c_pows.iter()).enumerate() {
let temp =
var.mul_by_constant(&mut cs.ns(|| format!("var_mul_by_constant_c_pow_x_{}_{}", c, i)), c_pow)?;
x = x.add(&mut cs.ns(|| format!("sum_add_x_{}_{}", c, i)), &temp)?
}
for (i, (var, c_pow)) in other_reduced.limbs.iter().zip(c_pows.iter()).enumerate() {
let temp =
var.mul_by_constant(&mut cs.ns(|| format!("var_mul_by_constant_c_pow_y_{}_{}", c, i)), c_pow)?;
y = y.add(&mut cs.ns(|| format!("sum_add_y_{}_ {}", c, i)), &temp)?
}
for (i, (var, c_pow)) in prod_limbs.iter().zip(c_pows.iter()).enumerate() {
let temp =
var.mul_by_constant(&mut cs.ns(|| format!("var_mul_by_constant_c_pow_z_{}_{}", c, i)), c_pow)?;
z = z.add(&mut cs.ns(|| format!("sum_add_z_{}_{}", c, i)), &temp)?
}
let x_mul_y = x.mul(cs.ns(|| format!("x_mul_y_{}", c)), &y)?;
z.enforce_equal(cs.ns(|| format!("enforce_equal_{}", c)), &x_mul_y)?;
}
Ok(AllocatedNonNativeFieldMulResultVar {
limbs: prod_limbs,
prod_of_num_of_additions: (self_reduced.num_of_additions_over_normal_form + &BaseField::one())
* &(other_reduced.num_of_additions_over_normal_form + &BaseField::one()),
target_phantom: PhantomData,
})
}
pub(crate) fn frobenius_map(&self, _power: usize) -> Self {
self.clone()
}
pub(crate) fn conditional_enforce_equal<CS: ConstraintSystem<BaseField>>(
&self,
cs: &mut CS,
other: &Self,
should_enforce: &Boolean,
) -> Result<(), SynthesisError> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let field_parameters = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
self.get_optimization_type(),
);
let p_representations =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations_from_big_integer(
&<TargetField as PrimeField>::Parameters::MODULUS,
self.get_optimization_type(),
)?;
let p_bigint = limbs_to_bigint(field_parameters.bits_per_limb, &p_representations);
let mut p_gadget_limbs = Vec::new();
for limb in p_representations.iter() {
p_gadget_limbs.push(FpGadget::<BaseField>::Constant(*limb));
}
let p_gadget = AllocatedNonNativeFieldVar::<TargetField, BaseField> {
limbs: p_gadget_limbs,
num_of_additions_over_normal_form: BaseField::one(),
is_in_the_normal_form: false,
target_phantom: PhantomData,
};
let zero = Self::zero(&mut cs.ns(|| "zero"))?;
let mut delta = self.sub_without_reduce(&mut cs.ns(|| "sub_without_reduce"), other)?;
delta = Self::conditionally_select(&mut cs.ns(|| "cond_select"), should_enforce, &delta, &zero)?;
let k_gadget = FpGadget::<BaseField>::alloc(cs.ns(|| "k"), || {
let mut delta_limbs_values = Vec::<BaseField>::new();
for limb in delta.limbs.iter() {
delta_limbs_values.push(limb.get_value().get()?);
}
let delta_bigint = limbs_to_bigint(field_parameters.bits_per_limb, &delta_limbs_values);
Ok(bigint_to_basefield::<BaseField>(&(delta_bigint / p_bigint)))
})?;
let surfeit = overhead!(delta.num_of_additions_over_normal_form + &BaseField::one()) + 1;
Reducer::<TargetField, BaseField>::limb_to_bits_be(&mut cs.ns(|| "limb_to_bits"), &k_gadget, surfeit)?;
let mut kp_gadget_limbs = Vec::new();
for (i, limb) in p_gadget.limbs.iter().enumerate() {
kp_gadget_limbs.push(limb.mul(cs.ns(|| format!("mul_limb_{}", i)), &k_gadget)?);
}
Reducer::<TargetField, BaseField>::group_and_check_equality(
cs,
surfeit,
field_parameters.bits_per_limb,
field_parameters.bits_per_limb,
&delta.limbs,
&kp_gadget_limbs,
)?;
Ok(())
}
pub(crate) fn conditional_enforce_not_equal<CS: ConstraintSystem<BaseField>>(
&self,
cs: &mut CS,
other: &Self,
should_enforce: &Boolean,
) -> Result<(), SynthesisError> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let one = Self::one(&mut cs.ns(|| "one"))?;
let subbed = &self.sub(&mut cs.ns(|| "sub"), other)?;
let selected = Self::conditionally_select(cs.ns(|| "conditionally_select"), should_enforce, subbed, &one)?;
selected.inverse(&mut cs.ns(|| "inverse"))?;
Ok(())
}
pub(crate) fn get_optimization_type(&self) -> OptimizationType {
OptimizationType::Weight
}
}
impl<TargetField: PrimeField, BaseField: PrimeField> ToBitsBEGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
{
fn to_bits_be<CS: ConstraintSystem<BaseField>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
let field_parameters = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
self.get_optimization_type(),
);
let mut self_normal = self.clone();
Reducer::<TargetField, BaseField>::pre_eq_reduce(&mut cs.ns(|| "pre_eq_reduce"), &mut self_normal)?;
let mut bits = Vec::<Boolean>::new();
for (i, limb) in self_normal.limbs.iter().enumerate() {
bits.extend_from_slice(&Reducer::<TargetField, BaseField>::limb_to_bits_be(
&mut cs.ns(|| format!("limb_to_bits_{}", i)),
&limb,
field_parameters.bits_per_limb,
)?);
}
let mut b = TargetField::characteristic().to_vec();
assert_eq!(b[0] % 2, 1);
b[0] -= 1; let run = Boolean::enforce_smaller_or_equal_than_be(cs.ns(|| "enforce_smaller_or_equal_than_be"), &bits, b)?;
assert!(run.is_empty());
Ok(bits)
}
fn to_bits_be_strict<CS: ConstraintSystem<BaseField>>(&self, cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
self.to_bits_be(cs)
}
}
impl<TargetField: PrimeField, BaseField: PrimeField> ToBitsLEGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
{
fn to_bits_le<CS: ConstraintSystem<BaseField>>(&self, mut cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
let field_parameters = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
self.get_optimization_type(),
);
let mut self_normal = self.clone();
Reducer::<TargetField, BaseField>::pre_eq_reduce(&mut cs.ns(|| "pre_eq_reduce"), &mut self_normal)?;
let mut bits = Vec::<Boolean>::new();
for (i, limb) in self_normal.limbs.iter().enumerate() {
bits.extend_from_slice(&Reducer::<TargetField, BaseField>::limb_to_bits_be(
&mut cs.ns(|| format!("limb_to_bits_{}", i)),
&limb,
field_parameters.bits_per_limb,
)?);
}
bits.reverse();
let mut b = TargetField::characteristic().to_vec();
assert_eq!(b[0] % 2, 1);
b[0] -= 1; let run = Boolean::enforce_smaller_or_equal_than_le(cs.ns(|| "enforce_smaller_or_equal_than_le"), &bits, b)?;
assert!(run.is_empty());
Ok(bits)
}
fn to_bits_le_strict<CS: ConstraintSystem<BaseField>>(&self, cs: CS) -> Result<Vec<Boolean>, SynthesisError> {
self.to_bits_le(cs)
}
}
impl<TargetField: PrimeField, BaseField: PrimeField> ToBytesGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
{
fn to_bytes<CS: ConstraintSystem<BaseField>>(&self, cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
let bits = self.to_bits_le(cs)?;
let mut bytes = Vec::<UInt8>::new();
bits.chunks(8).for_each(|bits_per_byte| {
let mut bits_per_byte: Vec<Boolean> = bits_per_byte.to_vec();
if bits_per_byte.len() < 8 {
bits_per_byte.resize_with(8, || Boolean::constant(false));
}
bytes.push(UInt8::from_bits_le(&bits_per_byte));
});
Ok(bytes)
}
fn to_bytes_strict<CS: ConstraintSystem<BaseField>>(&self, cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
self.to_bytes(cs)
}
}
impl<TargetField: PrimeField, BaseField: PrimeField> CondSelectGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
{
fn conditionally_select<CS: ConstraintSystem<BaseField>>(
mut cs: CS,
cond: &Boolean,
first: &Self,
second: &Self,
) -> Result<Self, SynthesisError> {
assert_eq!(first.get_optimization_type(), second.get_optimization_type());
let mut limbs_sel = Vec::with_capacity(first.limbs.len());
for (i, (x, y)) in first.limbs.iter().zip(&second.limbs).enumerate() {
limbs_sel.push(FpGadget::<BaseField>::conditionally_select(
cs.ns(|| format!("cond_select_{}", i)),
cond,
x,
y,
)?);
}
Ok(Self {
limbs: limbs_sel,
num_of_additions_over_normal_form: max(
first.num_of_additions_over_normal_form,
second.num_of_additions_over_normal_form,
),
is_in_the_normal_form: first.is_in_the_normal_form && second.is_in_the_normal_form,
target_phantom: PhantomData,
})
}
fn cost() -> usize {
1
}
}
impl<TargetField: PrimeField, BaseField: PrimeField> TwoBitLookupGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
{
type TableConstant = TargetField;
fn two_bit_lookup<CS: ConstraintSystem<BaseField>>(
mut cs: CS,
bits: &[Boolean],
constants: &[Self::TableConstant],
) -> Result<Self, SynthesisError> {
debug_assert!(bits.len() == 2);
debug_assert!(constants.len() == 4);
let optimization_type = OptimizationType::Weight;
let field_parameters = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
optimization_type,
);
let mut limbs_constants = Vec::new();
for _ in 0..field_parameters.num_limbs {
limbs_constants.push(Vec::new());
}
for constant in constants.iter() {
let representations = AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations(
constant,
optimization_type,
)?;
for (i, representation) in representations.iter().enumerate() {
limbs_constants[i].push(*representation);
}
}
let mut limbs = Vec::new();
for (i, limbs_constant) in limbs_constants.iter().enumerate() {
limbs.push(FpGadget::<BaseField>::two_bit_lookup(
&mut cs.ns(|| format!("two_bit_lookup_{}", i)),
bits,
limbs_constant,
)?);
}
Ok(AllocatedNonNativeFieldVar::<TargetField, BaseField> {
limbs,
num_of_additions_over_normal_form: BaseField::zero(),
is_in_the_normal_form: true,
target_phantom: PhantomData,
})
}
fn cost() -> usize {
unimplemented!()
}
}
impl<TargetField: PrimeField, BaseField: PrimeField> ThreeBitCondNegLookupGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
{
type TableConstant = TargetField;
fn three_bit_cond_neg_lookup<CS: ConstraintSystem<BaseField>>(
mut cs: CS,
bits: &[Boolean],
b0b1: &Boolean,
constants: &[Self::TableConstant],
) -> Result<Self, SynthesisError> {
debug_assert!(bits.len() == 3);
debug_assert!(constants.len() == 4);
let optimization_type = OptimizationType::Weight;
let field_parameters = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
optimization_type,
);
let mut limbs_constants = Vec::new();
for _ in 0..field_parameters.num_limbs {
limbs_constants.push(Vec::new());
}
for constant in constants.iter() {
let representations = AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations(
constant,
optimization_type,
)?;
for (i, representation) in representations.iter().enumerate() {
limbs_constants[i].push(*representation);
}
}
let mut limbs = Vec::new();
for (i, limbs_constant) in limbs_constants.iter().enumerate() {
limbs.push(FpGadget::<BaseField>::three_bit_cond_neg_lookup(
cs.ns(|| format!("three_bit_cond_neg_lookup_{}", i)),
bits,
b0b1,
limbs_constant,
)?);
}
Ok(AllocatedNonNativeFieldVar::<TargetField, BaseField> {
limbs,
num_of_additions_over_normal_form: BaseField::zero(),
is_in_the_normal_form: true,
target_phantom: PhantomData,
})
}
fn cost() -> usize {
unimplemented!()
}
}
impl<TargetField: PrimeField, BaseField: PrimeField> AllocGadget<TargetField, BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
{
#[inline]
fn alloc_constant<FN, T, CS: ConstraintSystem<BaseField>>(mut cs: CS, value_gen: FN) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TargetField>,
{
let optimization_type = OptimizationType::Weight;
let zero = TargetField::zero();
let elem = match value_gen() {
Ok(t) => *(t.borrow()),
Err(_) => zero,
};
let elem_representations = Self::get_limbs_representations(&elem, optimization_type)?;
let mut limbs = Vec::new();
for (i, limb) in elem_representations.iter().enumerate() {
limbs.push(FpGadget::<BaseField>::alloc_constant(
cs.ns(|| format!("alloc_constant_limb_{}", i)),
|| Ok(limb),
)?);
}
let num_of_additions_over_normal_form = BaseField::zero();
Ok(Self {
limbs,
num_of_additions_over_normal_form,
is_in_the_normal_form: true,
target_phantom: PhantomData,
})
}
#[inline]
fn alloc<FN, T, CS: ConstraintSystem<BaseField>>(mut cs: CS, value_gen: FN) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TargetField>,
{
let optimization_type = OptimizationType::Weight;
let zero = TargetField::zero();
let elem = match value_gen() {
Ok(t) => *(t.borrow()),
Err(_) => zero,
};
let elem_representations = Self::get_limbs_representations(&elem, optimization_type)?;
let mut limbs = Vec::new();
for (i, limb) in elem_representations.iter().enumerate() {
limbs.push(FpGadget::<BaseField>::alloc(cs.ns(|| format!("alloc_{}", i)), || {
Ok(limb)
})?);
}
let num_of_additions_over_normal_form = BaseField::zero();
Ok(Self {
limbs,
num_of_additions_over_normal_form,
is_in_the_normal_form: true,
target_phantom: PhantomData,
})
}
#[inline]
fn alloc_input<FN, T, CS: ConstraintSystem<BaseField>>(mut cs: CS, value_gen: FN) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TargetField>,
{
let optimization_type = OptimizationType::Weight;
let params = get_params(
TargetField::size_in_bits(),
BaseField::size_in_bits(),
optimization_type,
);
let zero = TargetField::zero();
let elem = match value_gen() {
Ok(t) => *(t.borrow()),
Err(_) => zero,
};
let elem_representations = Self::get_limbs_representations(&elem, optimization_type)?;
let mut limbs = Vec::new();
for (i, limb) in elem_representations.iter().enumerate() {
limbs.push(FpGadget::<BaseField>::alloc_input(
cs.ns(|| format!("alloc_input_{}", i)),
|| Ok(limb),
)?);
}
let num_of_additions_over_normal_form = BaseField::one();
for (i, limb) in limbs.iter().rev().take(params.num_limbs - 1).enumerate() {
Reducer::<TargetField, BaseField>::limb_to_bits_be(
&mut cs.ns(|| format!("limb_to_bits_{}", i)),
limb,
params.bits_per_limb,
)?;
}
Reducer::<TargetField, BaseField>::limb_to_bits_be(
&mut cs,
&limbs[0],
TargetField::size_in_bits() - (params.num_limbs - 1) * params.bits_per_limb,
)?;
Ok(Self {
limbs,
num_of_additions_over_normal_form,
is_in_the_normal_form: false,
target_phantom: PhantomData,
})
}
}
impl<TargetField: PrimeField, BaseField: PrimeField> Clone for AllocatedNonNativeFieldVar<TargetField, BaseField> {
fn clone(&self) -> Self {
AllocatedNonNativeFieldVar {
limbs: self.limbs.clone(),
num_of_additions_over_normal_form: self.num_of_additions_over_normal_form,
is_in_the_normal_form: self.is_in_the_normal_form,
target_phantom: PhantomData,
}
}
}