diff --git a/changelogs/unreleased/1304-dark64 b/changelogs/unreleased/1304-dark64 new file mode 100644 index 000000000..e9936ec5f --- /dev/null +++ b/changelogs/unreleased/1304-dark64 @@ -0,0 +1 @@ +Add implementation of big integer arithmetic to stdlib \ No newline at end of file diff --git a/zokrates_analysis/src/expression_validator.rs b/zokrates_analysis/src/expression_validator.rs index 9dcd63413..f60542376 100644 --- a/zokrates_analysis/src/expression_validator.rs +++ b/zokrates_analysis/src/expression_validator.rs @@ -39,12 +39,14 @@ impl<'ast, T: Field> ResultFolder<'ast, T> for ExpressionValidator { ) -> Result, Self::Error> { match e { // these should have been propagated away - FieldElementExpression::And(_) + FieldElementExpression::IDiv(_) + | FieldElementExpression::Rem(_) + | FieldElementExpression::And(_) | FieldElementExpression::Or(_) | FieldElementExpression::Xor(_) | FieldElementExpression::LeftShift(_) | FieldElementExpression::RightShift(_) => Err(Error(format!( - "Found non-constant bitwise operation in field element expression `{}`", + "Field element expression `{}` must be a constant expression", e ))), FieldElementExpression::Pow(e) => { diff --git a/zokrates_analysis/src/flatten_complex_types.rs b/zokrates_analysis/src/flatten_complex_types.rs index 64705d1ad..e9ec88a91 100644 --- a/zokrates_analysis/src/flatten_complex_types.rs +++ b/zokrates_analysis/src/flatten_complex_types.rs @@ -1069,6 +1069,12 @@ fn fold_field_expression<'ast, T: Field>( typed::FieldElementExpression::Div(e) => { zir::FieldElementExpression::Div(f.fold_binary_expression(statements_buffer, e)) } + typed::FieldElementExpression::IDiv(e) => { + zir::FieldElementExpression::IDiv(f.fold_binary_expression(statements_buffer, e)) + } + typed::FieldElementExpression::Rem(e) => { + zir::FieldElementExpression::Rem(f.fold_binary_expression(statements_buffer, e)) + } typed::FieldElementExpression::Pow(e) => { zir::FieldElementExpression::Pow(f.fold_binary_expression(statements_buffer, e)) } diff --git a/zokrates_analysis/src/propagation.rs b/zokrates_analysis/src/propagation.rs index 03c98c38b..06f23b9ba 100644 --- a/zokrates_analysis/src/propagation.rs +++ b/zokrates_analysis/src/propagation.rs @@ -856,6 +856,22 @@ impl<'ast, T: Field> ResultFolder<'ast, T> for Propagator<'ast, T> { Ok(UExpression::and(e1.annotate(bitwidth), e2.annotate(bitwidth)).into_inner()) } }, + UExpressionInner::Or(e) => match ( + self.fold_uint_expression(*e.left)?.into_inner(), + self.fold_uint_expression(*e.right)?.into_inner(), + ) { + (UExpressionInner::Value(v1), UExpressionInner::Value(v2)) => { + Ok(UExpression::value(v1.value | v2.value)) + } + (UExpressionInner::Value(v), e) | (e, UExpressionInner::Value(v)) + if v.value == 0 => + { + Ok(e) + } + (e1, e2) => { + Ok(UExpression::or(e1.annotate(bitwidth), e2.annotate(bitwidth)).into_inner()) + } + }, UExpressionInner::Not(e) => { let e = self.fold_uint_expression(*e.inner)?.into_inner(); match e { @@ -939,6 +955,35 @@ impl<'ast, T: Field> ResultFolder<'ast, T> for Propagator<'ast, T> { (e1, e2) => Ok(e1 / e2), } } + FieldElementExpression::IDiv(e) => { + let left = self.fold_field_expression(*e.left)?; + let right = self.fold_field_expression(*e.right)?; + + Ok(match (left, right) { + (FieldElementExpression::Value(n1), FieldElementExpression::Value(n2)) => { + FieldElementExpression::value( + T::try_from(n1.value.to_biguint().div(n2.value.to_biguint())).unwrap(), + ) + } + (e1, e2) => FieldElementExpression::idiv(e1, e2), + }) + } + FieldElementExpression::Rem(e) => { + let left = self.fold_field_expression(*e.left)?; + let right = self.fold_field_expression(*e.right)?; + + Ok(match (left, right) { + (_, FieldElementExpression::Value(n)) if n.value == T::from(1) => { + FieldElementExpression::value(T::zero()) + } + (FieldElementExpression::Value(n1), FieldElementExpression::Value(n2)) => { + FieldElementExpression::value( + T::try_from(n1.value.to_biguint().rem(n2.value.to_biguint())).unwrap(), + ) + } + (e1, e2) => e1 % e2, + }) + } FieldElementExpression::Neg(e) => match self.fold_field_expression(*e.inner)? { FieldElementExpression::Value(n) => { Ok(FieldElementExpression::value(T::zero() - n.value)) @@ -1606,6 +1651,48 @@ mod tests { ); } + #[test] + fn idiv() { + let e = FieldElementExpression::idiv( + FieldElementExpression::value(Bn128Field::from(7)), + FieldElementExpression::value(Bn128Field::from(2)), + ); + + assert_eq!( + Propagator::default().fold_field_expression(e), + Ok(FieldElementExpression::value(Bn128Field::from(3))) + ); + } + + #[test] + fn rem() { + let mut propagator = Propagator::default(); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::rem( + FieldElementExpression::value(Bn128Field::from(5)), + FieldElementExpression::value(Bn128Field::from(2)), + )), + Ok(FieldElementExpression::value(Bn128Field::from(1))) + ); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::rem( + FieldElementExpression::value(Bn128Field::from(2)), + FieldElementExpression::value(Bn128Field::from(5)), + )), + Ok(FieldElementExpression::value(Bn128Field::from(2))) + ); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::rem( + FieldElementExpression::identifier("a".into()), + FieldElementExpression::value(Bn128Field::from(1)), + )), + Ok(FieldElementExpression::value(Bn128Field::from(0))) + ); + } + #[test] fn pow() { let e = FieldElementExpression::pow( diff --git a/zokrates_analysis/src/zir_propagation.rs b/zokrates_analysis/src/zir_propagation.rs index a677520c8..8e62bb34d 100644 --- a/zokrates_analysis/src/zir_propagation.rs +++ b/zokrates_analysis/src/zir_propagation.rs @@ -322,6 +322,42 @@ impl<'ast, T: Field> ResultFolder<'ast, T> for ZirPropagator<'ast, T> { (e1, e2) => Ok(FieldElementExpression::div(e1, e2).span(e.span)), } } + FieldElementExpression::IDiv(e) => { + let left = self.fold_field_expression(*e.left)?; + let right = self.fold_field_expression(*e.right)?; + + match (left, right) { + (_, FieldElementExpression::Value(n)) if n.value == T::from(0) => { + Err(Error::DivisionByZero) + } + (e, FieldElementExpression::Value(n)) if n.value == T::from(1) => Ok(e), + (FieldElementExpression::Value(n1), FieldElementExpression::Value(n2)) => { + Ok(FieldElementExpression::value( + T::try_from(n1.value.to_biguint().div(n2.value.to_biguint())).unwrap(), + )) + } + (e1, e2) => Ok(FieldElementExpression::idiv(e1, e2).span(e.span)), + } + } + FieldElementExpression::Rem(e) => { + let left = self.fold_field_expression(*e.left)?; + let right = self.fold_field_expression(*e.right)?; + + match (left, right) { + (_, FieldElementExpression::Value(n)) if n.value == T::from(0) => { + Err(Error::DivisionByZero) + } + (_, FieldElementExpression::Value(n)) if n.value == T::from(1) => { + Ok(FieldElementExpression::value(T::zero())) + } + (FieldElementExpression::Value(n1), FieldElementExpression::Value(n2)) => { + Ok(FieldElementExpression::value( + T::try_from(n1.value.to_biguint().rem(n2.value.to_biguint())).unwrap(), + )) + } + (e1, e2) => Ok(FieldElementExpression::rem(e1, e2).span(e.span)), + } + } FieldElementExpression::Pow(e) => { let exponent = self.fold_uint_expression(*e.right)?; match (self.fold_field_expression(*e.left)?, exponent.into_inner()) { @@ -1077,6 +1113,72 @@ mod tests { ); } + #[test] + fn idiv() { + let mut propagator = ZirPropagator::default(); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::idiv( + FieldElementExpression::value(Bn128Field::from(7)), + FieldElementExpression::value(Bn128Field::from(2)), + )), + Ok(FieldElementExpression::value(Bn128Field::from(3))) + ); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::idiv( + FieldElementExpression::identifier("a".into()), + FieldElementExpression::value(Bn128Field::from(1)), + )), + Ok(FieldElementExpression::identifier("a".into())) + ); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::idiv( + FieldElementExpression::identifier("a".into()), + FieldElementExpression::value(Bn128Field::from(0)), + )), + Err(Error::DivisionByZero) + ); + } + + #[test] + fn rem() { + let mut propagator = ZirPropagator::default(); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::rem( + FieldElementExpression::value(Bn128Field::from(5)), + FieldElementExpression::value(Bn128Field::from(2)), + )), + Ok(FieldElementExpression::value(Bn128Field::from(1))) + ); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::rem( + FieldElementExpression::value(Bn128Field::from(2)), + FieldElementExpression::value(Bn128Field::from(5)), + )), + Ok(FieldElementExpression::value(Bn128Field::from(2))) + ); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::rem( + FieldElementExpression::identifier("a".into()), + FieldElementExpression::value(Bn128Field::from(1)), + )), + Ok(FieldElementExpression::value(Bn128Field::from(0))) + ); + + assert_eq!( + propagator.fold_field_expression(FieldElementExpression::div( + FieldElementExpression::identifier("a".into()), + FieldElementExpression::value(Bn128Field::from(0)), + )), + Err(Error::DivisionByZero) + ); + } + #[test] fn pow() { let mut propagator = ZirPropagator::::default(); diff --git a/zokrates_ast/src/common/operators.rs b/zokrates_ast/src/common/operators.rs index 18a26cec2..268959762 100644 --- a/zokrates_ast/src/common/operators.rs +++ b/zokrates_ast/src/common/operators.rs @@ -37,6 +37,13 @@ impl OperatorStr for OpDiv { const STR: &'static str = "/"; } +#[derive(Clone, PartialEq, Debug, Hash, Eq, PartialOrd, Ord)] +pub struct OpIDiv; + +impl OperatorStr for OpIDiv { + const STR: &'static str = "\\"; +} + #[derive(Clone, PartialEq, Debug, Hash, Eq, PartialOrd, Ord)] pub struct OpRem; diff --git a/zokrates_ast/src/ir/check.rs b/zokrates_ast/src/ir/check.rs index e4c1652ae..d49439fde 100644 --- a/zokrates_ast/src/ir/check.rs +++ b/zokrates_ast/src/ir/check.rs @@ -4,6 +4,7 @@ use crate::ir::Parameter; use crate::ir::ProgIterator; use crate::ir::Statement; use crate::ir::Variable; +use crate::Solver; use std::collections::HashSet; use zokrates_field::Field; @@ -46,7 +47,10 @@ impl<'ast, T: Field> Folder<'ast, T> for UnconstrainedVariableDetector { &mut self, d: DirectiveStatement<'ast, T>, ) -> Vec> { - self.variables.extend(d.outputs.iter()); + match d.solver { + Solver::Zir(_) => {} // we do not check variables introduced by assembly + _ => self.variables.extend(d.outputs.iter()), // this is not necessary, but we keep it as a sanity check + }; vec![Statement::Directive(d)] } } diff --git a/zokrates_ast/src/ir/serialize.rs b/zokrates_ast/src/ir/serialize.rs index 4e5ff7a23..3d77aea75 100644 --- a/zokrates_ast/src/ir/serialize.rs +++ b/zokrates_ast/src/ir/serialize.rs @@ -222,10 +222,10 @@ impl<'ast, T: Field, I: IntoIterator>> ProgIterator<'a if matches!(s, Statement::Constraint(..)) { count += 1; } - let s: Vec> = solver_indexer + let s: Vec> = unconstrained_variable_detector .fold_statement(s) .into_iter() - .flat_map(|s| unconstrained_variable_detector.fold_statement(s)) + .flat_map(|s| solver_indexer.fold_statement(s)) .collect(); for s in s { serde_cbor::to_writer(&mut w, &s)?; diff --git a/zokrates_ast/src/typed/folder.rs b/zokrates_ast/src/typed/folder.rs index 424109bf3..a5af5e1ac 100644 --- a/zokrates_ast/src/typed/folder.rs +++ b/zokrates_ast/src/typed/folder.rs @@ -1073,6 +1073,14 @@ pub fn fold_field_expression_cases<'ast, T: Field, F: Folder<'ast, T>>( BinaryOrExpression::Binary(e) => Div(e), BinaryOrExpression::Expression(u) => u, }, + IDiv(e) => match f.fold_binary_expression(&Type::FieldElement, e) { + BinaryOrExpression::Binary(e) => IDiv(e), + BinaryOrExpression::Expression(u) => u, + }, + Rem(e) => match f.fold_binary_expression(&Type::FieldElement, e) { + BinaryOrExpression::Binary(e) => Rem(e), + BinaryOrExpression::Expression(u) => u, + }, Pow(e) => match f.fold_binary_expression(&Type::FieldElement, e) { BinaryOrExpression::Binary(e) => Pow(e), BinaryOrExpression::Expression(u) => u, diff --git a/zokrates_ast/src/typed/integer.rs b/zokrates_ast/src/typed/integer.rs index d5ba9ad58..bd3d428b9 100644 --- a/zokrates_ast/src/typed/integer.rs +++ b/zokrates_ast/src/typed/integer.rs @@ -317,6 +317,14 @@ pub enum IntExpression<'ast, T> { IntExpression<'ast, T>, >, ), + IDiv( + BinaryExpression< + OpIDiv, + IntExpression<'ast, T>, + IntExpression<'ast, T>, + IntExpression<'ast, T>, + >, + ), Rem( BinaryExpression< OpRem, @@ -434,6 +442,10 @@ impl<'ast, T> Neg for IntExpression<'ast, T> { } impl<'ast, T> IntExpression<'ast, T> { + pub fn idiv(self, other: Self) -> Self { + IntExpression::IDiv(BinaryExpression::new(self, other)) + } + pub fn pow(self, other: Self) -> Self { IntExpression::Pow(BinaryExpression::new(self, other)) } @@ -470,6 +482,7 @@ impl<'ast, T: fmt::Display> fmt::Display for IntExpression<'ast, T> { IntExpression::Pos(ref e) => write!(f, "{}", e), IntExpression::Neg(ref e) => write!(f, "{}", e), IntExpression::Div(ref e) => write!(f, "{}", e), + IntExpression::IDiv(ref e) => write!(f, "{}", e), IntExpression::Rem(ref e) => write!(f, "{}", e), IntExpression::Pow(ref e) => write!(f, "{}", e), IntExpression::Select(ref select) => write!(f, "{}", select), diff --git a/zokrates_ast/src/typed/mod.rs b/zokrates_ast/src/typed/mod.rs index 11786660c..8d84ee242 100644 --- a/zokrates_ast/src/typed/mod.rs +++ b/zokrates_ast/src/typed/mod.rs @@ -1376,6 +1376,8 @@ pub enum FieldElementExpression<'ast, T> { Sub(BinaryExpression), Mult(BinaryExpression), Div(BinaryExpression), + IDiv(BinaryExpression), + Rem(BinaryExpression), Pow(BinaryExpression, Self>), And(BinaryExpression), Or(BinaryExpression), @@ -1517,7 +1519,19 @@ impl<'ast, T> std::ops::Neg for FieldElementExpression<'ast, T> { } } +impl<'ast, T> std::ops::Rem for FieldElementExpression<'ast, T> { + type Output = Self; + + fn rem(self, other: Self) -> Self::Output { + FieldElementExpression::Rem(BinaryExpression::new(self, other)) + } +} + impl<'ast, T: Field> FieldElementExpression<'ast, T> { + pub fn idiv(self, other: Self) -> Self { + FieldElementExpression::IDiv(BinaryExpression::new(self, other)) + } + pub fn pow(self, other: UExpression<'ast, T>) -> Self { FieldElementExpression::Pow(BinaryExpression::new(self, other)) } @@ -2104,6 +2118,8 @@ impl<'ast, T: fmt::Display> fmt::Display for FieldElementExpression<'ast, T> { FieldElementExpression::Sub(ref e) => write!(f, "{}", e), FieldElementExpression::Mult(ref e) => write!(f, "{}", e), FieldElementExpression::Div(ref e) => write!(f, "{}", e), + FieldElementExpression::IDiv(ref e) => write!(f, "{}", e), + FieldElementExpression::Rem(ref e) => write!(f, "{}", e), FieldElementExpression::Pow(ref e) => write!(f, "{}", e), FieldElementExpression::Neg(ref e) => write!(f, "{}", e), FieldElementExpression::Pos(ref e) => write!(f, "{}", e), @@ -2270,6 +2286,8 @@ impl<'ast, T> WithSpan for FieldElementExpression<'ast, T> { Sub(e) => Sub(e.span(span)), Pow(e) => Pow(e.span(span)), Div(e) => Div(e.span(span)), + IDiv(e) => IDiv(e.span(span)), + Rem(e) => Rem(e.span(span)), Pos(e) => Pos(e.span(span)), Neg(e) => Neg(e.span(span)), And(e) => And(e.span(span)), @@ -2295,6 +2313,8 @@ impl<'ast, T> WithSpan for FieldElementExpression<'ast, T> { Sub(e) => e.get_span(), Mult(e) => e.get_span(), Div(e) => e.get_span(), + IDiv(e) => e.get_span(), + Rem(e) => e.get_span(), Pow(e) => e.get_span(), Neg(e) => e.get_span(), Pos(e) => e.get_span(), @@ -2529,6 +2549,7 @@ impl<'ast, T> WithSpan for IntExpression<'ast, T> { Sub(e) => Sub(e.span(span)), Mult(e) => Mult(e.span(span)), Div(e) => Div(e.span(span)), + IDiv(e) => IDiv(e.span(span)), Rem(e) => Rem(e.span(span)), Pow(e) => Pow(e.span(span)), Xor(e) => Xor(e.span(span)), @@ -2552,6 +2573,7 @@ impl<'ast, T> WithSpan for IntExpression<'ast, T> { Sub(e) => e.get_span(), Mult(e) => e.get_span(), Div(e) => e.get_span(), + IDiv(e) => e.get_span(), Rem(e) => e.get_span(), Pow(e) => e.get_span(), Xor(e) => e.get_span(), diff --git a/zokrates_ast/src/typed/result_folder.rs b/zokrates_ast/src/typed/result_folder.rs index d8541add0..dc3d38aff 100644 --- a/zokrates_ast/src/typed/result_folder.rs +++ b/zokrates_ast/src/typed/result_folder.rs @@ -1162,6 +1162,14 @@ pub fn fold_field_expression_cases<'ast, T: Field, F: ResultFolder<'ast, T>>( BinaryOrExpression::Binary(e) => Div(e), BinaryOrExpression::Expression(u) => u, }, + IDiv(e) => match f.fold_binary_expression(&Type::FieldElement, e)? { + BinaryOrExpression::Binary(e) => IDiv(e), + BinaryOrExpression::Expression(u) => u, + }, + Rem(e) => match f.fold_binary_expression(&Type::FieldElement, e)? { + BinaryOrExpression::Binary(e) => Rem(e), + BinaryOrExpression::Expression(u) => u, + }, Pow(e) => match f.fold_binary_expression(&Type::FieldElement, e)? { BinaryOrExpression::Binary(e) => Pow(e), BinaryOrExpression::Expression(u) => u, diff --git a/zokrates_ast/src/untyped/from_ast.rs b/zokrates_ast/src/untyped/from_ast.rs index ff37ce6ba..820424c8a 100644 --- a/zokrates_ast/src/untyped/from_ast.rs +++ b/zokrates_ast/src/untyped/from_ast.rs @@ -405,6 +405,10 @@ impl<'ast> From> for untyped::ExpressionNode<'ast> Box::new(untyped::ExpressionNode::from(*expression.left)), Box::new(untyped::ExpressionNode::from(*expression.right)), ), + pest::BinaryOperator::IDiv => untyped::Expression::IDiv( + Box::new(untyped::ExpressionNode::from(*expression.left)), + Box::new(untyped::ExpressionNode::from(*expression.right)), + ), pest::BinaryOperator::Rem => untyped::Expression::Rem( Box::new(untyped::ExpressionNode::from(*expression.left)), Box::new(untyped::ExpressionNode::from(*expression.right)), diff --git a/zokrates_ast/src/untyped/mod.rs b/zokrates_ast/src/untyped/mod.rs index c0f77ea64..26541a90a 100644 --- a/zokrates_ast/src/untyped/mod.rs +++ b/zokrates_ast/src/untyped/mod.rs @@ -610,6 +610,7 @@ pub enum Expression<'ast> { Sub(Box>, Box>), Mult(Box>, Box>), Div(Box>, Box>), + IDiv(Box>, Box>), Rem(Box>, Box>), Pow(Box>, Box>), Neg(Box>), @@ -658,6 +659,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::IDiv(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::Neg(ref e) => write!(f, "(-{})", e), diff --git a/zokrates_ast/src/zir/folder.rs b/zokrates_ast/src/zir/folder.rs index b536ccc78..3bd723251 100644 --- a/zokrates_ast/src/zir/folder.rs +++ b/zokrates_ast/src/zir/folder.rs @@ -464,6 +464,14 @@ pub fn fold_field_expression_cases<'ast, T: Field, F: Folder<'ast, T>>( BinaryOrExpression::Binary(e) => FieldElementExpression::Div(e), BinaryOrExpression::Expression(e) => e, }, + FieldElementExpression::IDiv(e) => match f.fold_binary_expression(&Type::FieldElement, e) { + BinaryOrExpression::Binary(e) => FieldElementExpression::IDiv(e), + BinaryOrExpression::Expression(e) => e, + }, + FieldElementExpression::Rem(e) => match f.fold_binary_expression(&Type::FieldElement, e) { + BinaryOrExpression::Binary(e) => FieldElementExpression::Rem(e), + BinaryOrExpression::Expression(e) => e, + }, FieldElementExpression::Pow(e) => match f.fold_binary_expression(&Type::FieldElement, e) { BinaryOrExpression::Binary(e) => FieldElementExpression::Pow(e), BinaryOrExpression::Expression(e) => e, diff --git a/zokrates_ast/src/zir/mod.rs b/zokrates_ast/src/zir/mod.rs index 5ae001bc1..aa01021d5 100644 --- a/zokrates_ast/src/zir/mod.rs +++ b/zokrates_ast/src/zir/mod.rs @@ -692,6 +692,8 @@ pub enum FieldElementExpression<'ast, T> { Sub(BinaryExpression), Mult(BinaryExpression), Div(BinaryExpression), + IDiv(BinaryExpression), + Rem(BinaryExpression), Pow(BinaryExpression, Self>), And(BinaryExpression), Or(BinaryExpression), @@ -706,6 +708,10 @@ impl<'ast, T> FieldElementExpression<'ast, T> { Self::Value(ValueExpression::new(n)) } + pub fn idiv(self, right: Self) -> Self { + Self::IDiv(BinaryExpression::new(self, right)) + } + pub fn pow(self, right: UExpression<'ast, T>) -> Self { Self::Pow(BinaryExpression::new(self, right)) } @@ -757,6 +763,14 @@ impl<'ast, T: Field> std::ops::BitXor for FieldElementExpression<'ast, T> { } } +impl<'ast, T: Field> std::ops::Rem for FieldElementExpression<'ast, T> { + type Output = Self; + + fn rem(self, other: Self) -> Self { + FieldElementExpression::Rem(BinaryExpression::new(self, other)) + } +} + /// An expression of type `bool` #[derive(Derivative)] #[derivative(PartialEq, Hash)] @@ -865,6 +879,8 @@ impl<'ast, T: fmt::Display> fmt::Display for FieldElementExpression<'ast, T> { FieldElementExpression::Sub(ref e) => write!(f, "{}", e), FieldElementExpression::Mult(ref e) => write!(f, "{}", e), FieldElementExpression::Div(ref e) => write!(f, "{}", e), + FieldElementExpression::IDiv(ref e) => write!(f, "{}", e), + FieldElementExpression::Rem(ref e) => write!(f, "{}", e), FieldElementExpression::Pow(ref e) => write!(f, "{}", e), FieldElementExpression::And(ref e) => write!(f, "{}", e), FieldElementExpression::Or(ref e) => write!(f, "{}", e), @@ -1318,6 +1334,8 @@ impl<'ast, T> WithSpan for FieldElementExpression<'ast, T> { Sub(e) => Sub(e.span(span)), Mult(e) => Mult(e.span(span)), Div(e) => Div(e.span(span)), + IDiv(e) => IDiv(e.span(span)), + Rem(e) => Rem(e.span(span)), Pow(e) => Pow(e.span(span)), And(e) => And(e.span(span)), Or(e) => Or(e.span(span)), @@ -1338,6 +1356,8 @@ impl<'ast, T> WithSpan for FieldElementExpression<'ast, T> { Sub(e) => e.get_span(), Mult(e) => e.get_span(), Div(e) => e.get_span(), + IDiv(e) => e.get_span(), + Rem(e) => e.get_span(), Pow(e) => e.get_span(), And(e) => e.get_span(), Or(e) => e.get_span(), diff --git a/zokrates_ast/src/zir/result_folder.rs b/zokrates_ast/src/zir/result_folder.rs index d368d972d..c956c093e 100644 --- a/zokrates_ast/src/zir/result_folder.rs +++ b/zokrates_ast/src/zir/result_folder.rs @@ -525,6 +525,18 @@ pub fn fold_field_expression_cases<'ast, T: Field, F: ResultFolder<'ast, T>>( BinaryOrExpression::Expression(e) => e, } } + FieldElementExpression::IDiv(e) => { + match f.fold_binary_expression(&Type::FieldElement, e)? { + BinaryOrExpression::Binary(e) => FieldElementExpression::IDiv(e), + BinaryOrExpression::Expression(e) => e, + } + } + FieldElementExpression::Rem(e) => { + match f.fold_binary_expression(&Type::FieldElement, e)? { + BinaryOrExpression::Binary(e) => FieldElementExpression::Rem(e), + BinaryOrExpression::Expression(e) => e, + } + } FieldElementExpression::Pow(e) => { match f.fold_binary_expression(&Type::FieldElement, e)? { BinaryOrExpression::Binary(e) => FieldElementExpression::Pow(e), diff --git a/zokrates_core/src/semantics.rs b/zokrates_core/src/semantics.rs index df226c3dd..4fd66d9dc 100644 --- a/zokrates_core/src/semantics.rs +++ b/zokrates_core/src/semantics.rs @@ -2616,7 +2616,6 @@ impl<'ast, T: Field> Checker<'ast, T> { } (t1, t2) => Err(ErrorInner { span: Some(span), - message: format!( "Cannot apply `/` to {}, {}", t1.get_type(), @@ -2625,6 +2624,33 @@ impl<'ast, T: Field> Checker<'ast, T> { }), } } + Expression::IDiv(e1, e2) => { + let e1_checked = self.check_expression(*e1, module_id, types)?; + let e2_checked = self.check_expression(*e2, module_id, types)?; + + use self::TypedExpression::*; + + let (e1_checked, e2_checked) = TypedExpression::align_without_integers( + e1_checked, e2_checked, + ) + .map_err(|(e1, e2)| ErrorInner { + span: Some(span), + message: format!("Cannot apply `\\` to {}, {}", e1.get_type(), e2.get_type()), + })?; + + match (e1_checked, e2_checked) { + (Int(e1), Int(e2)) => Ok(IntExpression::idiv(e1, e2).into()), + (FieldElement(e1), FieldElement(e2)) => Ok(FieldElementExpression::idiv(e1, e2).into()), + (t1, t2) => Err(ErrorInner { + span: Some(span), + message: format!( + "Cannot apply `\\` to {}, {}", + t1.get_type(), + t2.get_type() + ), + }), + } + } Expression::Rem(e1, e2) => { let e1_checked = self.check_expression(*e1, module_id, types)?; let e2_checked = self.check_expression(*e2, module_id, types)?; @@ -2638,6 +2664,12 @@ impl<'ast, T: Field> Checker<'ast, T> { })?; match (e1_checked, e2_checked) { + (TypedExpression::Int(e1), TypedExpression::Int(e2)) => { + Ok((e1 % e2).into()) + } + (TypedExpression::FieldElement(e1), TypedExpression::FieldElement(e2)) => { + Ok((e1 % e2).into()) + } (TypedExpression::Uint(e1), TypedExpression::Uint(e2)) if e1.get_type() == e2.get_type() => { diff --git a/zokrates_core_test/tests/tests/panics/deep_branch.zok b/zokrates_core_test/tests/tests/panics/deep_branch.zok index fca9bbe23..18735c6db 100644 --- a/zokrates_core_test/tests/tests/panics/deep_branch.zok +++ b/zokrates_core_test/tests/tests/panics/deep_branch.zok @@ -4,13 +4,13 @@ def check(bool[N] conditions, bool[N] expected) -> bool[3] { } def main(bool[3] conditions) -> bool[3] { - return conditions[0] ? \ - conditions[1] ? \ - conditions[2] ? check(conditions, [true, true, true]) : check(conditions, [true, true, false]) : \ - conditions[2] ? check(conditions, [true, false, true]) : \ - check(conditions, [true, false, false]) : \ - conditions[1] ? \ - conditions[2] ? check(conditions, [false, true, true]) : check(conditions, [false, true, false]) : \ - conditions[2] ? check(conditions, [false, false, true]) : \ + return conditions[0] ? + conditions[1] ? + conditions[2] ? check(conditions, [true, true, true]) : check(conditions, [true, true, false]) : + conditions[2] ? check(conditions, [true, false, true]) : + check(conditions, [true, false, false]) : + conditions[1] ? + conditions[2] ? check(conditions, [false, true, true]) : check(conditions, [false, true, false]) : + conditions[2] ? check(conditions, [false, false, true]) : check(conditions, [false, false, false]); } \ No newline at end of file diff --git a/zokrates_core_test/tests/tests/panics/panic_isolation.zok b/zokrates_core_test/tests/tests/panics/panic_isolation.zok index 276bcc58b..f5a9348d5 100644 --- a/zokrates_core_test/tests/tests/panics/panic_isolation.zok +++ b/zokrates_core_test/tests/tests/panics/panic_isolation.zok @@ -32,7 +32,7 @@ def main(bool condition, field[2] a, field x) -> (bool, field[2], field) { // first branch asserts that `condition` is true, second branch asserts that `condition` is false. This should never throw. // first branch asserts that all elements in `a` are 1, 2 in the second branch. This should throw only if `a` is neither ones or zeroes // first branch asserts that `x` is zero and returns it, second branch asserts that `x` isn't 0 and returns its inverse (which internally generates a failing assert if x is 0). This should never throw - return (condition ? yes(condition) : no(condition), \ - condition ? ones(a) : twos(a), \ + return (condition ? yes(condition) : no(condition), + condition ? ones(a) : twos(a), x == 0 ? zero(x) : inverse(x)); } \ No newline at end of file diff --git a/zokrates_parser/src/zokrates.pest b/zokrates_parser/src/zokrates.pest index af4998e09..5e29b0b9a 100644 --- a/zokrates_parser/src/zokrates.pest +++ b/zokrates_parser/src/zokrates.pest @@ -171,6 +171,7 @@ op_add = {"+"} op_sub = {"-"} op_mul = {"*"} op_div = {"/"} +op_idiv = @{"\\"} op_rem = {"%"} op_pow = @{"**"} op_not = {"!"} @@ -181,15 +182,15 @@ op_right_shift = @{">>"} op_ternary = {"?" ~ expression ~ ":"} // `op_pow` is *not* in `op_binary` because its precedence is handled in this parser rather than down the line in precedence climbing -op_binary = _ { 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_ternary } +op_binary = _ { 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_idiv | op_rem | op_ternary } op_unary = { op_pos | op_neg | op_not } -WHITESPACE = _{ " " | "\t" | "\\" | COMMENT | NEWLINE } +WHITESPACE = _{ " " | "\t" | COMMENT | NEWLINE } COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) } // the ordering of reserved keywords matters: if "as" is before "assert", then "assert" gets parsed as (as)(sert) and incorrectly // accepted keyword = @{ - "log"|"assert"|"as"|"bool"|"const"|"def"|"else"|"false"|"field"|"for"|"if"|"import"|"from"| + "log"|"assert"|"asm"|"as"|"bool"|"const"|"def"|"else"|"false"|"field"|"for"|"if"|"import"|"from"| "in"|"mut"|"private"|"public"|"return"|"struct"|"true"|"type"|"u8"|"u16"|"u32"|"u64" } diff --git a/zokrates_pest_ast/src/lib.rs b/zokrates_pest_ast/src/lib.rs index 2fca23ac2..5aed66a70 100644 --- a/zokrates_pest_ast/src/lib.rs +++ b/zokrates_pest_ast/src/lib.rs @@ -60,6 +60,7 @@ mod ast { 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_idiv, Assoc::Left) | Operator::new(Rule::op_rem, Assoc::Left), ]) } @@ -81,6 +82,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_idiv => Expression::binary(BinaryOperator::IDiv, lhs, rhs, span), Rule::op_rem => Expression::binary(BinaryOperator::Rem, lhs, rhs, span), Rule::op_equal => Expression::binary(BinaryOperator::Eq, lhs, rhs, span), Rule::op_not_equal => Expression::binary(BinaryOperator::NotEq, lhs, rhs, span), @@ -498,6 +500,7 @@ mod ast { Sub, Mul, Div, + IDiv, Rem, Eq, NotEq, diff --git a/zokrates_stdlib/stdlib/algebra/biginteger.zok b/zokrates_stdlib/stdlib/algebra/biginteger.zok new file mode 100644 index 000000000..3a8012102 --- /dev/null +++ b/zokrates_stdlib/stdlib/algebra/biginteger.zok @@ -0,0 +1,198 @@ +import "utils/pack/bool/unpack_unchecked" as unpack; +import "utils/casts/u32_to_field" as u32_to_field; +from "./biginteger_util" import split, split2, ilog2; + +const u32 LIMB_BITWIDTH = 32; + +struct BigInteger { + field[N] limbs; +} + +// Construct a big integer from `N` limbs +// Note: inheriting circuits need to constrain the limbs to be in the correct bit width +def bigint_from_limbs(field[N] limbs) -> BigInteger { + assert(M >= N, "invalid number of limbs"); + return BigInteger { limbs: [...limbs, ...[0; M-N]] }; +} + +// Construct a big integer from `N` u32 limbs +def bigint_from_u32_limbs(u32[N] limbs) -> BigInteger { + assert(M >= N, "invalid number of limbs"); + field[N] mut f = [0; N]; + for u32 i in 0..N { + f[i] = u32_to_field(limbs[i]); + } + return bigint_from_limbs(f); +} + +// Expand big integer from `N` limbs to `M` limbs +def bigint_expand(BigInteger input) -> BigInteger { + BigInteger r = bigint_from_limbs(input.limbs); + return r; +} + +// Big integer addition (a + b) +// The output has N+1 limbs where the Nth (0-indexed) limb is the carry value +def bigint_add(BigInteger a, BigInteger b) -> BigInteger { + assert(M == N + 1); + BigInteger mut c = bigint_from_limbs([0; N]); + field mut carry = 0; + for u32 i in 0..N { + field sum = a.limbs[i] + b.limbs[i] + carry; + bool[LIMB_BITWIDTH + 1] bits = unpack(sum); + carry = bits[0] ? 1 : 0; + c.limbs[i] = sum - carry * (1 << LIMB_BITWIDTH); + } + c.limbs[N] = carry; + return c; +} + +// Big integer addition (a + b) +// Note: last carry is ignored +def bigint_add_no_carry(BigInteger a, BigInteger b) -> BigInteger { + BigInteger mut c = bigint_from_limbs([0; N]); + field mut carry = 0; + for u32 i in 0..N { + field sum = a.limbs[i] + b.limbs[i] + carry; + bool[LIMB_BITWIDTH + 1] bits = unpack(sum); + carry = bits[0] ? 1 : 0; + c.limbs[i] = sum - carry * (1 << LIMB_BITWIDTH); + } + return c; +} + +// Big integer subtraction (a - b) +// Note: assumes a >= b +def bigint_sub(BigInteger a, BigInteger b) -> BigInteger { + BigInteger mut c = bigint_from_limbs([0; N]); + field mut borrow = 0; + for u32 i in 0..N { + c.limbs[i] = borrow * (1 << LIMB_BITWIDTH) + (a.limbs[i] - b.limbs[i]); + bool[LIMB_BITWIDTH + 1] bits = unpack(a.limbs[i] + (1 << LIMB_BITWIDTH) - b.limbs[i]); + borrow = 1 - (bits[0] ? 1 : 0); + } + return c; +} + +// Big integer multiplication (a * b) +def bigint_mul(BigInteger a, BigInteger b) -> BigInteger { + assert(M == 2 * N); + + u32 K = M - 1; + field[K] mut ovf = [0; K]; + for u32 i in 0..N { + for u32 j in 0..N { + ovf[i + j] = ovf[i + j] + (a.limbs[i] * b.limbs[j]); + } + } + + field[K] mut a_poly = [0; K]; + field[K] mut b_poly = [0; K]; + field[K] mut c_poly = [0; K]; + + for u32 i in 0..K { + field i_field = u32_to_field(i); + for u32 j in 0..K { + c_poly[i] = c_poly[i] + ovf[j] * (i_field ** j); + } + for u32 j in 0..N { + a_poly[i] = a_poly[i] + a.limbs[j] * (i_field ** j); + b_poly[i] = b_poly[i] + b.limbs[j] * (i_field ** j); + } + assert(c_poly[i] == a_poly[i] * b_poly[i]); + } + + field[K][3] mut split = [[0; 3]; K]; + for u32 i in 0..K { + asm { + split[i] <-- [ + (ovf[i] % (1 << LIMB_BITWIDTH)), + (ovf[i] \ (1 << LIMB_BITWIDTH)) % (1 << LIMB_BITWIDTH), + (ovf[i] \ (1 << LIMB_BITWIDTH * 2)) % (1 << LIMB_BITWIDTH) + ]; + } + } + + field[K] mut acc = [0; K]; + field[K] mut carry = [0; K]; + + acc[0] = split[0][0]; + acc[1] = K == 1 ? split[0][1] : acc[1]; + + field[2] sum_and_carry = K > 1 ? split::(split[0][1] + split[1][0]) : [acc[1], carry[1]]; + acc[1] = sum_and_carry[0]; + carry[1] = sum_and_carry[1]; + + acc[2] = K == 2 ? split[1][1] + split[0][2] + carry[1] : acc[2]; + (field[K], field[K]) tmp = K > 2 ? split2::(acc, carry, split) : (acc, carry); + + BigInteger mut out = bigint_from_limbs(tmp.0); + carry = tmp.1; + out.limbs[K] = split[K-1][1] + split[K-2][2] + carry[K-1]; + + // out range check + for u32 i in 0..M { + assert(out.limbs[i] < (1 << LIMB_BITWIDTH)); + } + + // running carry range checks + field[K] mut running_carry = [0; K]; + asm { + running_carry[0] <-- (ovf[0] - out.limbs[0]) / (1 << LIMB_BITWIDTH); + running_carry[0] * (1 << LIMB_BITWIDTH) === ovf[0] - out.limbs[0]; + } + + u32 logk = ilog2(K); + assert(running_carry[0] < (1 << (LIMB_BITWIDTH + logk))); + + for u32 i in 1..K { + asm { + running_carry[i] <-- (ovf[i] - out.limbs[i] + running_carry[i-1]) / (1 << LIMB_BITWIDTH); + running_carry[i] * (1 << LIMB_BITWIDTH) === ovf[i] - out.limbs[i] + running_carry[i-1]; + } + assert(running_carry[i] < (1 << (LIMB_BITWIDTH + logk))); + } + + assert(running_carry[K - 1] == out.limbs[K]); + return out; +} + +// Right shift by `n` limbs +def bigint_rshift_limb(BigInteger mut a, u32 n) -> BigInteger { + assert(n < N); + for u32 i in 0..N-n { + a.limbs[i] = a.limbs[i + n]; + } + for u32 i in N-n..N { + a.limbs[i] = 0; + } + return a; +} + +// Left shift by `n` limbs +def bigint_lshift_limb(BigInteger mut a, u32 n) -> BigInteger { + assert(n < N); + for u32 i in 0..N-n { + u32 j = N - i - 1; + a.limbs[j] = a.limbs[j - n]; + } + for u32 i in 0..n { + a.limbs[i] = 0; + } + return a; +} + +// Check equality of two big integers +def bigint_eq(BigInteger a, BigInteger b) -> bool { + bool mut eq = true; + for u32 i in 0..N { + eq = eq && (a.limbs[i] == b.limbs[i]); + } + return eq; +} + +// Check if a big integer is equal to 0 +def bigint_is_zero(BigInteger a) -> bool { + BigInteger zero = bigint_from_limbs([0; N]); + return bigint_eq(a, zero); +} \ No newline at end of file diff --git a/zokrates_stdlib/stdlib/algebra/biginteger_util.zok b/zokrates_stdlib/stdlib/algebra/biginteger_util.zok new file mode 100644 index 000000000..371eca6d2 --- /dev/null +++ b/zokrates_stdlib/stdlib/algebra/biginteger_util.zok @@ -0,0 +1,37 @@ +// Count leading zeroes using de Bruijn sequence +def clz32(u32 mut x) -> u32 { + u32[32] debruijn32 = [ + 0, 31, 9, 30, 3, 8, 13, 29, 2, 5, 7, 21, 12, 24, 28, 19, + 1, 10, 4, 14, 6, 22, 25, 20, 11, 15, 23, 26, 16, 27, 17, 18 + ]; + x = x | x >> 1; + x = x | x >> 2; + x = x | x >> 4; + x = x | x >> 8; + x = x | x >> 16; + x = x == 0 ? 32 : debruijn32[(x + 1) * 0x076be629 >> 27]; + return x; +} + +// Integer base-2 logarithm +def ilog2(u32 x) -> u32 { + return 32 - clz32(x); +} + +def split(field input) -> field[2] { + field[2] mut res = [0; 2]; + asm { + res <-- [input % (1 << N), (input \ (1 << N)) % (1 << N)]; + } + return res; +} + +def split2(field[K] mut limbs, field[K] mut carry, field[K][3] split) -> (field[K], field[K]) { + assert(K > 2); + for u32 i in 2..K { + field[2] sum_and_carry = split::(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1]); + limbs[i] = sum_and_carry[0]; + carry[i] = sum_and_carry[1]; + } + return (limbs, carry); +} \ No newline at end of file diff --git a/zokrates_stdlib/stdlib/hashes/blake2/blake2s_p.zok b/zokrates_stdlib/stdlib/hashes/blake2/blake2s_p.zok index 628566aa6..1b0689417 100644 --- a/zokrates_stdlib/stdlib/hashes/blake2/blake2s_p.zok +++ b/zokrates_stdlib/stdlib/hashes/blake2/blake2s_p.zok @@ -30,9 +30,9 @@ def rotr32(u32 x) -> u32 { // change endianness def swap_u32(u32 val) -> u32 { - return (val << 24) | \ - ((val << 8) & 0x00ff0000) | \ - ((val >> 8) & 0x0000ff00) | \ + return (val << 24) | + ((val << 8) & 0x00ff0000) | + ((val >> 8) & 0x0000ff00) | ((val >> 24) & 0x000000ff); } diff --git a/zokrates_stdlib/stdlib/hashes/sha256/1024bitPadded.zok b/zokrates_stdlib/stdlib/hashes/sha256/1024bitPadded.zok index b2e50eb0d..5d8b79541 100644 --- a/zokrates_stdlib/stdlib/hashes/sha256/1024bitPadded.zok +++ b/zokrates_stdlib/stdlib/hashes/sha256/1024bitPadded.zok @@ -7,7 +7,7 @@ def main(u32[8] a, u32[8] b, u32[8] c, u32[8] d) -> u32[8] { // Hash is computed on the full 1024bit block size // padding does not fit in the first two blocks // add dummy block (single "1" followed by "0" + total length) - u32[8] dummyblock1 = [ \ + u32[8] dummyblock1 = [ 0x80000000, 0x00000000, 0x00000000, @@ -18,7 +18,7 @@ def main(u32[8] a, u32[8] b, u32[8] c, u32[8] d) -> u32[8] { 0x00000000 ]; - u32[8] dummyblock2 = [ \ + u32[8] dummyblock2 = [ 0x00000000, 0x00000000, 0x00000000, diff --git a/zokrates_stdlib/stdlib/hashes/sha256/256bitPadded.zok b/zokrates_stdlib/stdlib/hashes/sha256/256bitPadded.zok index e7fca109b..471563d23 100644 --- a/zokrates_stdlib/stdlib/hashes/sha256/256bitPadded.zok +++ b/zokrates_stdlib/stdlib/hashes/sha256/256bitPadded.zok @@ -7,7 +7,7 @@ def main(u32[8] a) -> u32[8] { // Hash is computed on 256 bits of input // padding fits in the remaining 256 bits of the first block // add dummy block (single "1" followed by "0" + total length) - u32[8] dummyblock1 = [ \ + u32[8] dummyblock1 = [ 0x80000000, 0x00000000, 0x00000000, diff --git a/zokrates_stdlib/stdlib/utils/pack/bool/unpack.zok b/zokrates_stdlib/stdlib/utils/pack/bool/unpack.zok index dbfba7b71..e810b5842 100644 --- a/zokrates_stdlib/stdlib/utils/pack/bool/unpack.zok +++ b/zokrates_stdlib/stdlib/utils/pack/bool/unpack.zok @@ -5,8 +5,8 @@ from "EMBED" import bit_array_le; // Unpack a field element as N big endian bits def main(field i) -> bool[N] { bool[N] res = unpack_unchecked(i); - assert(N >= FIELD_SIZE_IN_BITS \ - ? bit_array_le(res, [...[false; N - FIELD_SIZE_IN_BITS], ...unpack_unchecked::(-1)]) \ + assert(N >= FIELD_SIZE_IN_BITS + ? bit_array_le(res, [...[false; N - FIELD_SIZE_IN_BITS], ...unpack_unchecked::(-1)]) : true); return res; } \ No newline at end of file diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_add.json b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_add.json new file mode 100644 index 000000000..a73aec5ab --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_add.json @@ -0,0 +1,49 @@ +{ + "max_constraint_count": 71, + "curves": ["Bn128"], + "tests": [ + { + "input": { + "values": [{ "limbs": ["1", "2"] }, { "limbs": ["3", "4"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["4", "6", "0"] } + } + } + }, + { + "input": { + "values": [{ "limbs": ["4294967295", "0"] }, { "limbs": ["1", "0"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["0", "1", "0"] } + } + } + }, + { + "input": { + "values": [{ "limbs": ["0", "4294967295"] }, { "limbs": ["0", "1"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["0", "0", "1"] } + } + } + }, + { + "input": { + "values": [ + { "limbs": ["4294967295", "4294967295"] }, + { "limbs": ["1", "1"] } + ] + }, + "output": { + "Ok": { + "value": { "limbs": ["0", "1", "1"] } + } + } + } + ] +} diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_add.zok b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_add.zok new file mode 100644 index 000000000..56e0776b3 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_add.zok @@ -0,0 +1,6 @@ +from "algebra/biginteger" import BigInteger, bigint_add; + +def main(BigInteger<2> a, BigInteger<2> b) -> BigInteger<3> { + BigInteger<3> c = bigint_add::<2, 3>(a, b); + return c; +} \ No newline at end of file diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_eq.json b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_eq.json new file mode 100644 index 000000000..1f281731b --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_eq.json @@ -0,0 +1,36 @@ +{ + "max_constraint_count": 6, + "curves": ["Bn128"], + "tests": [ + { + "input": { + "values": [{ "limbs": ["2", "2"] }, { "limbs": ["2", "0"] }] + }, + "output": { + "Ok": { + "value": false + } + } + }, + { + "input": { + "values": [{ "limbs": ["2", "2"] }, { "limbs": ["0", "2"] }] + }, + "output": { + "Ok": { + "value": false + } + } + }, + { + "input": { + "values": [{ "limbs": ["2", "2"] }, { "limbs": ["2", "2"] }] + }, + "output": { + "Ok": { + "value": true + } + } + } + ] +} diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_eq.zok b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_eq.zok new file mode 100644 index 000000000..c22bec26a --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_eq.zok @@ -0,0 +1,5 @@ +from "algebra/biginteger" import BigInteger, bigint_eq; + +def main(BigInteger<2> a, BigInteger<2> b) -> bool { + return bigint_eq(a, b); +} \ No newline at end of file diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_expand.json b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_expand.json new file mode 100644 index 000000000..03cea865e --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_expand.json @@ -0,0 +1,16 @@ +{ + "max_constraint_count": 4, + "curves": ["Bn128"], + "tests": [ + { + "input": { + "values": [{ "limbs": ["2", "2"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["2", "2", "0", "0"] } + } + } + } + ] +} diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_expand.zok b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_expand.zok new file mode 100644 index 000000000..b30ca5072 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_expand.zok @@ -0,0 +1,6 @@ +from "algebra/biginteger" import BigInteger, bigint_expand; + +def main(BigInteger<2> a) -> BigInteger<4> { + BigInteger<4> b = bigint_expand(a); + return b; +} \ No newline at end of file diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_is_zero.json b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_is_zero.json new file mode 100644 index 000000000..064f579c9 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_is_zero.json @@ -0,0 +1,36 @@ +{ + "max_constraint_count": 6, + "curves": ["Bn128"], + "tests": [ + { + "input": { + "values": [{ "limbs": ["0", "2"] }] + }, + "output": { + "Ok": { + "value": false + } + } + }, + { + "input": { + "values": [{ "limbs": ["2", "0"] }] + }, + "output": { + "Ok": { + "value": false + } + } + }, + { + "input": { + "values": [{ "limbs": ["0", "0"] }] + }, + "output": { + "Ok": { + "value": true + } + } + } + ] +} diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_is_zero.zok b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_is_zero.zok new file mode 100644 index 000000000..64a732e1d --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_is_zero.zok @@ -0,0 +1,5 @@ +from "algebra/biginteger" import BigInteger, bigint_is_zero; + +def main(BigInteger<2> a) -> bool { + return bigint_is_zero(a); +} \ No newline at end of file diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_lshift_limb.json b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_lshift_limb.json new file mode 100644 index 000000000..da4be56e5 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_lshift_limb.json @@ -0,0 +1,16 @@ +{ + "max_constraint_count": 22, + "curves": ["Bn128"], + "tests": [ + { + "input": { + "values": [{ "limbs": ["2", "2"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["0", "2"] } + } + } + } + ] +} diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_lshift_limb.zok b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_lshift_limb.zok new file mode 100644 index 000000000..f0e733be1 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_lshift_limb.zok @@ -0,0 +1,6 @@ +from "algebra/biginteger" import BigInteger, bigint_lshift_limb; + +def main(BigInteger<2> a) -> BigInteger<2> { + BigInteger<2> c = bigint_lshift_limb(a, 1); + return c; +} \ No newline at end of file diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_mul.json b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_mul.json new file mode 100644 index 000000000..3ca191d37 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_mul.json @@ -0,0 +1,49 @@ +{ + "max_constraint_count": 245, + "curves": ["Bn128"], + "tests": [ + { + "input": { + "values": [{ "limbs": ["1", "2"] }, { "limbs": ["3", "4"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["3", "10", "8", "0"] } + } + } + }, + { + "input": { + "values": [{ "limbs": ["128", "0"] }, { "limbs": ["0", "2"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["0", "256", "0", "0"] } + } + } + }, + { + "input": { + "values": [{ "limbs": ["4294967295", "0"] }, { "limbs": ["2", "0"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["4294967294", "1", "0", "0"] } + } + } + }, + { + "input": { + "values": [ + { "limbs": ["4294967295", "4294967295"] }, + { "limbs": ["2", "2"] } + ] + }, + "output": { + "Ok": { + "value": { "limbs": ["4294967294", "4294967293", "1", "2"] } + } + } + } + ] +} diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_mul.zok b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_mul.zok new file mode 100644 index 000000000..6cc0d1ce9 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_mul.zok @@ -0,0 +1,6 @@ +from "algebra/biginteger" import BigInteger, bigint_mul; + +def main(BigInteger<2> a, BigInteger<2> b) -> BigInteger<4> { + BigInteger<4> c = bigint_mul::<2, 4>(a, b); + return c; +} \ No newline at end of file diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_rshift_limb.json b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_rshift_limb.json new file mode 100644 index 000000000..e10c37678 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_rshift_limb.json @@ -0,0 +1,16 @@ +{ + "max_constraint_count": 22, + "curves": ["Bn128"], + "tests": [ + { + "input": { + "values": [{ "limbs": ["2", "2"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["2", "0"] } + } + } + } + ] +} diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_rshift_limb.zok b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_rshift_limb.zok new file mode 100644 index 000000000..3d6abf902 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_rshift_limb.zok @@ -0,0 +1,6 @@ +from "algebra/biginteger" import BigInteger, bigint_rshift_limb; + +def main(BigInteger<2> a) -> BigInteger<2> { + BigInteger<2> c = bigint_rshift_limb(a, 1); + return c; +} \ No newline at end of file diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_sub.json b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_sub.json new file mode 100644 index 000000000..c76c71509 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_sub.json @@ -0,0 +1,26 @@ +{ + "max_constraint_count": 36, + "curves": ["Bn128"], + "tests": [ + { + "input": { + "values": [{ "limbs": ["3", "4"] }, { "limbs": ["1", "2"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["2", "2"] } + } + } + }, + { + "input": { + "values": [{ "limbs": ["255", "0"] }, { "limbs": ["1", "0"] }] + }, + "output": { + "Ok": { + "value": { "limbs": ["254", "0"] } + } + } + } + ] +} diff --git a/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_sub.zok b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_sub.zok new file mode 100644 index 000000000..ea0c3afa1 --- /dev/null +++ b/zokrates_stdlib/tests/tests/algebra/biginteger/bigint_sub.zok @@ -0,0 +1,6 @@ +from "algebra/biginteger" import BigInteger, bigint_sub; + +def main(BigInteger<2> a, BigInteger<2> b) -> BigInteger<2> { + BigInteger<2> c = bigint_sub::<2>(a, b); + return c; +} \ No newline at end of file