@NotNull
 private List<String> getPaths(Value<Boolean> leftValue, Value<Boolean> rightValue) {
   List<String> paths = new ArrayList<>();
   paths.addAll(leftValue.getPaths());
   paths.addAll(rightValue.getPaths());
   return paths;
 }
 private ValueList evaluateBooleanConstraint(RuleEvaluation evaluation, BinaryOperator statement) {
   ValueList leftValues = evaluation.evaluate(statement.getLeftOperand());
   if (!(statement.getRightOperand() instanceof Constraint)) {
     throw new IllegalArgumentException(
         "cannot evaluate matches statement, right operand not a constraint");
   }
   Constraint constraint = (Constraint) statement.getRightOperand();
   ValueList result = new ValueList();
   result.setType(PrimitiveType.Boolean);
   for (Value value : leftValues.getValues()) {
     result.addValue(constraint.getItem().isValidValue(value.getValue()), value.getPaths());
   }
   return result;
 }
 public ValueList checkAndHandleNull(ValueList leftValues, ValueList rightValues) {
   ValueList result = new ValueList();
   if (leftValues.isEmpty() && rightValues.isEmpty()) {
     // this solves part of the null-valued expression problem. But certainly not all.
     result.addValue(Value.createNull(Lists.newArrayList()));
   } else if (leftValues.isEmpty()) {
     for (Value value : rightValues.getValues()) {
       result.addValue(Value.createNull(value.getPaths()));
     }
   } else if (rightValues.isEmpty()) {
     for (Value value : leftValues.getValues()) {
       result.addValue(Value.createNull(value.getPaths()));
     }
   } else {
     return null;
   }
   return result;
 }
  private Value evaluateMultipleValuesRelOp(
      BinaryOperator statement, ValueList leftValues, ValueList rightValues) {

    for (Value leftValue : leftValues.getValues()) {
      for (Value rightValue : rightValues.getValues()) {
        Value evaluatedRelOp =
            evaluateRelOp(
                statement,
                leftValue.getValue(),
                rightValue.getValue(),
                getPaths(leftValue, rightValue));
        if (((Boolean) evaluatedRelOp.getValue()).booleanValue()) {
          return evaluatedRelOp;
        }
      }
    }
    return new Value(false, getAllPaths(leftValues, rightValues));
  }
  private ValueList evaluateBooleanOperator(RuleEvaluation evaluation, BinaryOperator statement) {

    ValueList leftValues = evaluation.evaluate(statement.getLeftOperand());
    ValueList rightValues = evaluation.evaluate(statement.getRightOperand());

    ValueList possibleNullResult = checkAndHandleNull(leftValues, rightValues);
    if (possibleNullResult != null) {
      possibleNullResult.setType(PrimitiveType.Boolean);
      return possibleNullResult;
    }

    checkisBoolean(leftValues, rightValues);

    ValueList result = new ValueList();
    result.setType(PrimitiveType.Boolean);
    if (leftValues.size() == rightValues.size()) {
      for (int i = 0; i < leftValues.size(); i++) {
        Value<Boolean> leftValue = leftValues.get(i);
        Value<Boolean> rightValue = rightValues.get(i);
        List<String> paths = getPaths(leftValue, rightValue);
        result.addValue(
            evaluateBoolean(statement, leftValue.getValue(), rightValue.getValue()), paths);
      }
    } else if (leftValues.size() == 1) {
      Value<Boolean> leftValue = leftValues.get(0);
      for (Value<Boolean> rightValue : rightValues.getValues()) {
        List<String> paths = getPaths(leftValue, rightValue);
        result.addValue(
            evaluateBoolean(statement, leftValue.getValue(), rightValue.getValue()), paths);
      }
    } else if (rightValues.size() == 1) {
      Value<Boolean> rightValue = rightValues.get(0);
      for (Value<Boolean> leftValue : leftValues.getValues()) {
        List<String> paths = getPaths(leftValue, rightValue);
        result.addValue(
            evaluateBoolean(statement, leftValue.getValue(), rightValue.getValue()), paths);
      }
    } else {
      throw new IllegalArgumentException("sizes of operator arguments not compatible");
    }

    return result;
  }
  private ValueList evaluateArithmeticOperator(
      RuleEvaluation evaluation, BinaryOperator statement) {
    ValueList leftValues = evaluation.evaluate(statement.getLeftOperand());
    ValueList rightValues = evaluation.evaluate(statement.getRightOperand());

    ValueList possibleNullResult = checkAndHandleNull(leftValues, rightValues);
    if (possibleNullResult != null) {
      possibleNullResult.setType(PrimitiveType.Real);
      return possibleNullResult;
    } else {
      ValueList result = new ValueList();
      result.setType(PrimitiveType.Real);

      checkIsNumber(leftValues, rightValues);
      if (leftValues.size() == rightValues.size()) {
        for (int i = 0; i < leftValues.size(); i++) {
          Value leftValue = leftValues.get(i);
          Value rightValue = rightValues.get(i);
          evaluateArithmetic(
              statement,
              result,
              rightValue.getValue(),
              leftValue.getValue(),
              getPaths(leftValue, rightValue));
        }
      } else if (leftValues.size() == 1) {
        Value leftValue = leftValues.get(0);
        for (Value rightValue : rightValues.getValues()) {
          evaluateArithmetic(
              statement,
              result,
              rightValue.getValue(),
              leftValue.getValue(),
              getPaths(leftValue, rightValue));
        }
      } else if (rightValues.size() == 1) {
        Value rightValue = rightValues.get(0);
        for (Value leftValue : leftValues.getValues()) {
          evaluateArithmetic(
              statement,
              result,
              rightValue.getValue(),
              leftValue.getValue(),
              getPaths(leftValue, rightValue));
        }
      } else {
        // TODO: this also happens when one of the modelreferences has a value that does not exist
        // in the model, but others have - while the rules are correct. Those are very valid cases
        // how to fix?
        throw new IllegalArgumentException("sizes of operator arguments not compatible");
      }
      return result;
    }
  }