Merge branch 'develop' of github.com:Zokrates/ZoKrates into dummy-contraint
This commit is contained in:
commit
760bd7085a
29 changed files with 455 additions and 30 deletions
|
@ -5,7 +5,7 @@ The following table lists the precedence and associativity of all available oper
|
|||
| Operator | Description | Associativity | Remarks |
|
||||
|------------------------------|--------------------------------------------------------------|------------------------------------|---------|
|
||||
| ** <br> | Power | Left | [^1] |
|
||||
| * <br> /<br> | Multiplication <br> Division <br> | Left <br> Left | |
|
||||
| *<br> /<br> %<br> | Multiplication <br> Division <br> Remainder | Left <br> Left <br>Left | |
|
||||
| + <br> - <br> | Addition <br> Subtraction <br> | Left <br> Left | |
|
||||
| << <br> >> <br> | Left shift <br> Right shift <br> | Left <br> Left | [^2] |
|
||||
| & | Bitwise AND | Left <br> Left | |
|
||||
|
@ -22,4 +22,4 @@ The following table lists the precedence and associativity of all available oper
|
|||
|
||||
[^2]: The right operand must be a compile time constant
|
||||
|
||||
[^3]: Both operands are be asserted to be strictly lower than the biggest power of 2 lower than `p/2`
|
||||
[^3]: Both operands are asserted to be strictly lower than the biggest power of 2 lower than `p/2`
|
|
@ -16,6 +16,8 @@ While `field` values mostly behave like unsigned integers, one should keep in mi
|
|||
{{#include ../../../zokrates_cli/examples/book/field_overflow.zok}}
|
||||
```
|
||||
|
||||
Note that for field elements, the division operation multiplies the numerator with the denominator's inverse field element. The results coincide with integer divisions for cases with remainder 0, but differ otherwise.
|
||||
|
||||
### `bool`
|
||||
|
||||
Booleans are available in ZoKrates. When a boolean is used as a parameter of the main function, the program is constrained to only accept `0` or `1` for that parameter. A boolean can be asserted to be true using an `assert(bool)` statement.
|
||||
|
@ -28,6 +30,8 @@ Internally, they use a binary encoding, which makes them particularly efficient
|
|||
|
||||
Similarly to booleans, unsigned integer inputs of the main function only accept values of the appropriate range.
|
||||
|
||||
The division operation calculates the standard floor division for integers. The `%` operand can be used to obtain the remainder.
|
||||
|
||||
## Complex Types
|
||||
|
||||
ZoKrates provides two complex types: arrays and structs.
|
||||
|
|
3
zokrates_cli/examples/runtime_errors/div_zero_field.zok
Normal file
3
zokrates_cli/examples/runtime_errors/div_zero_field.zok
Normal file
|
@ -0,0 +1,3 @@
|
|||
def main(field x):
|
||||
field y = 1 / x
|
||||
return
|
3
zokrates_cli/examples/runtime_errors/div_zero_uint.zok
Normal file
3
zokrates_cli/examples/runtime_errors/div_zero_uint.zok
Normal file
|
@ -0,0 +1,3 @@
|
|||
def main(u8 x):
|
||||
u8 y = 0x01 / x
|
||||
return
|
|
@ -320,6 +320,10 @@ impl<'ast> From<pest::BinaryExpression<'ast>> for absy::ExpressionNode<'ast> {
|
|||
box absy::ExpressionNode::from(*expression.left),
|
||||
box absy::ExpressionNode::from(*expression.right),
|
||||
),
|
||||
pest::BinaryOperator::Rem => absy::Expression::Rem(
|
||||
box absy::ExpressionNode::from(*expression.left),
|
||||
box absy::ExpressionNode::from(*expression.right),
|
||||
),
|
||||
pest::BinaryOperator::Eq => absy::Expression::Eq(
|
||||
box absy::ExpressionNode::from(*expression.left),
|
||||
box absy::ExpressionNode::from(*expression.right),
|
||||
|
|
|
@ -482,6 +482,7 @@ pub enum Expression<'ast> {
|
|||
Sub(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
Mult(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
Div(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
Rem(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
Pow(Box<ExpressionNode<'ast>>, Box<ExpressionNode<'ast>>),
|
||||
IfElse(
|
||||
Box<ExpressionNode<'ast>>,
|
||||
|
@ -522,6 +523,7 @@ impl<'ast> fmt::Display for Expression<'ast> {
|
|||
Expression::Sub(ref lhs, ref rhs) => write!(f, "({} - {})", lhs, rhs),
|
||||
Expression::Mult(ref lhs, ref rhs) => write!(f, "({} * {})", lhs, rhs),
|
||||
Expression::Div(ref lhs, ref rhs) => write!(f, "({} / {})", lhs, rhs),
|
||||
Expression::Rem(ref lhs, ref rhs) => write!(f, "({} % {})", lhs, rhs),
|
||||
Expression::Pow(ref lhs, ref rhs) => write!(f, "({}**{})", lhs, rhs),
|
||||
Expression::BooleanConstant(b) => write!(f, "{}", b),
|
||||
Expression::IfElse(ref condition, ref consequent, ref alternative) => write!(
|
||||
|
@ -590,6 +592,7 @@ impl<'ast> fmt::Debug for Expression<'ast> {
|
|||
Expression::Sub(ref lhs, ref rhs) => write!(f, "Sub({:?}, {:?})", lhs, rhs),
|
||||
Expression::Mult(ref lhs, ref rhs) => write!(f, "Mult({:?}, {:?})", lhs, rhs),
|
||||
Expression::Div(ref lhs, ref rhs) => write!(f, "Div({:?}, {:?})", lhs, rhs),
|
||||
Expression::Rem(ref lhs, ref rhs) => write!(f, "Rem({:?}, {:?})", lhs, rhs),
|
||||
Expression::Pow(ref lhs, ref rhs) => write!(f, "Pow({:?}, {:?})", lhs, rhs),
|
||||
Expression::BooleanConstant(b) => write!(f, "{}", b),
|
||||
Expression::IfElse(ref condition, ref consequent, ref alternative) => write!(
|
||||
|
|
|
@ -1012,6 +1012,97 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
FlatUExpression::with_bits(xor)
|
||||
}
|
||||
|
||||
fn euclidean_division(
|
||||
&mut self,
|
||||
symbols: &ZirFunctionSymbols<'ast, T>,
|
||||
statements_flattened: &mut FlatStatements<T>,
|
||||
target_bitwidth: UBitwidth,
|
||||
left: UExpression<'ast, T>,
|
||||
right: UExpression<'ast, T>,
|
||||
) -> (FlatExpression<T>, FlatExpression<T>) {
|
||||
let left_flattened = self
|
||||
.flatten_uint_expression(symbols, statements_flattened, left)
|
||||
.get_field_unchecked();
|
||||
let right_flattened = self
|
||||
.flatten_uint_expression(symbols, statements_flattened, right)
|
||||
.get_field_unchecked();
|
||||
let n = if left_flattened.is_linear() {
|
||||
left_flattened
|
||||
} else {
|
||||
let id = self.use_sym();
|
||||
statements_flattened.push(FlatStatement::Definition(id, left_flattened));
|
||||
FlatExpression::Identifier(id)
|
||||
};
|
||||
let d = if right_flattened.is_linear() {
|
||||
right_flattened
|
||||
} else {
|
||||
let id = self.use_sym();
|
||||
statements_flattened.push(FlatStatement::Definition(id, right_flattened));
|
||||
FlatExpression::Identifier(id)
|
||||
};
|
||||
|
||||
// first check that the d is not 0 by giving its inverse
|
||||
let invd = self.use_sym();
|
||||
|
||||
// # invd = 1/d
|
||||
statements_flattened.push(FlatStatement::Directive(FlatDirective::new(
|
||||
vec![invd],
|
||||
Solver::Div,
|
||||
vec![FlatExpression::Number(T::one()), d.clone()],
|
||||
)));
|
||||
|
||||
// assert(invd * d == 1)
|
||||
statements_flattened.push(FlatStatement::Condition(
|
||||
FlatExpression::Number(T::one()),
|
||||
FlatExpression::Mult(box invd.into(), box d.clone().into()),
|
||||
));
|
||||
|
||||
// now introduce the quotient and remainder
|
||||
let q = self.use_sym();
|
||||
let r = self.use_sym();
|
||||
|
||||
statements_flattened.push(FlatStatement::Directive(FlatDirective {
|
||||
inputs: vec![n.clone(), d.clone()],
|
||||
outputs: vec![q.clone(), r.clone()],
|
||||
solver: Solver::EuclideanDiv,
|
||||
}));
|
||||
|
||||
// q in range
|
||||
let _ = self.get_bits(
|
||||
FlatUExpression::with_field(FlatExpression::from(q)),
|
||||
target_bitwidth.to_usize(),
|
||||
target_bitwidth,
|
||||
statements_flattened,
|
||||
);
|
||||
|
||||
// r in range
|
||||
let _ = self.get_bits(
|
||||
FlatUExpression::with_field(FlatExpression::from(r)),
|
||||
target_bitwidth.to_usize(),
|
||||
target_bitwidth,
|
||||
statements_flattened,
|
||||
);
|
||||
|
||||
// r < d <=> r - d + 2**w < 2**w
|
||||
let _ = self.get_bits(
|
||||
FlatUExpression::with_field(FlatExpression::Add(
|
||||
box FlatExpression::Sub(box r.into(), box d.clone().into()),
|
||||
box FlatExpression::Number(T::from(2usize.pow(target_bitwidth.to_usize() as u32))),
|
||||
)),
|
||||
target_bitwidth.to_usize(),
|
||||
target_bitwidth,
|
||||
statements_flattened,
|
||||
);
|
||||
|
||||
// q*d == n - r
|
||||
statements_flattened.push(FlatStatement::Condition(
|
||||
FlatExpression::Sub(box n, box r.into()),
|
||||
FlatExpression::Mult(box q.into(), box d),
|
||||
));
|
||||
|
||||
(q.into(), r.into())
|
||||
}
|
||||
|
||||
/// Flattens a uint expression
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -1208,6 +1299,28 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
|
||||
FlatUExpression::with_field(FlatExpression::Identifier(res))
|
||||
}
|
||||
UExpressionInner::Div(box left, box right) => {
|
||||
let (q, _) = self.euclidean_division(
|
||||
symbols,
|
||||
statements_flattened,
|
||||
target_bitwidth,
|
||||
left,
|
||||
right,
|
||||
);
|
||||
|
||||
FlatUExpression::with_field(q)
|
||||
}
|
||||
UExpressionInner::Rem(box left, box right) => {
|
||||
let (_, r) = self.euclidean_division(
|
||||
symbols,
|
||||
statements_flattened,
|
||||
target_bitwidth,
|
||||
left,
|
||||
right,
|
||||
);
|
||||
|
||||
FlatUExpression::with_field(r)
|
||||
}
|
||||
UExpressionInner::IfElse(box condition, box consequence, box alternative) => self
|
||||
.flatten_if_else_expression(
|
||||
symbols,
|
||||
|
@ -1533,10 +1646,10 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
assert!(to < T::get_required_bits());
|
||||
|
||||
// constants do not require directives
|
||||
match e.field.clone() {
|
||||
Some(FlatExpression::Number(x)) => {
|
||||
match e.field {
|
||||
Some(FlatExpression::Number(ref x)) => {
|
||||
let bits: Vec<_> = ir::Interpreter::default()
|
||||
.execute_solver(&Solver::bits(to), &vec![x])
|
||||
.execute_solver(&Solver::bits(to), &vec![x.clone()])
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|x| FlatExpression::Number(x))
|
||||
|
@ -1981,7 +2094,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
// push parameters
|
||||
let arguments_flattened = funct
|
||||
.arguments
|
||||
.iter()
|
||||
.into_iter()
|
||||
.map(|p| self.use_parameter(&p, &mut statements_flattened))
|
||||
.collect();
|
||||
|
||||
|
@ -2079,7 +2192,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
if parameter.private {
|
||||
// we insert dummy condition statement for private field elements
|
||||
// to avoid unconstrained variables
|
||||
// translates to y = x * x
|
||||
// translates to y == x * x
|
||||
statements_flattened.push(FlatStatement::Condition(
|
||||
self.use_sym().into(),
|
||||
FlatExpression::Mult(box variable.into(), box variable.into()),
|
||||
|
|
|
@ -263,7 +263,7 @@ impl<T: Field> Div<&T> for LinComb<T> {
|
|||
type Output = LinComb<T>;
|
||||
|
||||
fn div(self, scalar: &T) -> LinComb<T> {
|
||||
self * &scalar.inverse_mul()
|
||||
self * &scalar.inverse_mul().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -141,7 +141,10 @@ impl Interpreter {
|
|||
let res = match s {
|
||||
Solver::ConditionEq => match inputs[0].is_zero() {
|
||||
true => vec![T::zero(), T::one()],
|
||||
false => vec![T::one(), T::one() / inputs[0].clone()],
|
||||
false => vec![
|
||||
T::one(),
|
||||
T::one().checked_div(&inputs[0]).unwrap_or(T::one()),
|
||||
],
|
||||
},
|
||||
Solver::Bits(bit_width) => {
|
||||
let mut num = inputs[0].clone();
|
||||
|
@ -183,7 +186,20 @@ impl Interpreter {
|
|||
let c = inputs[2].clone();
|
||||
vec![a * (b - c.clone()) + c]
|
||||
}
|
||||
Solver::Div => vec![inputs[0].clone() / inputs[1].clone()],
|
||||
Solver::Div => vec![inputs[0]
|
||||
.clone()
|
||||
.checked_div(&inputs[1])
|
||||
.unwrap_or(T::one())],
|
||||
Solver::EuclideanDiv => {
|
||||
use num::CheckedDiv;
|
||||
|
||||
let n = inputs[0].clone().to_biguint();
|
||||
let d = inputs[1].clone().to_biguint();
|
||||
|
||||
let q = n.checked_div(&d).unwrap_or(0u32.into());
|
||||
let r = n - d * &q;
|
||||
vec![T::try_from(q).unwrap(), T::try_from(r).unwrap()]
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(res.len(), expected_output_count);
|
||||
|
|
|
@ -1243,7 +1243,7 @@ impl<'ast> Checker<'ast> {
|
|||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Cannot apply `+` to {:?}, {:?}",
|
||||
"Cannot apply `+` to {}, {}",
|
||||
e1.get_type(),
|
||||
e2.get_type()
|
||||
),
|
||||
|
@ -1254,7 +1254,7 @@ impl<'ast> Checker<'ast> {
|
|||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Cannot apply `+` to {:?}, {:?}",
|
||||
"Cannot apply `+` to {}, {}",
|
||||
t1.get_type(),
|
||||
t2.get_type()
|
||||
),
|
||||
|
@ -1277,7 +1277,7 @@ impl<'ast> Checker<'ast> {
|
|||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Cannot apply `+` to {:?}, {:?}",
|
||||
"Cannot apply `+` to {}, {}",
|
||||
e1.get_type(),
|
||||
e2.get_type()
|
||||
),
|
||||
|
@ -1288,7 +1288,7 @@ impl<'ast> Checker<'ast> {
|
|||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Expected only field elements, found {:?}, {:?}",
|
||||
"Expected only field elements, found {}, {}",
|
||||
t1.get_type(),
|
||||
t2.get_type()
|
||||
),
|
||||
|
@ -1311,7 +1311,7 @@ impl<'ast> Checker<'ast> {
|
|||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Cannot apply `*` to {:?}, {:?}",
|
||||
"Cannot apply `*` to {}, {}",
|
||||
e1.get_type(),
|
||||
e2.get_type()
|
||||
),
|
||||
|
@ -1322,7 +1322,7 @@ impl<'ast> Checker<'ast> {
|
|||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Cannot apply `*` to {:?}, {:?}",
|
||||
"Cannot apply `*` to {}, {}",
|
||||
t1.get_type(),
|
||||
t2.get_type()
|
||||
),
|
||||
|
@ -1337,11 +1337,57 @@ impl<'ast> Checker<'ast> {
|
|||
(TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => {
|
||||
Ok(FieldElementExpression::Div(box e1, box e2).into())
|
||||
}
|
||||
(TypedExpression::Uint(e1), TypedExpression::Uint(e2)) => {
|
||||
if e1.get_type() == e2.get_type() {
|
||||
Ok(UExpression::div(e1, e2).into())
|
||||
} else {
|
||||
Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Cannot apply `/` to {}, {}",
|
||||
e1.get_type(),
|
||||
e2.get_type()
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
(t1, t2) => Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Expected only field elements, found {:?}, {:?}",
|
||||
"Cannot apply `/` to {}, {}",
|
||||
t1.get_type(),
|
||||
t2.get_type()
|
||||
),
|
||||
}),
|
||||
}
|
||||
}
|
||||
Expression::Rem(box e1, box e2) => {
|
||||
let e1_checked = self.check_expression(e1, module_id, &types)?;
|
||||
let e2_checked = self.check_expression(e2, module_id, &types)?;
|
||||
|
||||
match (e1_checked, e2_checked) {
|
||||
(TypedExpression::Uint(e1), TypedExpression::Uint(e2)) => {
|
||||
if e1.get_type() == e2.get_type() {
|
||||
Ok(UExpression::rem(e1, e2).into())
|
||||
} else {
|
||||
Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Cannot apply `%` to {}, {}",
|
||||
e1.get_type(),
|
||||
e2.get_type()
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
(t1, t2) => Err(ErrorInner {
|
||||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Cannot apply `%` to {}, {}",
|
||||
t1.get_type(),
|
||||
t2.get_type()
|
||||
),
|
||||
|
@ -1360,7 +1406,7 @@ impl<'ast> Checker<'ast> {
|
|||
pos: Some(pos),
|
||||
|
||||
message: format!(
|
||||
"Expected only field elements, found {:?}, {:?}",
|
||||
"Expected only field elements, found {}, {}",
|
||||
t1.get_type(),
|
||||
t2.get_type()
|
||||
),
|
||||
|
|
|
@ -10,6 +10,7 @@ pub enum Solver {
|
|||
Or,
|
||||
ShaAndXorAndXorAnd,
|
||||
ShaCh,
|
||||
EuclideanDiv,
|
||||
}
|
||||
|
||||
impl fmt::Display for Solver {
|
||||
|
@ -28,6 +29,7 @@ impl Solver {
|
|||
Solver::Or => (2, 1),
|
||||
Solver::ShaAndXorAndXorAnd => (3, 1),
|
||||
Solver::ShaCh => (3, 1),
|
||||
Solver::EuclideanDiv => (2, 2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -724,6 +724,18 @@ pub fn fold_uint_expression_inner<'ast, T: Field>(
|
|||
|
||||
zir::UExpressionInner::Mult(box left, box right)
|
||||
}
|
||||
typed_absy::UExpressionInner::Div(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
||||
zir::UExpressionInner::Div(box left, box right)
|
||||
}
|
||||
typed_absy::UExpressionInner::Rem(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
||||
zir::UExpressionInner::Rem(box left, box right)
|
||||
}
|
||||
typed_absy::UExpressionInner::Xor(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
|
|
@ -420,6 +420,48 @@ impl<'ast, T: Field> Folder<'ast, T> for Propagator<'ast, T> {
|
|||
UExpressionInner::Mult(box e1.annotate(bitwidth), box e2.annotate(bitwidth))
|
||||
}
|
||||
},
|
||||
UExpressionInner::Div(box e1, box e2) => match (
|
||||
self.fold_uint_expression(e1).into_inner(),
|
||||
self.fold_uint_expression(e2).into_inner(),
|
||||
) {
|
||||
(UExpressionInner::Value(v1), UExpressionInner::Value(v2)) => {
|
||||
use std::convert::TryInto;
|
||||
UExpressionInner::Value(
|
||||
(v1 / v2) % 2_u128.pow(bitwidth.to_usize().try_into().unwrap()),
|
||||
)
|
||||
}
|
||||
(e, UExpressionInner::Value(v)) => match v {
|
||||
1 => e,
|
||||
_ => UExpressionInner::Div(
|
||||
box e.annotate(bitwidth),
|
||||
box UExpressionInner::Value(v).annotate(bitwidth),
|
||||
),
|
||||
},
|
||||
(e1, e2) => {
|
||||
UExpressionInner::Div(box e1.annotate(bitwidth), box e2.annotate(bitwidth))
|
||||
}
|
||||
},
|
||||
UExpressionInner::Rem(box e1, box e2) => match (
|
||||
self.fold_uint_expression(e1).into_inner(),
|
||||
self.fold_uint_expression(e2).into_inner(),
|
||||
) {
|
||||
(UExpressionInner::Value(v1), UExpressionInner::Value(v2)) => {
|
||||
use std::convert::TryInto;
|
||||
UExpressionInner::Value(
|
||||
(v1 % v2) % 2_u128.pow(bitwidth.to_usize().try_into().unwrap()),
|
||||
)
|
||||
}
|
||||
(e, UExpressionInner::Value(v)) => match v {
|
||||
1 => UExpressionInner::Value(0),
|
||||
_ => UExpressionInner::Rem(
|
||||
box e.annotate(bitwidth),
|
||||
box UExpressionInner::Value(v).annotate(bitwidth),
|
||||
),
|
||||
},
|
||||
(e1, e2) => {
|
||||
UExpressionInner::Rem(box e1.annotate(bitwidth), box e2.annotate(bitwidth))
|
||||
}
|
||||
},
|
||||
UExpressionInner::RightShift(box e, box by) => {
|
||||
let e = self.fold_uint_expression(e);
|
||||
let by = self.fold_field_expression(by);
|
||||
|
|
|
@ -258,6 +258,20 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
|
|||
|
||||
UExpression::mult(left, right).with_max(max)
|
||||
}
|
||||
Div(box left, box right) => {
|
||||
// reduce the two terms
|
||||
let left = self.fold_uint_expression(left);
|
||||
let right = self.fold_uint_expression(right);
|
||||
|
||||
UExpression::div(force_reduce(left), force_reduce(right)).with_max(range_max)
|
||||
}
|
||||
Rem(box left, box right) => {
|
||||
// reduce the two terms
|
||||
let left = self.fold_uint_expression(left);
|
||||
let right = self.fold_uint_expression(right);
|
||||
|
||||
UExpression::rem(force_reduce(left), force_reduce(right)).with_max(range_max)
|
||||
}
|
||||
Not(box e) => {
|
||||
let e = self.fold_uint_expression(e);
|
||||
|
||||
|
|
|
@ -430,6 +430,18 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
|
||||
UExpressionInner::Mult(box left, box right)
|
||||
}
|
||||
UExpressionInner::Div(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
||||
UExpressionInner::Div(box left, box right)
|
||||
}
|
||||
UExpressionInner::Rem(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
||||
UExpressionInner::Rem(box left, box right)
|
||||
}
|
||||
UExpressionInner::Xor(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
|
|
@ -890,6 +890,8 @@ impl<'ast, T: fmt::Display> fmt::Display for UExpression<'ast, T> {
|
|||
UExpressionInner::Xor(ref lhs, ref rhs) => write!(f, "({} ^ {})", lhs, rhs),
|
||||
UExpressionInner::Sub(ref lhs, ref rhs) => write!(f, "({} - {})", lhs, rhs),
|
||||
UExpressionInner::Mult(ref lhs, ref rhs) => write!(f, "({} * {})", lhs, rhs),
|
||||
UExpressionInner::Div(ref lhs, ref rhs) => write!(f, "({} / {})", lhs, rhs),
|
||||
UExpressionInner::Rem(ref lhs, ref rhs) => write!(f, "({} % {})", lhs, rhs),
|
||||
UExpressionInner::RightShift(ref e, ref by) => write!(f, "({} >> {})", e, by),
|
||||
UExpressionInner::LeftShift(ref e, ref by) => write!(f, "({} << {})", e, by),
|
||||
UExpressionInner::Not(ref e) => write!(f, "!{}", e),
|
||||
|
|
|
@ -23,6 +23,18 @@ impl<'ast, T: Field> UExpression<'ast, T> {
|
|||
UExpressionInner::Mult(box self, box other).annotate(bitwidth)
|
||||
}
|
||||
|
||||
pub fn div(self, other: Self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
assert_eq!(bitwidth, other.bitwidth);
|
||||
UExpressionInner::Div(box self, box other).annotate(bitwidth)
|
||||
}
|
||||
|
||||
pub fn rem(self, other: Self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
assert_eq!(bitwidth, other.bitwidth);
|
||||
UExpressionInner::Rem(box self, box other).annotate(bitwidth)
|
||||
}
|
||||
|
||||
pub fn xor(self, other: Self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
assert_eq!(bitwidth, other.bitwidth);
|
||||
|
@ -89,6 +101,8 @@ pub enum UExpressionInner<'ast, T> {
|
|||
Add(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Sub(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Mult(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Div(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Rem(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Xor(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
And(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Or(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
|
|
|
@ -283,6 +283,18 @@ pub fn fold_uint_expression_inner<'ast, T: Field, F: Folder<'ast, T>>(
|
|||
|
||||
UExpressionInner::Mult(box left, box right)
|
||||
}
|
||||
UExpressionInner::Div(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
||||
UExpressionInner::Div(box left, box right)
|
||||
}
|
||||
UExpressionInner::Rem(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
||||
UExpressionInner::Rem(box left, box right)
|
||||
}
|
||||
UExpressionInner::Xor(box left, box right) => {
|
||||
let left = f.fold_uint_expression(left);
|
||||
let right = f.fold_uint_expression(right);
|
||||
|
|
|
@ -487,6 +487,8 @@ impl<'ast, T: fmt::Display> fmt::Display for UExpression<'ast, T> {
|
|||
UExpressionInner::Add(ref lhs, ref rhs) => write!(f, "({} + {})", lhs, rhs),
|
||||
UExpressionInner::Sub(ref lhs, ref rhs) => write!(f, "({} - {})", lhs, rhs),
|
||||
UExpressionInner::Mult(ref lhs, ref rhs) => write!(f, "({} * {})", lhs, rhs),
|
||||
UExpressionInner::Div(ref lhs, ref rhs) => write!(f, "({} * {})", lhs, rhs),
|
||||
UExpressionInner::Rem(ref lhs, ref rhs) => write!(f, "({} % {})", lhs, rhs),
|
||||
UExpressionInner::Xor(ref lhs, ref rhs) => write!(f, "({} ^ {})", lhs, rhs),
|
||||
UExpressionInner::And(ref lhs, ref rhs) => write!(f, "({} & {})", lhs, rhs),
|
||||
UExpressionInner::Or(ref lhs, ref rhs) => write!(f, "({} | {})", lhs, rhs),
|
||||
|
|
|
@ -22,6 +22,18 @@ impl<'ast, T: Field> UExpression<'ast, T> {
|
|||
UExpressionInner::Mult(box self, box other).annotate(bitwidth)
|
||||
}
|
||||
|
||||
pub fn div(self, other: Self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
assert_eq!(bitwidth, other.bitwidth);
|
||||
UExpressionInner::Div(box self, box other).annotate(bitwidth)
|
||||
}
|
||||
|
||||
pub fn rem(self, other: Self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
assert_eq!(bitwidth, other.bitwidth);
|
||||
UExpressionInner::Rem(box self, box other).annotate(bitwidth)
|
||||
}
|
||||
|
||||
pub fn xor(self, other: Self) -> UExpression<'ast, T> {
|
||||
let bitwidth = self.bitwidth;
|
||||
assert_eq!(bitwidth, other.bitwidth);
|
||||
|
@ -148,6 +160,8 @@ pub enum UExpressionInner<'ast, T> {
|
|||
Add(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Sub(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Mult(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Div(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Rem(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Xor(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
And(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
Or(Box<UExpression<'ast, T>>, Box<UExpression<'ast, T>>),
|
||||
|
|
26
zokrates_core_test/tests/tests/uint/div.json
Normal file
26
zokrates_core_test/tests/tests/uint/div.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/uint/div.zok",
|
||||
"max_constraint_count": 43,
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["255", "1"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["255"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["42", "10"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["4"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
6
zokrates_core_test/tests/tests/uint/div.zok
Normal file
6
zokrates_core_test/tests/tests/uint/div.zok
Normal file
|
@ -0,0 +1,6 @@
|
|||
def main(u8 x, u8 y) -> u8:
|
||||
assert(0x02 / 0x02 == 0x01)
|
||||
assert(0x04 / 0x02 == 0x02)
|
||||
assert(0x05 / 0x02 == 0x02)
|
||||
assert(0xff / 0x03 == 0x55)
|
||||
return x / y
|
26
zokrates_core_test/tests/tests/uint/div_rem.json
Normal file
26
zokrates_core_test/tests/tests/uint/div_rem.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/uint/div_rem.zok",
|
||||
"max_constraint_count": 43,
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["255", "1"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["255", "0"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["42", "10"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["4", "2"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
2
zokrates_core_test/tests/tests/uint/div_rem.zok
Normal file
2
zokrates_core_test/tests/tests/uint/div_rem.zok
Normal file
|
@ -0,0 +1,2 @@
|
|||
def main(u8 n, u8 d) -> (u8, u8):
|
||||
return n / d, n % d
|
26
zokrates_core_test/tests/tests/uint/rem.json
Normal file
26
zokrates_core_test/tests/tests/uint/rem.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/uint/rem.zok",
|
||||
"max_constraint_count": 43,
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": ["255", "1"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["0"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"input": {
|
||||
"values": ["42", "10"]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["2"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
7
zokrates_core_test/tests/tests/uint/rem.zok
Normal file
7
zokrates_core_test/tests/tests/uint/rem.zok
Normal file
|
@ -0,0 +1,7 @@
|
|||
def main(u8 x, u8 y) -> u8:
|
||||
assert(0x02 % 0x02 == 0x00)
|
||||
assert(0x04 % 0x02 == 0x00)
|
||||
assert(0x05 % 0x02 == 0x01)
|
||||
assert(0xff % 0x03 == 0x00)
|
||||
assert(0xff % 0x01 == 0x00)
|
||||
return x % y
|
|
@ -12,7 +12,7 @@ use ark_ec::PairingEngine;
|
|||
use bellman_ce::pairing::ff::ScalarEngine;
|
||||
use bellman_ce::pairing::Engine;
|
||||
use num_bigint::BigUint;
|
||||
use num_traits::{One, Zero};
|
||||
use num_traits::{CheckedDiv, One, Zero};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::{From, TryFrom};
|
||||
use std::fmt::{Debug, Display};
|
||||
|
@ -63,8 +63,8 @@ pub trait Field:
|
|||
+ for<'a> Sub<&'a Self, Output = Self>
|
||||
+ Mul<Self, Output = Self>
|
||||
+ for<'a> Mul<&'a Self, Output = Self>
|
||||
+ CheckedDiv
|
||||
+ Div<Self, Output = Self>
|
||||
+ for<'a> Div<&'a Self, Output = Self>
|
||||
+ Pow<usize, Output = Self>
|
||||
+ Pow<Self, Output = Self>
|
||||
+ for<'a> Pow<&'a Self, Output = Self>
|
||||
|
@ -80,7 +80,7 @@ pub trait Field:
|
|||
/// Returns this `Field`'s contents as decimal string
|
||||
fn to_dec_string(&self) -> String;
|
||||
/// Returns the multiplicative inverse, i.e.: self * self.inverse_mul() = Self::one()
|
||||
fn inverse_mul(&self) -> Self;
|
||||
fn inverse_mul(&self) -> Option<Self>;
|
||||
/// Returns the smallest value that can be represented by this field type.
|
||||
fn min_value() -> Self;
|
||||
/// Returns the largest value that can be represented by this field type.
|
||||
|
@ -131,7 +131,7 @@ mod prime_field {
|
|||
use lazy_static::lazy_static;
|
||||
use num_bigint::{BigInt, BigUint, Sign, ToBigInt};
|
||||
use num_integer::Integer;
|
||||
use num_traits::{One, Zero};
|
||||
use num_traits::{CheckedDiv, One, Zero};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::convert::From;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -175,11 +175,14 @@ mod prime_field {
|
|||
self.value.to_str_radix(10)
|
||||
}
|
||||
|
||||
fn inverse_mul(&self) -> FieldPrime {
|
||||
fn inverse_mul(&self) -> Option<FieldPrime> {
|
||||
let (b, s, _) = extended_euclid(&self.value, &*P);
|
||||
assert_eq!(b, BigInt::one());
|
||||
FieldPrime {
|
||||
value: &s - s.div_floor(&*P) * &*P,
|
||||
if b == BigInt::one() {
|
||||
Some(FieldPrime {
|
||||
value: &s - s.div_floor(&*P) * &*P,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn min_value() -> FieldPrime {
|
||||
|
@ -389,11 +392,17 @@ mod prime_field {
|
|||
}
|
||||
}
|
||||
|
||||
impl CheckedDiv for FieldPrime {
|
||||
fn checked_div(&self, other: &FieldPrime) -> Option<FieldPrime> {
|
||||
other.inverse_mul().map(|inv| inv * self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<FieldPrime> for FieldPrime {
|
||||
type Output = FieldPrime;
|
||||
|
||||
fn div(self, other: FieldPrime) -> FieldPrime {
|
||||
self * other.inverse_mul()
|
||||
self.checked_div(&other).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,7 +410,7 @@ mod prime_field {
|
|||
type Output = FieldPrime;
|
||||
|
||||
fn div(self, other: &FieldPrime) -> FieldPrime {
|
||||
self / other.clone()
|
||||
self.checked_div(&other).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,11 +114,12 @@ op_add = {"+"}
|
|||
op_sub = {"-"}
|
||||
op_mul = {"*"}
|
||||
op_div = {"/"}
|
||||
op_rem = {"%"}
|
||||
op_pow = @{"**"}
|
||||
op_not = {"!"}
|
||||
op_left_shift = @{"<<"}
|
||||
op_right_shift = @{">>"}
|
||||
op_binary = _ { op_pow | op_or | op_and | op_bit_xor | op_bit_and | op_bit_or | op_left_shift | op_right_shift | op_equal | op_not_equal | op_lte | op_lt | op_gte | op_gt | op_add | op_sub | op_mul | op_div }
|
||||
op_binary = _ { op_pow | op_or | op_and | op_bit_xor | op_bit_and | op_bit_or | op_left_shift | op_right_shift | op_equal | op_not_equal | op_lte | op_lt | op_gte | op_gt | op_add | op_sub | op_mul | op_div | op_rem }
|
||||
op_unary = { op_not }
|
||||
|
||||
|
||||
|
|
|
@ -49,7 +49,9 @@ mod ast {
|
|||
Operator::new(Rule::op_left_shift, Assoc::Left)
|
||||
| Operator::new(Rule::op_right_shift, Assoc::Left),
|
||||
Operator::new(Rule::op_add, Assoc::Left) | Operator::new(Rule::op_sub, Assoc::Left),
|
||||
Operator::new(Rule::op_mul, Assoc::Left) | Operator::new(Rule::op_div, Assoc::Left),
|
||||
Operator::new(Rule::op_mul, Assoc::Left)
|
||||
| Operator::new(Rule::op_div, Assoc::Left)
|
||||
| Operator::new(Rule::op_rem, Assoc::Left),
|
||||
Operator::new(Rule::op_pow, Assoc::Left),
|
||||
])
|
||||
}
|
||||
|
@ -71,6 +73,7 @@ mod ast {
|
|||
Rule::op_sub => Expression::binary(BinaryOperator::Sub, lhs, rhs, span),
|
||||
Rule::op_mul => Expression::binary(BinaryOperator::Mul, lhs, rhs, span),
|
||||
Rule::op_div => Expression::binary(BinaryOperator::Div, lhs, rhs, span),
|
||||
Rule::op_rem => Expression::binary(BinaryOperator::Rem, lhs, rhs, span),
|
||||
Rule::op_pow => Expression::binary(BinaryOperator::Pow, lhs, rhs, span),
|
||||
Rule::op_equal => Expression::binary(BinaryOperator::Eq, lhs, rhs, span),
|
||||
Rule::op_not_equal => Expression::binary(BinaryOperator::NotEq, lhs, rhs, span),
|
||||
|
@ -418,6 +421,7 @@ mod ast {
|
|||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Rem,
|
||||
Eq,
|
||||
NotEq,
|
||||
Lt,
|
||||
|
|
Loading…
Reference in a new issue