/*
* 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 static org.oakgp.node.NodeType.isFunction;
import org.oakgp.Assignments;
import org.oakgp.Type;
import org.oakgp.function.Function;
import org.oakgp.node.ConstantNode;
import org.oakgp.node.FunctionNode;
import org.oakgp.node.Node;
/**
* Provides support for working with classes that represent numeric values.
*
* @param <T>
* the type this instance is concerned with; this will commonly be (but is not limited to) a sub-class of {@code java.lang.Number}
*/
abstract class NumberUtils<T extends Comparable<T>> {
private final Type type;
private final T rawZero;
private final T rawOne;
private final ConstantNode zero;
private final ConstantNode one;
private final ConstantNode two;
private final ArithmeticExpressionSimplifier simplifier;
private final Add add;
private final Subtract subtract;
private final Multiply multiply;
private final Divide divide;
/**
* Creates a {@code NumberUtils} for the numeric values represented by instances of {@link #T}.
*
* @param type
* the {@code Type} associated with type {@link #T}
* @param zero
* the value {@code 0} of type {@link #T}
* @param one
* the value {@code 1} of type {@link #T}
* @param two
* the value {@code 2} of type {@link #T}
*/
protected NumberUtils(Type type, T zero, T one, T two) {
this.type = type;
this.rawZero = zero;
this.rawOne = one;
this.zero = createConstant(zero);
this.one = createConstant(one);
this.two = createConstant(two);
this.simplifier = new ArithmeticExpressionSimplifier(this);
this.add = new Add(this);
this.subtract = new Subtract(this);
this.multiply = new Multiply(this);
this.divide = new Divide(this);
}
/** Returns the {@code Type} associated with the numeric values this instance is concerned with. */
public final Type getType() {
return type;
}
/** Returns a {@code ArithmeticExpressionSimplifier} for the numeric type this instance is concerned with. */
public final ArithmeticExpressionSimplifier getSimplifier() {
return simplifier;
}
/** Returns an addition operator for the numeric type this instance is concerned with. */
public final Add getAdd() {
return add;
}
/** Returns a subtraction operator for the numeric type this instance is concerned with. */
public final Subtract getSubtract() {
return subtract;
}
/** Returns a multiplication operator for the numeric type this instance is concerned with. */
public final Multiply getMultiply() {
return multiply;
}
/** Returns a division operator for the numeric type this instance is concerned with. */
public final Divide getDivide() {
return divide;
}
/** Returns true if the given {@code Node} is a {@code ConstantNode} with the value {@code 0} of type {@link #T}. */
public final boolean isZero(Node n) {
return zero.equals(n);
}
/** Returns true if the given {@code Node} is a {@code ConstantNode} with the value {@code 1} of type {@link #T}. */
public final boolean isOne(Node n) {
return one.equals(n);
}
/** Returns a {@code ConstantNode} with the value {@code 0} of type {@link #T}. */
public final ConstantNode zero() {
return zero;
}
/** Returns a {@code ConstantNode} with the value {@code 1} of type {@link #T}. */
public final ConstantNode one() {
return one;
}
/** Returns the result of adding the result of evaluating the given {@code Node}s with the given {@code Assignments}. */
public final ConstantNode add(Node n1, Node n2, Assignments assignments) {
return createConstant(add(evaluate(n1, assignments), evaluate(n2, assignments)));
}
/**
* Returns the result of adding the numeric values represented by the given {@code Node}s.
*
* @param n1
* a {@code ConstantNode} with a value of type {@link #T}
* @param n2
* a {@code ConstantNode} with a value of type {@link #T}
*/
public final ConstantNode add(Node n1, Node n2) {
return add(n1, n2, null);
}
/**
* Returns the result of adding one to the numeric value represented by the given node.
*
* @param n
* a {@code ConstantNode} with a value of type {@link #T}
*/
public final ConstantNode increment(Node n) {
return createConstant(add(evaluate(n), rawOne));
}
/**
* Returns the result of subtracting one from the numeric value represented by the given node.
*
* @param n
* a {@code ConstantNode} with a value of type {@link #T}
*/
public final ConstantNode decrement(Node n) {
return createConstant(subtract(evaluate(n), rawOne));
}
/**
* Returns the result of subtracting the result of evaluating the given {@code Node}s with the given {@code Assignments}.
*
* @return {@code n1} - {@code n2}
*/
public final ConstantNode subtract(Node n1, Node n2, Assignments assignments) {
return createConstant(subtract(evaluate(n1, assignments), evaluate(n2, assignments)));
}
/**
* Returns the result of subtracting the numeric values represented by the given {@code Node}s.
*
* @param n1
* a {@code ConstantNode} with a value of type {@link #T}
* @param n2
* a {@code ConstantNode} with a value of type {@link #T}
* @return {@code n1} - {@code n2}
*/
public final ConstantNode subtract(Node n1, Node n2) {
return subtract(n1, n2, null);
}
/**
* Returns the result of negating the numeric value represented by the given node.
*
* @param n
* the expression to negate (may be a {@code ConstantNode}, {@code VariableNode} or a {@code FunctionNode})
*/
public final Node negate(Node arg) {
if (isConstant(arg)) {
return negateConstant(arg);
} else {
return new FunctionNode(subtract, zero, arg);
}
}
/**
* Returns the result of negating the numeric value represented by the given node.
*
* @param n
* a {@code ConstantNode} with a value of type {@link #T}
*/
public final ConstantNode negateConstant(Node n) {
return subtract(zero, n);
}
/** Returns the result of multiplying the result of evaluating the given {@code Node}s with the given {@code Assignments}. */
public final ConstantNode multiply(Node n1, Node n2, Assignments assignments) {
return createConstant(multiply(evaluate(n1, assignments), evaluate(n2, assignments)));
}
/**
* Returns the result of multiplying the numeric values represented by the given {@code Node}s.
*
* @param n1
* a {@code ConstantNode} with a value of type {@link #T}
* @param n2
* a {@code ConstantNode} with a value of type {@link #T}
*/
public final ConstantNode multiply(Node n1, Node n2) {
return multiply(n1, n2, null);
}
/** Returns a new expression which multiplies the given {@code Node} by two. */
public final FunctionNode multiplyByTwo(Node arg) {
return new FunctionNode(multiply, two, arg);
}
/**
* Returns the result of dividing the result of evaluating the given {@code Node}s with the given {@code Assignments}.
*
* @return {@code n1} / {@code n2}
*/
public final ConstantNode divide(Node n1, Node n2, Assignments assignments) {
return createConstant(divide(evaluate(n1, assignments), evaluate(n2, assignments)));
}
/**
* Returns {@code true} if the numeric value represented by the given node is less than zero.
*
* @param n
* a {@code ConstantNode} with a value of type {@link #T}
*/
public final boolean isNegative(Node n) {
return evaluate(n).compareTo(rawZero) < 0;
}
/** Returns {@code true} if the given {@code Function} is the same as the values returned from {@link #getAdd()} or {@link #getSubtract()}. */
public final boolean isAddOrSubtract(Function f) {
return isAdd(f) || isSubtract(f);
}
/** Returns {@code true} if the given {@code Function} is the same as returned from {@link #getAdd()}. */
public final boolean isAdd(Function f) {
return f == add;
}
/** Returns {@code true} if the given {@code Node} is a {@code FunctionNode} with the same {@code Function} as returned from {@link #getSubtract()}. */
public final boolean isSubtract(Node n) {
return isFunction(n) && isSubtract((FunctionNode) n);
}
/** Returns {@code true} if the {@code Function} of the given {@code FunctionNode} is the same as returned from {@link #getSubtract()}. */
public final boolean isSubtract(FunctionNode n) {
return isSubtract(n.getFunction());
}
/** Returns {@code true} if the given {@code Function} is the same as returned from {@link #getSubtract()}. */
public final boolean isSubtract(Function f) {
return f == subtract;
}
/** Returns {@code true} if the {@code Function} of the given {@code FunctionNode} is the same as returned from {@link #getMultiply()}. */
public final boolean isMultiply(FunctionNode n) {
return isMultiply(n.getFunction());
}
/** Returns {@code true} if the given {@code Function} is the same as returned from {@link #getMultiply()}. */
public final boolean isMultiply(Function f) {
return f == multiply;
}
/** Returns {@code true} if the given {@code Function} is the same as returned from {@link #getDivide()}. */
private boolean isDivide(Function f) {
return f == divide;
}
/**
* Returns {@code true} if the given {@code Node} is a {@code FunctionNode} with the same {@code Function} as returned from {@link #getAdd()},
* {@link #getSubtract()}, {@link #getMultiply()} or {@link #getDivide()}.
*/
public final boolean isArithmeticExpression(Node n) {
if (isFunction(n)) {
FunctionNode fn = (FunctionNode) n;
Function f = fn.getFunction();
return isAdd(f) || isSubtract(f) || isMultiply(f) || isDivide(f);
} else {
return false;
}
}
protected abstract T add(T i1, T i2);
protected abstract T subtract(T i1, T i2);
protected abstract T multiply(T i1, T i2);
protected abstract T divide(T i1, T i2);
private ConstantNode createConstant(T o) {
return new ConstantNode(o, type);
}
private T evaluate(Node n) {
return evaluate(n, null);
}
private T evaluate(Node n, Assignments a) {
return n.evaluate(a);
}
}