@Override
 public ValueList evaluate(RuleEvaluation evaluation, BinaryOperator statement) {
   switch (statement.getOperator()) {
     case plus:
     case minus:
     case multiply:
     case divide:
     case modulo:
     case exponent:
       return evaluateArithmeticOperator(evaluation, statement);
     case gt:
     case ge:
     case lt:
     case le:
     case eq:
     case ne:
       return evaluateRelOpOperator(evaluation, statement);
     case and:
     case or:
     case xor:
       return evaluateBooleanOperator(evaluation, statement);
     case matches:
       return evaluateBooleanConstraint(evaluation, statement);
     case implies:
       return evaluateImplies(evaluation, statement);
       // for all is not listed here; it has more information that just two operands, so it has its
       // own evaluator
       // not and exists are unary operators and not handled here
   }
   throw new RuntimeException("operation " + statement.getOperator() + " not yet supported");
 }
 private Boolean evaluateBoolean(
     BinaryOperator statement, Boolean leftBoolean, Boolean rightBoolean) {
   switch (statement.getOperator()) {
     case and:
       return leftBoolean & rightBoolean;
     case or:
       return leftBoolean | rightBoolean;
     case xor:
       return leftBoolean ^ rightBoolean;
     default:
       throw new IllegalArgumentException("Not a boolean operator: " + statement.getOperator());
   }
 }
 private Value evaluateRelOp(
     BinaryOperator statement, Object leftValue, Object rightValue, List<String> paths) {
   if (leftValue == null || rightValue == null) {
     return new Value(evaluateNullRelOp(statement.getOperator(), leftValue, rightValue), paths);
   } else if (leftValue instanceof Long && rightValue instanceof Long) {
     return new Value(
         evaluateIntegerRelOp(statement.getOperator(), (Long) leftValue, (Long) rightValue),
         paths);
   } else {
     return new Value(
         evaluateRealRelOp(
             statement.getOperator(), convertToDouble(leftValue), convertToDouble(rightValue)),
         paths);
   }
 }
 private void evaluateArithmetic(
     BinaryOperator statement,
     ValueList result,
     Object rightValue,
     Object leftValue,
     List<String> paths) {
   if (leftValue instanceof Long && rightValue instanceof Long) {
     result.addValue(
         evaluateIntegerArithmetic(statement.getOperator(), (Long) leftValue, (Long) rightValue),
         paths);
     result.setType(PrimitiveType.Integer);
   } else {
     result.addValue(
         evaluateRealArithmetic(
             statement.getOperator(), convertToDouble(leftValue), convertToDouble(rightValue)),
         paths);
     result.setType(PrimitiveType.Real);
   }
 }
 private ValueList handlePossibleNullRelOpResult(
     BinaryOperator statement, ValueList leftValues, ValueList rightValues) {
   switch (statement.getOperator()) {
     case eq:
     case ne:
       // if null, can be true or false depending on checks
       return null;
     case gt:
     case ge:
     case lt:
     case le:
       // if null, result undefined, return null
       return checkAndHandleNull(leftValues, rightValues);
     default:
       throw new IllegalStateException("unknown relop operator");
   }
 }