Skip to content

Commit

Permalink
Simplify arithmetic and bitwise operators
Browse files Browse the repository at this point in the history
NULL / x → NULL
x / NULL → NULL
DIV0(x,NULL) → NULL
DIV0(NULL,x) → NULL
DIV0(x,0) → 0 if x is not nullable
x % NULL → NULL
NULL % x → NULL
A % 1 → A
NULL ^ A → NULL
0 ^ A → A (even if A is null)
NULL | A → NULL
0 | A → A (even if A is null)
NULL & A → NULL
0 & A -> 0 (if A not nullable)
NULL * x → NULL

Simplify EQUAL operator with TRUE/FALSE when operands is of boolean type
Fix simplify NOT_EQUAL operator with TRUE/FALSE when operands is of
boolean type
Add tests EQUAL and BOT_EQUAL operators with NULL

Fix simplify 0 * A → 0 (if A is not nullable)
  • Loading branch information
nadment committed Jun 14, 2024
1 parent 9166104 commit da90d7c
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@
package org.apache.hop.expression.operator;

import java.io.StringWriter;
import java.util.PriorityQueue;
import org.apache.hop.expression.Call;
import org.apache.hop.expression.ExpressionComparator;
import org.apache.hop.expression.ExpressionException;
import org.apache.hop.expression.Function;
import org.apache.hop.expression.FunctionPlugin;
import org.apache.hop.expression.IExpression;
import org.apache.hop.expression.IExpressionContext;
import org.apache.hop.expression.Literal;
import org.apache.hop.expression.OperatorCategory;
import org.apache.hop.expression.type.OperandTypes;
import org.apache.hop.expression.type.ReturnTypes;
Expand Down Expand Up @@ -53,6 +59,39 @@ public BitAndFunction(String name) {
"/docs/bit_and.html");
}

@Override
public boolean isSymmetrical() {
return true;
}

@Override
public IExpression compile(IExpressionContext context, Call call) throws ExpressionException {
// Reorder chained symmetric operator and simplify A & (..A..) --> (..A..)
PriorityQueue<IExpression> operands = new PriorityQueue<>(new ExpressionComparator());
operands.addAll(this.getChainedOperands(call, true));
IExpression operand = operands.poll();
while (!operands.isEmpty()) {
call = new Call(this, operand, operands.poll());
call.inferReturnType();
operand = call;
}
call = operand.asCall();
IExpression left = call.getOperand(0);
IExpression right = call.getOperand(1);

// Simplify NULL & A → NULL
if (left.isNull()) {
return Literal.NULL;
}

// Simplify 0 & A -> 0 (if A not nullable)
if (Literal.ZERO.equals(left) && !right.getType().isNullable()) {
return Literal.ZERO;
}

return call;
}

@Override
public Object eval(final IExpression[] operands) {
Long left = operands[0].getValue(Long.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public boolean isSymmetrical() {

@Override
public IExpression compile(IExpressionContext context, Call call) throws ExpressionException {
// Reorder chained symmetric operator
// Reorder chained symmetric operator and simplify A | (..A..) --> (..A..)
PriorityQueue<IExpression> operands = new PriorityQueue<>(new ExpressionComparator());
operands.addAll(this.getChainedOperands(call, true));
IExpression operand = operands.poll();
Expand All @@ -75,12 +75,21 @@ public IExpression compile(IExpressionContext context, Call call) throws Express
operand = call;
}
call = operand.asCall();
IExpression left = call.getOperand(0);
IExpression right = call.getOperand(1);

// Simplify 0|A → A
if (Literal.ZERO.equals(call.getOperand(0))) {
return call.getOperand(1);
// Simplify NULL | A → NULL
if (left.isNull()) {
return Literal.NULL;
}

// Simplify 0 | A → A (even if A is null)
if (Literal.ZERO.equals(left)) {
return right;
}

// TODO: Simplify A | !A → -1 (if A is not nullable)

return call;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
package org.apache.hop.expression.operator;

import java.io.StringWriter;
import org.apache.hop.expression.Call;
import org.apache.hop.expression.ExpressionException;
import org.apache.hop.expression.Function;
import org.apache.hop.expression.FunctionPlugin;
import org.apache.hop.expression.IExpression;
import org.apache.hop.expression.IExpressionContext;
import org.apache.hop.expression.Literal;
import org.apache.hop.expression.OperatorCategory;
import org.apache.hop.expression.type.OperandTypes;
import org.apache.hop.expression.type.ReturnTypes;
Expand Down Expand Up @@ -52,6 +56,31 @@ public BitXorFunction(String name) {
"/docs/bit_xor.html");
}

@Override
public IExpression compile(IExpressionContext context, Call call) throws ExpressionException {
IExpression left = call.getOperand(0);
IExpression right = call.getOperand(1);

// Simplify A ^ NULL → NULL
// Simplify NULL ^ A → NULL
if (left.isNull() || right.isNull()) {
return Literal.NULL;
}

// Simplify 0 ^ A → A (even if A is not nullable)
if (Literal.ZERO.equals(left)) {
return right;
}
if (Literal.ZERO.equals(right)) {
return left;
}

// TODO: Simplify A ^ (..A..) → (the expression without A, if number of A is odd, otherwise
// one A)

return call;
}

@Override
public Object eval(final IExpression[] operands) {
Long left = operands[0].getValue(Long.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ public IExpression compile(IExpressionContext context, Call call) throws Express

// Simplify x OR NOT x → TRUE (if x is not nullable)
// Simplify x OR NOT x → x IS NOT NULL OR NULL (if x is nullable)
// Simplify x OR (x AND y) → x (if x not nullable)

// Simplify x OR x IS NOT NULL → x IS NOT NULL
for (IExpression identifier : identifiers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ public IExpression compile(IExpressionContext context, Call call) throws Express
IExpression left = call.getOperand(0);
IExpression right = call.getOperand(1);

// Simplify DIV0(x,NULL) → NULL
if (right.isNull()) {
return Literal.NULL;
}

// Simplify DIV0(NULL,x) → NULL
if (left.isNull()) {
return Literal.NULL;
}

// Simplify arithmetic DIV0(A,0) → 0 if x is not nullable
if (Literal.ZERO.equals(right) && !left.getType().isNullable()) {
return Literal.ZERO;
}

// Simplify arithmetic DIV0(A,1) → A
if (Literal.ONE.equals(right)) {
return call.getOperand(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,22 @@ public IExpression compile(IExpressionContext context, Call call) throws Express
IExpression left = call.getOperand(0);
IExpression right = call.getOperand(1);

// Simplify arithmetic A/1 → A
// Simplify x / NULL → NULL
if (right.isNull()) {
return Literal.NULL;
}

// Simplify NULL / x → NULL
if (left.isNull()) {
return Literal.NULL;
}

// Simplify arithmetic A / 1 → A
if (Literal.ONE.equals(right)) {
return call.getOperand(0);
}

// Simplify arithmetic (-A)/(-B) → A/B
// Simplify arithmetic (-A) / (-B) → A / B
if (left.is(Operators.NEGATE) && right.is(Operators.NEGATE)) {
return new Call(Operators.DIVIDE, left.asCall().getOperand(0), right.asCall().getOperand(0));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.hop.expression.type.Comparison;
import org.apache.hop.expression.type.OperandTypes;
import org.apache.hop.expression.type.ReturnTypes;
import org.apache.hop.expression.type.TypeId;
import org.apache.hop.expression.type.Types;

/**
Expand Down Expand Up @@ -92,7 +93,19 @@ public IExpression compile(IExpressionContext context, Call call) throws Express
return new Call(this, right, left);
}

// Simplify if not nullable x=x → TRUE
// Simplify comparison when operands is of boolean type
// TRUE=x → x
// FALSE=x → NOT x
if (right.getType().is(TypeId.BOOLEAN)) {
if (left == Literal.TRUE) {
return right;
}
if (left == Literal.FALSE) {
return new Call(Operators.BOOLNOT, right);
}
}

// Simplify x=x → TRUE (if x is not nullable)
if (left.equals(right) && !left.getType().isNullable()) {
return Literal.TRUE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@
import java.math.BigDecimal;
import org.apache.hop.expression.Call;
import org.apache.hop.expression.ErrorCode;
import org.apache.hop.expression.ExpressionException;
import org.apache.hop.expression.Function;
import org.apache.hop.expression.FunctionPlugin;
import org.apache.hop.expression.IExpression;
import org.apache.hop.expression.IExpressionContext;
import org.apache.hop.expression.Literal;
import org.apache.hop.expression.OperatorCategory;
import org.apache.hop.expression.type.OperandTypes;
import org.apache.hop.expression.type.ReturnTypes;
Expand Down Expand Up @@ -56,6 +59,29 @@ public ModFunction(String name) {
"/docs/mod.html");
}

@Override
public IExpression compile(IExpressionContext context, Call call) throws ExpressionException {
IExpression left = call.getOperand(0);
IExpression right = call.getOperand(1);

// Simplify A % NULL → NULL
if (right.isNull()) {
return Literal.NULL;
}

// Simplify NULL % A → NULL
if (left.isNull()) {
return Literal.NULL;
}

// Simplify arithmetic A % 1 → A
if (Literal.ONE.equals(right)) {
return call.getOperand(0);
}

return call;
}

@Override
public Object eval(final IExpression[] operands) {
BigDecimal value = operands[0].getValue(BigDecimal.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,28 +71,33 @@ public IExpression compile(IExpressionContext context, Call call) throws Express
IExpression left = call.getOperand(0);
IExpression right = call.getOperand(1);

// Simplify arithmetic 0*A → 0
if (Literal.ZERO.equals(left)) {
// Simplify NULL * A → NULL
if (left.isNull()) {
return Literal.NULL;
}

// Simplify arithmetic 0 * A → 0 (if A is not nullable)
if (Literal.ZERO.equals(left) && !right.getType().isNullable()) {
return Literal.ZERO;
}

// Simplify arithmetic 1*A → A
// Simplify arithmetic 1 * A → A
if (Literal.ONE.equals(left)) {
return right;
}

// Simplify arithmetic (-A)*(-B) → A*B
// Simplify arithmetic (-A) * (-B) → A * B
if (left.is(Operators.NEGATE) && right.is(Operators.NEGATE)) {
return new Call(
Operators.MULTIPLY, left.asCall().getOperand(0), right.asCall().getOperand(0));
}

// Simplify arithmetic A*(1/B) → A/B
// Simplify arithmetic A * (1 / B) → A / B
if (right.is(Operators.DIVIDE) && Literal.ONE.equals(right.asCall().getOperand(0))) {
return new Call(Operators.DIVIDE, left, right.asCall().getOperand(1));
}

// Simplify arithmetic A*A → SQUARE(A)
// Simplify arithmetic A * A → SQUARE(A)
if (left.equals(right)) {
return new Call(SquareFunction.INSTANCE, left);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,21 @@ public IExpression compile(IExpressionContext context, Call call) throws Express
return new Call(this, right, left);
}

// Simplify only if x is data type boolean TRUE<>x → X IS NOT TRUE
if (left.equals(Literal.TRUE) && right.getType().is(TypeId.BOOLEAN)) {
return new Call(Operators.IS_NOT_TRUE, right);
// Simplify comparison when operands is of boolean type
// TRUE<>x → x
// FALSE<>x → NOT x
if (right.getType().is(TypeId.BOOLEAN)) {
if (left == Literal.TRUE) {
return new Call(Operators.BOOLNOT, right);
}
if (left == Literal.FALSE) {
return right;
}
}

// Simplify only if x is data type boolean FALSE<>x → X IS NOT FALSE
if (left.equals(Literal.FALSE) && right.getType().is(TypeId.BOOLEAN)) {
return new Call(Operators.IS_NOT_FALSE, right);
// Simplify x!=NULL → NULL
if (left.isNull() || right.isNull()) {
return Literal.NULL;
}

// Simplify x!=x → NULL AND x IS NULL
Expand Down
Loading

0 comments on commit da90d7c

Please sign in to comment.