chore: implement Gt and GtEq as syntactic sugar (#216)
* Implement Gt and GtEq as syntactic sugar * Update src/backends/plonky2/circuits/mainpod.rs Co-authored-by: Eduard S. <eduardsanou@posteo.net> * Op verification circuit refactor * Code review * Add range check to Eq case of LtEq * Style * Factor out ValueOf statement argument type checks * Formatting * Clean-up * Safety * Take sign into account * Simplify sign check --------- Co-authored-by: Eduard S. <eduardsanou@posteo.net>
This commit is contained in:
parent
e420aa7b32
commit
53ade6ea26
6 changed files with 451 additions and 170 deletions
|
|
@ -30,6 +30,7 @@ use crate::{
|
|||
};
|
||||
|
||||
pub const CODE_SIZE: usize = HASH_SIZE + 2;
|
||||
const NUM_BITS: usize = 32;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ValueTarget {
|
||||
|
|
@ -102,6 +103,13 @@ impl StatementArgTarget {
|
|||
) -> Self {
|
||||
Self::new(*pod_id, *key)
|
||||
}
|
||||
|
||||
/// StatementArgTarget to ValueTarget coercion. Make sure to check
|
||||
/// that the arg is a value using the `statement_arg_is_value` method
|
||||
/// first!
|
||||
pub fn as_value(&self) -> ValueTarget {
|
||||
ValueTarget::from_slice(&self.elements[..VALUE_SIZE])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
@ -300,9 +308,17 @@ pub trait CircuitBuilderPod<F: RichField + Extendable<D>, const D: usize> {
|
|||
// Convenience methods for checking values.
|
||||
/// Checks whether `xs` is right-padded with 0s so as to represent a `Value`.
|
||||
fn statement_arg_is_value(&mut self, arg: &StatementArgTarget) -> BoolTarget;
|
||||
/// Checks whether `x < y` if `b` is true. This involves checking
|
||||
/// that `x` and `y` each consist of two `u32` limbs.
|
||||
fn assert_less_if(&mut self, b: BoolTarget, x: ValueTarget, y: ValueTarget);
|
||||
|
||||
/// Checks whether `x` is an i64, which involves checking that it
|
||||
/// consists of two `u32` limbs.
|
||||
fn assert_i64(&mut self, x: ValueTarget);
|
||||
|
||||
/// Checks whether an i64 is negative.
|
||||
fn i64_is_negative(&mut self, x: ValueTarget) -> BoolTarget;
|
||||
|
||||
/// Checks whether `x < y` if `b` is true. This assumes that `x`
|
||||
/// and `y` each consist of two `u32` limbs.
|
||||
fn assert_i64_less_if(&mut self, b: BoolTarget, x: ValueTarget, y: ValueTarget);
|
||||
|
||||
// Convenience methods for accessing and connecting elements of
|
||||
// (vectors of) flattenables.
|
||||
|
|
@ -389,14 +405,34 @@ impl CircuitBuilderPod<F, D> for CircuitBuilder<F, D> {
|
|||
self.is_equal_slice(&arg.elements[VALUE_SIZE..], &zeros)
|
||||
}
|
||||
|
||||
fn assert_less_if(&mut self, b: BoolTarget, x: ValueTarget, y: ValueTarget) {
|
||||
const NUM_BITS: usize = 32;
|
||||
fn assert_i64(&mut self, x: ValueTarget) {
|
||||
// `x` should only have two limbs.
|
||||
x.elements
|
||||
.into_iter()
|
||||
.skip(2)
|
||||
.for_each(|l| self.assert_zero(l));
|
||||
|
||||
// Lt assertion with 32-bit range check.
|
||||
// 32-bit range check.
|
||||
self.range_check(x.elements[0], NUM_BITS);
|
||||
self.range_check(x.elements[1], NUM_BITS);
|
||||
}
|
||||
|
||||
fn i64_is_negative(&mut self, x: ValueTarget) -> BoolTarget {
|
||||
// x is negative if the most significant bit of its most
|
||||
// significant limb is 1.
|
||||
let high_bits = self.split_le(x.elements[1], NUM_BITS);
|
||||
high_bits[31]
|
||||
}
|
||||
|
||||
fn assert_i64_less_if(&mut self, b: BoolTarget, x: ValueTarget, y: ValueTarget) {
|
||||
// If b is false, replace `x` and `y` with dummy values.
|
||||
let zero = ValueTarget::zero(self);
|
||||
let one = ValueTarget::one(self);
|
||||
let x = self.select_value(b, x, zero);
|
||||
let y = self.select_value(b, y, one);
|
||||
|
||||
// Lt assertion.
|
||||
let assert_limb_lt = |builder: &mut Self, x, y| {
|
||||
// Check that targets fit within `NUM_BITS` bits.
|
||||
builder.range_check(x, NUM_BITS);
|
||||
builder.range_check(y, NUM_BITS);
|
||||
// Check that `y-1-x` fits within `NUM_BITS` bits.
|
||||
let one = builder.one();
|
||||
let y_minus_one = builder.sub(y, one);
|
||||
|
|
@ -404,21 +440,14 @@ impl CircuitBuilderPod<F, D> for CircuitBuilder<F, D> {
|
|||
builder.range_check(expr, NUM_BITS);
|
||||
};
|
||||
|
||||
// If b is false, replace `x` and `y` with dummy values.
|
||||
let zero = ValueTarget::zero(self);
|
||||
let one = ValueTarget::one(self);
|
||||
let x = self.select_value(b, x, zero);
|
||||
let y = self.select_value(b, y, one);
|
||||
|
||||
// `x` and `y` should only have two limbs each.
|
||||
x.elements
|
||||
.into_iter()
|
||||
.skip(2)
|
||||
.for_each(|l| self.assert_zero(l));
|
||||
y.elements
|
||||
.into_iter()
|
||||
.skip(2)
|
||||
.for_each(|l| self.assert_zero(l));
|
||||
// Check if `x` and `y` have the same sign. If not, swap.
|
||||
let x_is_negative = self.i64_is_negative(x);
|
||||
let y_is_negative = self.i64_is_negative(y);
|
||||
let same_sign_ind = self.is_equal(x_is_negative.target, y_is_negative.target);
|
||||
let (x, y) = (
|
||||
self.select_value(same_sign_ind, x, y),
|
||||
self.select_value(same_sign_ind, y, x),
|
||||
);
|
||||
|
||||
let big_limbs_eq = self.is_equal(x.elements[1], y.elements[1]);
|
||||
let lhs = self.select(big_limbs_eq, x.elements[0], x.elements[1]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue