/*
* Copyright 2015 S. Webber
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.oakgp.function.math;
import static org.oakgp.node.NodeType.isConstant;
import org.oakgp.Arguments;
import org.oakgp.Assignments;
import org.oakgp.function.Function;
import org.oakgp.node.FunctionNode;
import org.oakgp.node.Node;
/** Performs subtraction. */
final class Subtract extends ArithmeticOperator {
private final NumberUtils<?> numberUtils;
private final ArithmeticExpressionSimplifier simplifier;
/** @see NumberUtils#getSubtract() */
Subtract(NumberUtils<?> numberUtils) {
super(numberUtils.getType());
this.numberUtils = numberUtils;
this.simplifier = numberUtils.getSimplifier();
}
/**
* Returns the result of subtracting the second element of the specified arguments from the first.
*
* @return the result of subtracting {@code arg2} from {@code arg1}
*/
@Override
protected Object evaluate(Node arg1, Node arg2, Assignments assignments) {
return numberUtils.subtract(arg1, arg2, assignments).evaluate(null);
}
@Override
public Node simplify(Arguments arguments) {
Node arg1 = arguments.firstArg();
Node arg2 = arguments.secondArg();
if (arg1.equals(arg2)) {
// anything minus itself is zero
// e.g. (- x x) -> 0
return numberUtils.zero();
} else if (numberUtils.isZero(arg2)) {
// anything minus zero is itself
// e.g. (- x 0) -> x
return arg1;
} else if (numberUtils.isZero(arg1) && numberUtils.isSubtract(arg2)) {
// simplify "zero minus n" expressions
// e.g. (- 0 (- x y) -> (- y x)
FunctionNode fn2 = (FunctionNode) arg2;
Arguments fn2Arguments = fn2.getArguments();
return new FunctionNode(this, fn2Arguments.secondArg(), fn2Arguments.firstArg());
} else if (isConstant(arg2) && numberUtils.isNegative(arg2)) {
// convert double negatives to addition
// e.g. (- x -1) -> (+ 1 x)
return new FunctionNode(numberUtils.getAdd(), numberUtils.negate(arg2), arg1);
} else {
if (numberUtils.isArithmeticExpression(arg2)) {
FunctionNode fn = (FunctionNode) arg2;
Function f = fn.getFunction();
Arguments args = fn.getArguments();
Node fnArg1 = args.firstArg();
Node fnArg2 = args.secondArg();
if (numberUtils.isMultiply(f) && isConstant(fnArg1)) {
if (numberUtils.isZero(arg1)) {
// (- 0 (* -3 v0)) -> (* 3 v0)
return new FunctionNode(f, numberUtils.negateConstant(fnArg1), fnArg2);
} else if (numberUtils.isNegative(fnArg1)) {
// (- 7 (* -3 v0)) -> (+ 7 (* 3 v0))
return new FunctionNode(numberUtils.getAdd(), arg1, new FunctionNode(f, numberUtils.negateConstant(fnArg1), fnArg2));
}
} else if (numberUtils.isAdd(f) && numberUtils.isZero(arg1)) {
// (- 0 (+ v0 v1)) -> (+ (0 - v0) (0 - v1))
return new FunctionNode(f, numberUtils.negate(fnArg1), numberUtils.negate(fnArg2));
} else if (numberUtils.isSubtract(fn) && isConstant(arg1) && isConstant(fnArg1)) {
if (numberUtils.isZero(arg1)) {
// added exception to confirm we never actually get here
throw new IllegalArgumentException();
} else if (numberUtils.isZero(fnArg1)) {
// (- 1 (- 0 v0)) -> (+ 1 v0)
return new FunctionNode(numberUtils.getAdd(), arg1, fnArg2);
} else {
// (- 1 (- 7 v0)) -> (- v0 6)
return new FunctionNode(numberUtils.getAdd(), numberUtils.subtract(arg1, fnArg1), fnArg2);
}
}
}
return simplifier.simplify(this, arg1, arg2);
}
}
@Override
public String getDisplayName() {
return "-";
}
}