test field comparators

This commit is contained in:
collin 2020-06-02 16:56:11 -07:00
parent 82ba9560be
commit c8229cef18
14 changed files with 206 additions and 69 deletions

View File

@ -90,10 +90,10 @@ pub enum BinaryOperator {
Or, Or,
And, And,
Eq, Eq,
Neq, Ne,
Geq, Ge,
Gt, Gt,
Leq, Le,
Lt, Lt,
Add, Add,
Sub, Sub,
@ -812,10 +812,10 @@ fn precedence_climber() -> PrecClimber<Rule> {
Operator::new(Rule::operation_or, Assoc::Left), Operator::new(Rule::operation_or, Assoc::Left),
Operator::new(Rule::operation_and, Assoc::Left), Operator::new(Rule::operation_and, Assoc::Left),
Operator::new(Rule::operation_eq, Assoc::Left) Operator::new(Rule::operation_eq, Assoc::Left)
| Operator::new(Rule::operation_neq, Assoc::Left), | Operator::new(Rule::operation_ne, Assoc::Left),
Operator::new(Rule::operation_geq, Assoc::Left) Operator::new(Rule::operation_ge, Assoc::Left)
| Operator::new(Rule::operation_gt, Assoc::Left) | Operator::new(Rule::operation_gt, Assoc::Left)
| Operator::new(Rule::operation_leq, Assoc::Left) | Operator::new(Rule::operation_le, Assoc::Left)
| Operator::new(Rule::operation_lt, Assoc::Left), | Operator::new(Rule::operation_lt, Assoc::Left),
Operator::new(Rule::operation_add, Assoc::Left) Operator::new(Rule::operation_add, Assoc::Left)
| Operator::new(Rule::operation_sub, Assoc::Left), | Operator::new(Rule::operation_sub, Assoc::Left),
@ -927,10 +927,10 @@ fn binary_expression<'ast>(
Rule::operation_or => Expression::binary(BinaryOperator::Or, lhs, rhs, span), Rule::operation_or => Expression::binary(BinaryOperator::Or, lhs, rhs, span),
Rule::operation_and => Expression::binary(BinaryOperator::And, lhs, rhs, span), Rule::operation_and => Expression::binary(BinaryOperator::And, lhs, rhs, span),
Rule::operation_eq => Expression::binary(BinaryOperator::Eq, lhs, rhs, span), Rule::operation_eq => Expression::binary(BinaryOperator::Eq, lhs, rhs, span),
Rule::operation_neq => Expression::binary(BinaryOperator::Neq, lhs, rhs, span), Rule::operation_ne => Expression::binary(BinaryOperator::Ne, lhs, rhs, span),
Rule::operation_geq => Expression::binary(BinaryOperator::Geq, lhs, rhs, span), Rule::operation_ge => Expression::binary(BinaryOperator::Ge, lhs, rhs, span),
Rule::operation_gt => Expression::binary(BinaryOperator::Gt, lhs, rhs, span), Rule::operation_gt => Expression::binary(BinaryOperator::Gt, lhs, rhs, span),
Rule::operation_leq => Expression::binary(BinaryOperator::Leq, lhs, rhs, span), Rule::operation_le => Expression::binary(BinaryOperator::Le, lhs, rhs, span),
Rule::operation_lt => Expression::binary(BinaryOperator::Lt, lhs, rhs, span), Rule::operation_lt => Expression::binary(BinaryOperator::Lt, lhs, rhs, span),
Rule::operation_add => Expression::binary(BinaryOperator::Add, lhs, rhs, span), Rule::operation_add => Expression::binary(BinaryOperator::Add, lhs, rhs, span),
Rule::operation_sub => Expression::binary(BinaryOperator::Sub, lhs, rhs, span), Rule::operation_sub => Expression::binary(BinaryOperator::Sub, lhs, rhs, span),

View File

@ -230,22 +230,23 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
} }
} }
fn evaluate_geq_expression( fn evaluate_ge_expression(
&mut self, &mut self,
left: ConstrainedValue<F, G>, left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>, right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> { ) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match (left, right) { match (left, right) {
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => { (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => {
// Self::field_geq(fe_1, fe_2) let result = fe_1.ge(&fe_2);
// } Ok(ConstrainedValue::Boolean(Boolean::Constant(result)))
}
(ConstrainedValue::Unresolved(string), val_2) => { (ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?; let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_geq_expression(val_1, val_2) self.evaluate_ge_expression(val_1, val_2)
} }
(val_1, ConstrainedValue::Unresolved(string)) => { (val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?; let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.evaluate_geq_expression(val_1, val_2) self.evaluate_ge_expression(val_1, val_2)
} }
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!( (val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} >= {}, values must be fields", "{} >= {}, values must be fields",
@ -260,9 +261,10 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
right: ConstrainedValue<F, G>, right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> { ) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match (left, right) { match (left, right) {
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => { (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => {
// Self::field_gt(fe_1, fe_2) let result = fe_1.gt(&fe_2);
// } Ok(ConstrainedValue::Boolean(Boolean::Constant(result)))
}
(ConstrainedValue::Unresolved(string), val_2) => { (ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?; let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_gt_expression(val_1, val_2) self.evaluate_gt_expression(val_1, val_2)
@ -278,22 +280,23 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
} }
} }
fn evaluate_leq_expression( fn evaluate_le_expression(
&mut self, &mut self,
left: ConstrainedValue<F, G>, left: ConstrainedValue<F, G>,
right: ConstrainedValue<F, G>, right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> { ) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match (left, right) { match (left, right) {
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => { (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => {
// Self::field_leq(fe_1, fe_2) let result = fe_1.le(&fe_2);
// } Ok(ConstrainedValue::Boolean(Boolean::Constant(result)))
}
(ConstrainedValue::Unresolved(string), val_2) => { (ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?; let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_leq_expression(val_1, val_2) self.evaluate_le_expression(val_1, val_2)
} }
(val_1, ConstrainedValue::Unresolved(string)) => { (val_1, ConstrainedValue::Unresolved(string)) => {
let val_2 = ConstrainedValue::from_other(string, &val_1)?; let val_2 = ConstrainedValue::from_other(string, &val_1)?;
self.evaluate_leq_expression(val_1, val_2) self.evaluate_le_expression(val_1, val_2)
} }
(val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!( (val_1, val_2) => Err(ExpressionError::IncompatibleTypes(format!(
"{} <= {}, values must be fields", "{} <= {}, values must be fields",
@ -308,9 +311,10 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
right: ConstrainedValue<F, G>, right: ConstrainedValue<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> { ) -> Result<ConstrainedValue<F, G>, ExpressionError> {
match (left, right) { match (left, right) {
// (ResolvedValue::FieldElement(fe_1), ResolvedValue::FieldElement(fe_2)) => { (ConstrainedValue::Field(fe_1), ConstrainedValue::Field(fe_2)) => {
// Self::field_lt(fe_1, fe_2) let result = fe_1.lt(&fe_2);
// } Ok(ConstrainedValue::Boolean(Boolean::Constant(result)))
}
(ConstrainedValue::Unresolved(string), val_2) => { (ConstrainedValue::Unresolved(string), val_2) => {
let val_1 = ConstrainedValue::from_other(string, &val_2)?; let val_1 = ConstrainedValue::from_other(string, &val_2)?;
self.evaluate_lt_expression(val_1, val_2) self.evaluate_lt_expression(val_1, val_2)
@ -930,7 +934,7 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
Ok(self.evaluate_eq_expression(resolved_left, resolved_right)?) Ok(self.evaluate_eq_expression(resolved_left, resolved_right)?)
} }
Expression::Geq(left, right) => { Expression::Ge(left, right) => {
let (resolved_left, resolved_right) = self.enforce_binary_expression( let (resolved_left, resolved_right) = self.enforce_binary_expression(
cs, cs,
file_scope.clone(), file_scope.clone(),
@ -940,7 +944,7 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
*right, *right,
)?; )?;
Ok(self.evaluate_geq_expression(resolved_left, resolved_right)?) Ok(self.evaluate_ge_expression(resolved_left, resolved_right)?)
} }
Expression::Gt(left, right) => { Expression::Gt(left, right) => {
let (resolved_left, resolved_right) = self.enforce_binary_expression( let (resolved_left, resolved_right) = self.enforce_binary_expression(
@ -954,7 +958,7 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
Ok(self.evaluate_gt_expression(resolved_left, resolved_right)?) Ok(self.evaluate_gt_expression(resolved_left, resolved_right)?)
} }
Expression::Leq(left, right) => { Expression::Le(left, right) => {
let (resolved_left, resolved_right) = self.enforce_binary_expression( let (resolved_left, resolved_right) = self.enforce_binary_expression(
cs, cs,
file_scope.clone(), file_scope.clone(),
@ -964,7 +968,7 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
*right, *right,
)?; )?;
Ok(self.evaluate_leq_expression(resolved_left, resolved_right)?) Ok(self.evaluate_le_expression(resolved_left, resolved_right)?)
} }
Expression::Lt(left, right) => { Expression::Lt(left, right) => {
let (resolved_left, resolved_right) = self.enforce_binary_expression( let (resolved_left, resolved_right) = self.enforce_binary_expression(

View File

@ -433,10 +433,6 @@ impl<F: Field + PrimeField, G: GroupType<F>, CS: ConstraintSystem<F>> Constraine
val_1, val_1,
val_2 val_2
) )
// return Err(StatementError::AssertEq(
// val_1.to_string(),
// val_2.to_string(),
// ))
} }
}) })
} }

View File

@ -20,6 +20,7 @@ use snarkos_models::{
}, },
}; };
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::Ordering;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum FieldType<F: Field + PrimeField> { pub enum FieldType<F: Field + PrimeField> {
@ -185,6 +186,15 @@ impl<F: Field + PrimeField> PartialEq for FieldType<F> {
impl<F: Field + PrimeField> Eq for FieldType<F> {} impl<F: Field + PrimeField> Eq for FieldType<F> {}
impl<F: Field + PrimeField> PartialOrd for FieldType<F> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let self_value = self.get_value();
let other_value = other.get_value();
Option::from(self_value.cmp(&other_value))
}
}
impl<F: Field + PrimeField> EqGadget<F> for FieldType<F> {} impl<F: Field + PrimeField> EqGadget<F> for FieldType<F> {}
impl<F: Field + PrimeField> ConditionalEqGadget<F> for FieldType<F> { impl<F: Field + PrimeField> ConditionalEqGadget<F> for FieldType<F> {

View File

@ -25,11 +25,11 @@ operation_and = { "&&" }
operation_or = { "||" } operation_or = { "||" }
operation_eq = { "==" } operation_eq = { "==" }
operation_neq = { "!=" } operation_ne = { "!=" }
operation_geq = { ">=" } operation_ge = { ">=" }
operation_gt = { ">" } operation_gt = { ">" }
operation_leq = { "<=" } operation_le = { "<=" }
operation_lt = { "<" } operation_lt = { "<" }
operation_add = { "+" } operation_add = { "+" }
@ -39,9 +39,9 @@ operation_div = { "/" }
operation_pow = { "**" } operation_pow = { "**" }
operation_compare = _{ operation_compare = _{
operation_eq | operation_neq | operation_eq | operation_ne |
operation_geq | operation_gt | operation_ge | operation_gt |
operation_leq | operation_lt operation_le | operation_lt
} }
operation_binary = _{ operation_binary = _{

View File

@ -82,9 +82,9 @@ pub enum Expression {
Or(Box<Expression>, Box<Expression>), Or(Box<Expression>, Box<Expression>),
And(Box<Expression>, Box<Expression>), And(Box<Expression>, Box<Expression>),
Eq(Box<Expression>, Box<Expression>), Eq(Box<Expression>, Box<Expression>),
Geq(Box<Expression>, Box<Expression>), Ge(Box<Expression>, Box<Expression>),
Gt(Box<Expression>, Box<Expression>), Gt(Box<Expression>, Box<Expression>),
Leq(Box<Expression>, Box<Expression>), Le(Box<Expression>, Box<Expression>),
Lt(Box<Expression>, Box<Expression>), Lt(Box<Expression>, Box<Expression>),
// Conditionals // Conditionals

View File

@ -93,9 +93,9 @@ impl<'ast> fmt::Display for Expression {
Expression::Or(ref lhs, ref rhs) => write!(f, "{} || {}", lhs, rhs), Expression::Or(ref lhs, ref rhs) => write!(f, "{} || {}", lhs, rhs),
Expression::And(ref lhs, ref rhs) => write!(f, "{} && {}", lhs, rhs), Expression::And(ref lhs, ref rhs) => write!(f, "{} && {}", lhs, rhs),
Expression::Eq(ref lhs, ref rhs) => write!(f, "{} == {}", lhs, rhs), Expression::Eq(ref lhs, ref rhs) => write!(f, "{} == {}", lhs, rhs),
Expression::Geq(ref lhs, ref rhs) => write!(f, "{} >= {}", lhs, rhs), Expression::Ge(ref lhs, ref rhs) => write!(f, "{} >= {}", lhs, rhs),
Expression::Gt(ref lhs, ref rhs) => write!(f, "{} > {}", lhs, rhs), Expression::Gt(ref lhs, ref rhs) => write!(f, "{} > {}", lhs, rhs),
Expression::Leq(ref lhs, ref rhs) => write!(f, "{} <= {}", lhs, rhs), Expression::Le(ref lhs, ref rhs) => write!(f, "{} <= {}", lhs, rhs),
Expression::Lt(ref lhs, ref rhs) => write!(f, "{} < {}", lhs, rhs), Expression::Lt(ref lhs, ref rhs) => write!(f, "{} < {}", lhs, rhs),
// Conditionals // Conditionals

View File

@ -191,10 +191,10 @@ impl<'ast> From<ast::BinaryExpression<'ast>> for types::Expression {
Box::new(types::Expression::from(*expression.left)), Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)), Box::new(types::Expression::from(*expression.right)),
), ),
ast::BinaryOperator::Neq => { ast::BinaryOperator::Ne => {
types::Expression::Not(Box::new(types::Expression::from(expression))) types::Expression::Not(Box::new(types::Expression::from(expression)))
} }
ast::BinaryOperator::Geq => types::Expression::Geq( ast::BinaryOperator::Ge => types::Expression::Ge(
Box::new(types::Expression::from(*expression.left)), Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)), Box::new(types::Expression::from(*expression.right)),
), ),
@ -202,7 +202,7 @@ impl<'ast> From<ast::BinaryExpression<'ast>> for types::Expression {
Box::new(types::Expression::from(*expression.left)), Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)), Box::new(types::Expression::from(*expression.right)),
), ),
ast::BinaryOperator::Leq => types::Expression::Leq( ast::BinaryOperator::Le => types::Expression::Le(
Box::new(types::Expression::from(*expression.left)), Box::new(types::Expression::from(*expression.left)),
Box::new(types::Expression::from(*expression.right)), Box::new(types::Expression::from(*expression.right)),
), ),

View File

@ -8,22 +8,21 @@ use snarkos_models::gadgets::utilities::boolean::Boolean;
const DIRECTORY_NAME: &str = "tests/boolean/"; const DIRECTORY_NAME: &str = "tests/boolean/";
pub fn output_true(program: EdwardsTestCompiler) { pub fn output_expected_boolean(program: EdwardsTestCompiler, boolean: bool) {
let output = get_output(program); let output = get_output(program);
assert_eq!( assert_eq!(
EdwardsConstrainedValue::Return(vec![ConstrainedValue::Boolean(Boolean::Constant(true))]) EdwardsConstrainedValue::Return(vec![ConstrainedValue::Boolean(Boolean::Constant(boolean))])
.to_string(), .to_string(),
output.to_string() output.to_string()
); );
} }
pub fn output_true(program: EdwardsTestCompiler) {
output_expected_boolean(program, true)
}
pub fn output_false(program: EdwardsTestCompiler) { pub fn output_false(program: EdwardsTestCompiler) {
let output = get_output(program); output_expected_boolean(program, false)
assert_eq!(
EdwardsConstrainedValue::Return(vec![ConstrainedValue::Boolean(Boolean::Constant(false))])
.to_string(),
output.to_string()
);
} }
fn fail_evaluate(program: EdwardsTestCompiler) { fn fail_evaluate(program: EdwardsTestCompiler) {

View File

@ -0,0 +1,3 @@
function main(a: field, b: field) -> bool {
return a >= b
}

View File

@ -0,0 +1,3 @@
function main(a: field, b: field) -> bool {
return a > b
}

View File

@ -0,0 +1,3 @@
function main(a: field, b: field) -> bool {
return a <= b
}

View File

@ -0,0 +1,3 @@
function main(a: field, b: field) -> bool {
return a < b
}

View File

@ -1,4 +1,4 @@
use crate::boolean::{output_false, output_true}; use crate::boolean::{output_false, output_true, output_expected_boolean};
use crate::{compile_program, get_error, get_output, EdwardsConstrainedValue, EdwardsTestCompiler}; use crate::{compile_program, get_error, get_output, EdwardsConstrainedValue, EdwardsTestCompiler};
use leo_compiler::{ use leo_compiler::{
errors::{CompilerError, FieldError, FunctionError}, errors::{CompilerError, FieldError, FunctionError},
@ -219,29 +219,23 @@ fn test_div() {
} }
#[test] #[test]
fn test_eq_true() { fn test_eq() {
for _ in 0..10 { for _ in 0..10 {
let r1: u64 = rand::random(); let r1: u64 = rand::random();
// test equal
let mut program = compile_program(DIRECTORY_NAME, "eq.leo").unwrap(); let mut program = compile_program(DIRECTORY_NAME, "eq.leo").unwrap();
program.set_inputs(vec![ program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())), Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r1.to_string())), Some(InputValue::Field(r1.to_string())),
]); ]);
output_true(program) output_true(program);
}
}
#[test] // test not equal
fn test_eq_false() {
for _ in 0..10 {
let r1: u64 = rand::random();
let r2: u64 = rand::random(); let r2: u64 = rand::random();
if r1 == r2 { let result = r1.eq(&r2);
continue;
}
let mut program = compile_program(DIRECTORY_NAME, "eq.leo").unwrap(); let mut program = compile_program(DIRECTORY_NAME, "eq.leo").unwrap();
program.set_inputs(vec![ program.set_inputs(vec![
@ -249,7 +243,129 @@ fn test_eq_false() {
Some(InputValue::Field(r2.to_string())), Some(InputValue::Field(r2.to_string())),
]); ]);
output_false(program) output_expected_boolean(program, result)
}
}
#[test]
fn test_ge() {
for _ in 0..10 {
let r1: u64 = rand::random();
// test equal
let mut program = compile_program(DIRECTORY_NAME, "ge.leo").unwrap();
program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r1.to_string())),
]);
output_true(program);
// test greater than
let r2: u64 = rand::random();
let result = r1.ge(&r2);
let mut program = compile_program(DIRECTORY_NAME, "ge.leo").unwrap();
program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r2.to_string())),
]);
output_expected_boolean(program, result)
}
}
#[test]
fn test_gt() {
for _ in 0..10 {
let r1: u64 = rand::random();
// test equal
let mut program = compile_program(DIRECTORY_NAME, "gt.leo").unwrap();
program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r1.to_string())),
]);
output_false(program);
// test greater than
let r2: u64 = rand::random();
let result = r1.gt(&r2);
let mut program = compile_program(DIRECTORY_NAME, "gt.leo").unwrap();
program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r2.to_string())),
]);
output_expected_boolean(program, result)
}
}
#[test]
fn test_le() {
for _ in 0..10 {
let r1: u64 = rand::random();
// test equal
let mut program = compile_program(DIRECTORY_NAME, "le.leo").unwrap();
program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r1.to_string())),
]);
output_true(program);
// test greater than
let r2: u64 = rand::random();
let result = r1.le(&r2);
let mut program = compile_program(DIRECTORY_NAME, "le.leo").unwrap();
program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r2.to_string())),
]);
output_expected_boolean(program, result)
}
}
#[test]
fn test_lt() {
for _ in 0..10 {
let r1: u64 = rand::random();
// test equal
let mut program = compile_program(DIRECTORY_NAME, "lt.leo").unwrap();
program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r1.to_string())),
]);
output_false(program);
// test greater than
let r2: u64 = rand::random();
let result = r1.lt(&r2);
let mut program = compile_program(DIRECTORY_NAME, "lt.leo").unwrap();
program.set_inputs(vec![
Some(InputValue::Field(r1.to_string())),
Some(InputValue::Field(r2.to_string())),
]);
output_expected_boolean(program, result)
} }
} }