FunctionExpresion.java

/*
* Copyright 2018 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.extras;

import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import net.objecthunter.exp4j.function.Function;
import net.objecthunter.exp4j.operator.Operator;
import net.objecthunter.exp4j.utils.Text;

import java.io.Serial;

/**
 * This class contains a wrapper that allows creating expression based functions.
 *
 * <p><i><b>Note</b></i>: This class <s>might</s> <i>will</i> fail in
 * threaded evaluations, if you need to use this to evaluate operations
 * concurrently, please create a new instance per thread.This is particularly
 * important when using {@link Expression#copy()} because {@code Function}s are
 * not copied.</p>
 * <p>Built expressions are simplified by default</p>
 *
 * @author Federico Vera {@literal <[email protected]>}
 * @since 0.8-riddler
 */
public class FunctionExpresion extends Function {
    @Serial
    private static final long serialVersionUID = -7343720821425746432L;
    private final Expression exp;
    private final String[] vars;

    /**
     * Constructor of a deterministic Expression based Function.
     *
     * <p>The arguments will be called {@code 'a'}, {@code 'b'}, {@code 'c'},
     * {@code 'd'}, ..., {@code 'z'}</p>
     *
     * @param name Name of the function
     * @param nargs Number of arguments
     * @param expr Expression to evaluate
     * @throws IllegalArgumentException If {@code nargs} is bigger than
     * {@code 26}
     */
    public FunctionExpresion(
            String name,
            int nargs,
            String expr) {
        this(name, nargs, true, expr, null, null);
    }

    /**
     * Constructor of a deterministic Expression based Function.
     *
     * <p>The arguments will be called {@code 'a'}, {@code 'b'}, {@code 'c'},
     * {@code 'd'}, ..., {@code 'z'}</p>
     *
     * @param name Name of the function
     * @param nargs Number of arguments
     * @param expr Expression to evaluate
     * @param functions Additional functions needed to compile this expression
     * @throws IllegalArgumentException If {@code nargs} is bigger than
     * {@code 26}
     */
    public FunctionExpresion(
            String name,
            int nargs,
            String expr,
            Function[] functions) {
        this(name, nargs, true, expr, functions, null);
    }

    /**
     * Constructor.
     *
     * <p>The arguments will be called {@code 'a'}, {@code 'b'}, {@code 'c'},
     * {@code 'd'}, ..., {@code 'z'}</p>
     *
     * @param name Name of the function
     * @param nargs Number of arguments
     * @param det Tells if the function is deterministic or not
     * @param expr Expression to evaluate
     * @param operators Additional operators needed to compile this expression
     * @param functions Additional functions needed to compile this expression
     * @throws IllegalArgumentException If {@code nargs} is bigger than
     * {@code 26}
     */
    public FunctionExpresion(
            String name,
            int nargs,
            boolean det,
            String expr,
            Function[] functions,
            Operator[] operators) {
        super(name, nargs, det);

        if (nargs > 'z' - 'a' + 1) {
            String msg = "Function must have less than 26 arguments";
            throw new IllegalArgumentException(Text.l10n(msg));
        }

        ExpressionBuilder builder = new ExpressionBuilder(expr);
        vars = new String[nargs];

        for (char i = 'a'; i < ('a' + nargs); i++) {
            vars[i - 'a'] = Character.toString(i);
        }

        if (functions != null) {
            builder.functions(functions);
        }

        if (operators != null) {
            builder.operators(operators);
        }

        if (nargs > 0) {
            builder.variables(vars);
        }

        exp = builder.build(true);
    }

    @Override
    public double apply(double... args) {
        for (int i = 0; i < args.length; i++) {
            exp.setVariable(vars[i], args[i]);
        }
        return exp.evaluate();
    }

}