Simplifier.java
/*
* Copyright 2015-2023 Federico Vera
*
* 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 net.objecthunter.exp4j.shuntingyard;
import net.objecthunter.exp4j.operator.Operator;
import net.objecthunter.exp4j.tokenizer.FunctionToken;
import net.objecthunter.exp4j.tokenizer.NumberToken;
import net.objecthunter.exp4j.tokenizer.OperatorToken;
import net.objecthunter.exp4j.tokenizer.Token;
import net.objecthunter.exp4j.utils.Text;
import static net.objecthunter.exp4j.tokenizer.TokenType.NUMBER;
/**
*
* @author Federico Vera {@literal <[email protected]>}
*/
final class Simplifier {
private Simplifier() {
// Don't let anyone initialize this class
}
public static Token[] simplify (Token[] tokens) {
final TokenStack output = new TokenStack(tokens.length);
for (Token t : tokens) {
switch(t.getType()) {
case NUMBER:
case VARIABLE:
output.push(t);
break;
case OPERATOR:
final OperatorToken op = (OperatorToken) t;
final Operator operator = op.getOperator();
if (output.size() < operator.getNumOperands()) {
throw new IllegalArgumentException(Text.l10n(
"Invalid number of operands available"
));
}
if (operator.getNumOperands() == 2) {
final Token rightArg = output.pop();
final Token leftArg = output.pop();
if (rightArg.getType() == NUMBER &&
leftArg .getType() == NUMBER) {
output.push(new NumberToken(operator.apply(
((NumberToken)leftArg ).getValue(),
((NumberToken)rightArg).getValue()
))
);
} else {
output.push(leftArg);
output.push(rightArg);
output.push(t);
}
} else if (operator.getNumOperands() == 1) {
final Token arg = output.pop();
if (arg.getType() == NUMBER) {
output.push(new NumberToken(operator.apply(
((NumberToken)arg).getValue()
))
);
} else {
output.push(arg);
output.push(t);
}
}
break;
case FUNCTION:
final FunctionToken func = (FunctionToken) t;
final int numArgs = func.getFunction().getNumArguments();
//collect the arguments from the stack
final Token[] args = new Token[numArgs];
boolean areNumbers = true;
for (int j = 0; j < numArgs; j++) {
args[j] = output.pop();
areNumbers &= args[j].getType() == NUMBER;
}
if (areNumbers && func.getFunction().isDeterministic()) {
output.push(new NumberToken(
func.getFunction().apply(reverseInPlace(args)))
);
} else {
for (int i = args.length - 1; i >= 0; i--) {
output.push(args[i]);
}
output.push(t);
}
default:
//Do nothing
}
}
return output.toArray();
}
private static double[] reverseInPlace(Token[] args) {
final double[] nargs = new double[args.length];
final int sz = args.length - 1;
for (int j = 0; j <= sz; j++) {
nargs[j] = ((NumberToken)args[sz - j]).getValue();
}
return nargs;
}
}