Browse Source

Refactor variable traits in `r1cs-std`.

master
Pratyush Mishra 4 years ago
parent
commit
202ef3204d
5 changed files with 345 additions and 251 deletions
  1. +52
    -121
      r1cs-std/src/alloc.rs
  2. +86
    -97
      r1cs-std/src/eq.rs
  3. +64
    -16
      r1cs-std/src/lib.rs
  4. +135
    -0
      r1cs-std/src/macros.rs
  5. +8
    -17
      r1cs-std/src/select.rs

+ 52
- 121
r1cs-std/src/alloc.rs

@ -1,141 +1,72 @@
use crate::Vec; use crate::Vec;
use algebra::Field; use algebra::Field;
use core::borrow::Borrow; use core::borrow::Borrow;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::{Namespace, SynthesisError};
pub trait AllocGadget<V, ConstraintF: Field>
where
Self: Sized,
V: ?Sized,
{
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
t: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<V>;
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(cs: CS, f: F) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<V>;
fn alloc_checked<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<V>,
{
Self::alloc(cs, f)
}
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<V>;
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)]
pub enum AllocationMode {
Constant = 0,
Input = 1,
Witness = 2,
}
fn alloc_input_checked<F, T, CS: ConstraintSystem<ConstraintF>>(
cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<V>,
{
Self::alloc_input(cs, f)
impl AllocationMode {
// Outputs the maximum according to the relation `Constant < Input < Witness`.
pub fn max(&self, other: Self) -> Self {
use AllocationMode::*;
match (self, other) {
(Constant, _) => other,
(Input, Constant) => *self,
(Input, _) => other,
(Witness, _) => *self,
}
} }
} }
impl<I, ConstraintF: Field, A: AllocGadget<I, ConstraintF>> AllocGadget<[I], ConstraintF>
for Vec<A>
pub trait AllocVar<V, F: Field>
where
Self: Sized,
V: ?Sized,
{ {
#[inline]
fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
t: T,
) -> Result<Self, SynthesisError>
where
T: Borrow<[I]>,
{
let mut vec = Vec::new();
for (i, value) in t.borrow().iter().enumerate() {
vec.push(A::alloc_constant(cs.ns(|| format!("value_{}", i)), value)?);
}
Ok(vec)
}
fn new_variable<T: Borrow<V>>(
cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode,
) -> Result<Self, SynthesisError>;
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[I]>,
{
let mut vec = Vec::new();
for (i, value) in f()?.borrow().iter().enumerate() {
vec.push(A::alloc(&mut cs.ns(|| format!("value_{}", i)), || {
Ok(value)
})?);
}
Ok(vec)
fn new_constant(
cs: impl Into<Namespace<F>>,
t: impl Borrow<V>,
) -> Result<Self, SynthesisError> {
Self::new_variable(cs, || Ok(t), AllocationMode::Constant)
} }
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[I]>,
{
let mut vec = Vec::new();
for (i, value) in f()?.borrow().iter().enumerate() {
vec.push(A::alloc_input(
&mut cs.ns(|| format!("value_{}", i)),
|| Ok(value),
)?);
}
Ok(vec)
fn new_input<T: Borrow<V>>(
cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>,
) -> Result<Self, SynthesisError> {
Self::new_variable(cs, f, AllocationMode::Input)
} }
fn alloc_checked<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[I]>,
{
let mut vec = Vec::new();
for (i, value) in f()?.borrow().iter().enumerate() {
vec.push(A::alloc_checked(
&mut cs.ns(|| format!("value_{}", i)),
|| Ok(value),
)?);
}
Ok(vec)
fn new_witness<T: Borrow<V>>(
cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>,
) -> Result<Self, SynthesisError> {
Self::new_variable(cs, f, AllocationMode::Witness)
} }
}
fn alloc_input_checked<F, T, CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
f: F,
) -> Result<Self, SynthesisError>
where
F: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<[I]>,
{
impl<I, F: Field, A: AllocVar<I, F>> AllocVar<[I], F> for Vec<A> {
fn new_variable<T: Borrow<[I]>>(
cs: impl Into<Namespace<F>>,
f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode,
) -> Result<Self, SynthesisError> {
let ns = cs.into();
let cs = ns.cs();
let mut vec = Vec::new(); let mut vec = Vec::new();
for (i, value) in f()?.borrow().iter().enumerate() {
vec.push(A::alloc_input_checked(
&mut cs.ns(|| format!("value_{}", i)),
|| Ok(value),
)?);
for value in f()?.borrow().iter() {
vec.push(A::new_variable(cs.clone(), || Ok(value), mode)?);
} }
Ok(vec) Ok(vec)
} }

+ 86
- 97
r1cs-std/src/eq.rs

@ -1,142 +1,131 @@
use crate::prelude::*;
use crate::{prelude::*, Vec};
use algebra::Field; use algebra::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::SynthesisError;
/// If `condition == 1`, then enforces that `self` and `other` are equal;
/// otherwise, it doesn't enforce anything.
pub trait ConditionalEqGadget<ConstraintF: Field>: Eq {
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
&self,
cs: CS,
other: &Self,
condition: &Boolean,
) -> Result<(), SynthesisError>;
pub trait EqGadget<F: Field> {
/// Output a `Boolean` value representing whether `self.value() == other.value()`.
fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError>;
fn cost() -> usize;
}
impl<T: ConditionalEqGadget<ConstraintF>, ConstraintF: Field> ConditionalEqGadget<ConstraintF>
for [T]
{
fn conditional_enforce_equal<CS: ConstraintSystem<ConstraintF>>(
/// Output a `Boolean` value representing whether `self.value() != other.value()`.
fn is_neq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
Ok(self.is_eq(other)?.not())
}
/// If `should_enforce == true`, enforce that `self` and `other` are equal; else,
/// enforce a vacuously true statement.
fn conditional_enforce_equal(
&self, &self,
mut cs: CS,
other: &Self, other: &Self,
condition: &Boolean,
should_enforce: &Boolean<F>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
for (i, (a, b)) in self.iter().zip(other.iter()).enumerate() {
let mut cs = cs.ns(|| format!("Iteration {}", i));
a.conditional_enforce_equal(&mut cs, b, condition)?;
}
Ok(())
self.is_eq(&other)?
.conditional_enforce_equal(&Boolean::constant(true), should_enforce)
} }
fn cost() -> usize {
unimplemented!()
/// Enforce that `self` and `other` are equal.
fn enforce_equal(&self, other: &Self) -> Result<(), SynthesisError> {
self.conditional_enforce_equal(other, &Boolean::constant(true))
} }
}
pub trait EqGadget<ConstraintF: Field>: Eq
where
Self: ConditionalEqGadget<ConstraintF>,
{
fn enforce_equal<CS: ConstraintSystem<ConstraintF>>(
/// If `should_enforce == true`, enforce that `self` and `other` are not equal; else,
/// enforce a vacuously true statement.
fn conditional_enforce_not_equal(
&self, &self,
cs: CS,
other: &Self, other: &Self,
should_enforce: &Boolean<F>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
self.conditional_enforce_equal(cs, other, &Boolean::constant(true))
self.is_neq(&other)?
.conditional_enforce_equal(&Boolean::constant(true), should_enforce)
} }
fn cost() -> usize {
<Self as ConditionalEqGadget<ConstraintF>>::cost()
/// Enforce that `self` and `other` are not equal.
fn enforce_not_equal(&self, other: &Self) -> Result<(), SynthesisError> {
self.conditional_enforce_not_equal(other, &Boolean::constant(true))
} }
} }
impl<T: EqGadget<ConstraintF>, ConstraintF: Field> EqGadget<ConstraintF> for [T] {}
impl<T: EqGadget<F> + R1CSVar<F>, F: Field> EqGadget<F> for [T] {
fn is_eq(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
assert_eq!(self.len(), other.len());
assert!(!self.is_empty());
let mut results = Vec::with_capacity(self.len());
for (a, b) in self.iter().zip(other) {
results.push(a.is_eq(b)?);
}
Boolean::kary_and(&results)
}
pub trait NEqGadget<ConstraintF: Field>: Eq {
fn enforce_not_equal<CS: ConstraintSystem<ConstraintF>>(
fn conditional_enforce_equal(
&self, &self,
cs: CS,
other: &Self, other: &Self,
) -> Result<(), SynthesisError>;
condition: &Boolean<F>,
) -> Result<(), SynthesisError> {
assert_eq!(self.len(), other.len());
for (a, b) in self.iter().zip(other) {
a.conditional_enforce_equal(b, condition)?;
}
Ok(())
}
fn cost() -> usize;
fn conditional_enforce_not_equal(
&self,
other: &Self,
should_enforce: &Boolean<F>,
) -> Result<(), SynthesisError> {
assert_eq!(self.len(), other.len());
let some_are_different = self.is_neq(other)?;
if let Some(cs) = some_are_different.cs().or(should_enforce.cs()) {
cs.enforce_constraint(
some_are_different.lc(),
should_enforce.lc(),
should_enforce.lc(),
)
} else {
// `some_are_different` and `should_enforce` are both constants
assert!(some_are_different.value().unwrap());
Ok(())
}
}
} }
pub trait OrEqualsGadget<ConstraintF: Field> pub trait OrEqualsGadget<ConstraintF: Field>
where where
Self: Sized, Self: Sized,
{ {
fn enforce_equal_or<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
cond: &Boolean,
var: &Self,
/// If `should_enforce == true`, enforce that `self` equals
/// (a) `first` (if `cond` is `true`)
/// (b) `second` (if `cond` is `false`)
fn conditional_enforce_equal_or(
&self,
cond: &Boolean<ConstraintF>,
first: &Self, first: &Self,
second: &Self, second: &Self,
should_enforce: &Boolean<ConstraintF>,
) -> Result<(), SynthesisError>; ) -> Result<(), SynthesisError>;
fn cost() -> usize;
}
impl<ConstraintF: Field, T: Sized + ConditionalOrEqualsGadget<ConstraintF>>
OrEqualsGadget<ConstraintF> for T
{
fn enforce_equal_or<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
cond: &Boolean,
var: &Self,
fn enforce_equal_or(
&self,
cond: &Boolean<ConstraintF>,
first: &Self, first: &Self,
second: &Self, second: &Self,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
Self::conditional_enforce_equal_or(cs, cond, var, first, second, &Boolean::Constant(true))
}
fn cost() -> usize {
<Self as ConditionalOrEqualsGadget<ConstraintF>>::cost()
self.conditional_enforce_equal_or(cond, first, second, &Boolean::Constant(true))
} }
} }
pub trait ConditionalOrEqualsGadget<ConstraintF: Field>
impl<ConstraintF, T> OrEqualsGadget<ConstraintF> for T
where where
Self: Sized,
ConstraintF: Field,
T: Sized + EqGadget<ConstraintF> + CondSelectGadget<ConstraintF>,
{ {
fn conditional_enforce_equal_or<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
cond: &Boolean,
var: &Self,
first: &Self,
second: &Self,
should_enforce: &Boolean,
) -> Result<(), SynthesisError>;
fn cost() -> usize;
}
impl<
ConstraintF: Field,
T: Sized + ConditionalEqGadget<ConstraintF> + CondSelectGadget<ConstraintF>,
> ConditionalOrEqualsGadget<ConstraintF> for T
{
fn conditional_enforce_equal_or<CS: ConstraintSystem<ConstraintF>>(
mut cs: CS,
cond: &Boolean,
var: &Self,
fn conditional_enforce_equal_or(
&self,
cond: &Boolean<ConstraintF>,
first: &Self, first: &Self,
second: &Self, second: &Self,
should_enforce: &Boolean,
should_enforce: &Boolean<ConstraintF>,
) -> Result<(), SynthesisError> { ) -> Result<(), SynthesisError> {
let match_opt = Self::conditionally_select(
&mut cs.ns(|| "conditional_select_in_or"),
cond,
first,
second,
)?;
var.conditional_enforce_equal(&mut cs.ns(|| "equals_in_or"), &match_opt, should_enforce)
}
fn cost() -> usize {
<Self as ConditionalEqGadget<ConstraintF>>::cost()
+ <Self as CondSelectGadget<ConstraintF>>::cost()
let match_opt = cond.select(first, second)?;
self.conditional_enforce_equal(&match_opt, should_enforce)
} }
} }

+ 64
- 16
r1cs-std/src/lib.rs

@ -20,21 +20,16 @@ extern crate algebra;
#[macro_use] #[macro_use]
extern crate derivative; extern crate derivative;
/// used by test_constraint_system
#[cfg(not(feature = "std"))]
macro_rules! println {
() => {};
($($arg: tt)*) => {};
}
#[macro_use]
pub mod macros;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use ralloc::{collections::BTreeMap, string::String, vec::Vec};
use ralloc::vec::Vec;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::{collections::BTreeMap, string::String, vec::Vec};
use std::vec::Vec;
pub mod test_constraint_counter;
pub mod test_constraint_system;
use algebra::prelude::Field;
pub mod bits; pub mod bits;
pub use self::bits::*; pub use self::bits::*;
@ -48,6 +43,9 @@ mod instantiated;
#[cfg(feature = "bls12_377")] #[cfg(feature = "bls12_377")]
pub use instantiated::bls12_377; pub use instantiated::bls12_377;
#[cfg(feature = "ed_on_bn254")]
pub use instantiated::ed_on_bn254;
#[cfg(feature = "ed_on_bls12_377")] #[cfg(feature = "ed_on_bls12_377")]
pub use instantiated::ed_on_bls12_377; pub use instantiated::ed_on_bls12_377;
@ -60,8 +58,8 @@ pub use instantiated::ed_on_mnt4_753;
#[cfg(feature = "ed_on_cp6_782")] #[cfg(feature = "ed_on_cp6_782")]
pub use instantiated::ed_on_cp6_782; pub use instantiated::ed_on_cp6_782;
#[cfg(feature = "ed_on_bn254")]
pub use instantiated::ed_on_bn254;
#[cfg(feature = "ed_on_bw6_761")]
pub use instantiated::ed_on_bw6_761;
#[cfg(feature = "ed_on_bls12_381")] #[cfg(feature = "ed_on_bls12_381")]
pub use instantiated::ed_on_bls12_381; pub use instantiated::ed_on_bls12_381;
@ -89,20 +87,70 @@ pub mod prelude {
alloc::*, alloc::*,
bits::{boolean::Boolean, uint32::UInt32, uint8::UInt8, ToBitsGadget, ToBytesGadget}, bits::{boolean::Boolean, uint32::UInt32, uint8::UInt8, ToBitsGadget, ToBytesGadget},
eq::*, eq::*,
fields::{fp::FpGadget, FieldGadget, ToConstraintFieldGadget},
groups::GroupGadget,
fields::{FieldOpsBounds, FieldVar},
groups::{CurveVar, GroupOpsBounds},
instantiated::*, instantiated::*,
pairing::PairingGadget,
pairing::PairingVar,
select::*, select::*,
R1CSVar,
}; };
} }
pub trait R1CSVar<F: Field> {
type Value: core::fmt::Debug + Eq + Clone;
/// Returns the underlying `ConstraintSystemRef`.
fn cs(&self) -> Option<r1cs_core::ConstraintSystemRef<F>>;
/// Returns `true` if `self` is a circuit-generation-time constant.
fn is_constant(&self) -> bool {
self.cs()
.map_or(true, |cs| cs == r1cs_core::ConstraintSystemRef::None)
}
/// Returns the value that is assigned to `self` in the underlying
/// `ConstraintSystem`.
fn value(&self) -> Result<Self::Value, r1cs_core::SynthesisError>;
}
impl<F: Field, T: R1CSVar<F>> R1CSVar<F> for [T] {
type Value = Vec<T::Value>;
fn cs(&self) -> Option<r1cs_core::ConstraintSystemRef<F>> {
let mut result = None;
for var in self {
result = var.cs().or(result);
}
result
}
fn value(&self) -> Result<Self::Value, r1cs_core::SynthesisError> {
let mut result = Vec::new();
for var in self {
result.push(var.value()?);
}
Ok(result)
}
}
impl<'a, F: Field, T: 'a + R1CSVar<F>> R1CSVar<F> for &'a T {
type Value = T::Value;
fn cs(&self) -> Option<r1cs_core::ConstraintSystemRef<F>> {
(*self).cs()
}
fn value(&self) -> Result<Self::Value, r1cs_core::SynthesisError> {
(*self).value()
}
}
pub trait Assignment<T> { pub trait Assignment<T> {
fn get(self) -> Result<T, r1cs_core::SynthesisError>; fn get(self) -> Result<T, r1cs_core::SynthesisError>;
} }
impl<T> Assignment<T> for Option<T> { impl<T> Assignment<T> for Option<T> {
fn get(self) -> Result<T, r1cs_core::SynthesisError> { fn get(self) -> Result<T, r1cs_core::SynthesisError> {
self.ok_or_else(|| r1cs_core::SynthesisError::AssignmentMissing)
self.ok_or(r1cs_core::SynthesisError::AssignmentMissing)
} }
} }

+ 135
- 0
r1cs-std/src/macros.rs

@ -0,0 +1,135 @@
// Implements AddAssign on Self by deferring to an implementation on &Self
#[macro_export]
macro_rules! impl_ops {
(
$type: ty,
$native: ty,
$trait: ident,
$fn: ident,
$assign_trait: ident,
$assign_fn: ident,
$impl: expr,
$constant_impl: expr,
$($args:tt)*
) => {
impl_bounded_ops!($type, $native, $trait, $fn, $assign_trait, $assign_fn, $impl, $constant_impl, ($($args)+), );
};
}
macro_rules! impl_bounded_ops {
(
$type: ty,
$native: ty,
$trait: ident,
$fn: ident,
$assign_trait: ident,
$assign_fn: ident,
$impl: expr,
$constant_impl: expr,
($($params:tt)+),
$($bounds:tt)*
) => {
impl<'a, $($params)+> core::ops::$trait<&'a $type> for &'a $type
where
$($bounds)*
{
type Output = $type;
fn $fn(self, other: Self) -> Self::Output {
$impl(self, other)
}
}
impl<'a, $($params)+> core::ops::$trait<$type> for &'a $type
where
$($bounds)*
{
type Output = $type;
fn $fn(self, other: $type) -> Self::Output {
core::ops::$trait::$fn(self, &other)
}
}
impl<'a, $($params)+> core::ops::$trait<&'a $type> for $type
where
$($bounds)*
{
type Output = $type;
fn $fn(self, other: &'a $type) -> Self::Output {
core::ops::$trait::$fn(&self, other)
}
}
impl<$($params)+> core::ops::$trait<$type> for $type
where
$($bounds)*
{
type Output = $type;
fn $fn(self, other: $type) -> Self::Output {
core::ops::$trait::$fn(&self, &other)
}
}
impl<$($params)+> core::ops::$assign_trait<$type> for $type
where
$($bounds)*
{
fn $assign_fn(&mut self, other: $type) {
let result = core::ops::$trait::$fn(&*self, &other);
*self = result
}
}
impl<'a, $($params)+> core::ops::$assign_trait<&'a $type> for $type
where
$($bounds)*
{
fn $assign_fn(&mut self, other: &'a $type) {
let result = core::ops::$trait::$fn(&*self, other);
*self = result
}
}
impl<'a, $($params)+> core::ops::$trait<$native> for &'a $type
where
$($bounds)*
{
type Output = $type;
fn $fn(self, other: $native) -> Self::Output {
$constant_impl(self, other)
}
}
impl<$($params)+> core::ops::$trait<$native> for $type
where
$($bounds)*
{
type Output = $type;
fn $fn(self, other: $native) -> Self::Output {
core::ops::$trait::$fn(&self, other)
}
}
impl<$($params)+> core::ops::$assign_trait<$native> for $type
where
$($bounds)*
{
fn $assign_fn(&mut self, other: $native) {
let result = core::ops::$trait::$fn(&*self, other);
*self = result
}
}
}
}

+ 8
- 17
r1cs-std/src/select.rs

@ -1,20 +1,17 @@
use crate::prelude::*; use crate::prelude::*;
use algebra::Field; use algebra::Field;
use r1cs_core::{ConstraintSystem, SynthesisError};
use r1cs_core::SynthesisError;
/// If condition is `true`, return `true_value`; else, select `false_value`. /// If condition is `true`, return `true_value`; else, select `false_value`.
pub trait CondSelectGadget<ConstraintF: Field> pub trait CondSelectGadget<ConstraintF: Field>
where where
Self: Sized, Self: Sized,
{ {
fn conditionally_select<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
cond: &Boolean,
fn conditionally_select(
cond: &Boolean<ConstraintF>,
true_value: &Self, true_value: &Self,
false_value: &Self, false_value: &Self,
) -> Result<Self, SynthesisError>; ) -> Result<Self, SynthesisError>;
fn cost() -> usize;
} }
/// Uses two bits to perform a lookup into a table /// Uses two bits to perform a lookup into a table
@ -23,13 +20,10 @@ where
Self: Sized, Self: Sized,
{ {
type TableConstant; type TableConstant;
fn two_bit_lookup<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
bits: &[Boolean],
fn two_bit_lookup(
bits: &[Boolean<ConstraintF>],
constants: &[Self::TableConstant], constants: &[Self::TableConstant],
) -> Result<Self, SynthesisError>; ) -> Result<Self, SynthesisError>;
fn cost() -> usize;
} }
/// Uses three bits to perform a lookup into a table, where the last bit /// Uses three bits to perform a lookup into a table, where the last bit
@ -39,12 +33,9 @@ where
Self: Sized, Self: Sized,
{ {
type TableConstant; type TableConstant;
fn three_bit_cond_neg_lookup<CS: ConstraintSystem<ConstraintF>>(
cs: CS,
bits: &[Boolean],
b0b1: &Boolean,
fn three_bit_cond_neg_lookup(
bits: &[Boolean<ConstraintF>],
b0b1: &Boolean<ConstraintF>,
constants: &[Self::TableConstant], constants: &[Self::TableConstant],
) -> Result<Self, SynthesisError>; ) -> Result<Self, SynthesisError>;
fn cost() -> usize;
} }

Loading…
Cancel
Save