All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mariuszgromada.math.mxparser.Expression Maven / Gradle / Ivy

Go to download

mXparser is a super easy, rich, fast and highly flexible math expression parser library (parser and evaluator of mathematical expressions / formulas provided as plain text / string). Software delivers easy to use API for JAVA, Android and C# .NET/MONO (Common Language Specification compliant: F#, Visual Basic, C++/CLI). *** If you find the software useful donation or purchase is something you might consider: https://mathparser.org/donate/ *** Online store: https://payhip.com/INFIMA *** Scalar Scientific Calculator, Charts and Scripts, Scalar Lite: https://play.google.com/store/apps/details?id=org.mathparser.scalar.lite *** Scalar Pro: https://play.google.com/store/apps/details?id=org.mathparser.scalar.pro *** ScalarMath.org: https://scalarmath.org/ *** MathSpace.pl: https://mathspace.pl/ ***

There is a newer version: 6.1.0
Show newest version
/*
 * @(#)Expression.java        6.0.0    2024-05-19
 *
 * MathParser.org-mXparser DUAL LICENSE AGREEMENT as of date 2024-05-19
 * The most up-to-date license is available at the below link:
 * - https://mathparser.org/mxparser-license
 *
 * AUTHOR: Copyright 2010 - 2024 Mariusz Gromada - All rights reserved
 * PUBLISHER: INFIMA - https://payhip.com/infima
 *
 * SOFTWARE means source code and/or binary form and/or documentation.
 * PRODUCT: MathParser.org-mXparser SOFTWARE
 * LICENSE: DUAL LICENSE AGREEMENT
 *
 * BY INSTALLING, COPYING, OR OTHERWISE USING THE PRODUCT, YOU AGREE TO BE
 * BOUND BY ALL OF THE TERMS AND CONDITIONS OF THE DUAL LICENSE AGREEMENT.
 *
 * The AUTHOR & PUBLISHER provide the PRODUCT under the DUAL LICENSE AGREEMENT
 * model designed to meet the needs of both non-commercial use and commercial
 * use.
 *
 * NON-COMMERCIAL USE means any use or activity where a fee is not charged
 * and the purpose is not the sale of a good or service, and the use or
 * activity is not intended to produce a profit. Examples of NON-COMMERCIAL USE
 * include:
 *
 * 1. Non-commercial open-source software.
 * 2. Non-commercial mobile applications.
 * 3. Non-commercial desktop software.
 * 4. Non-commercial web applications/solutions.
 * 5. Non-commercial use in research, scholarly and educational context.
 *
 * The above list is non-exhaustive and illustrative only.
 *
 * COMMERCIAL USE means any use or activity where a fee is charged or the
 * purpose is the sale of a good or service, or the use or activity is
 * intended to produce a profit. COMMERCIAL USE examples:
 *
 * 1. OEMs (Original Equipment Manufacturers).
 * 2. ISVs (Independent Software Vendors).
 * 3. VARs (Value Added Resellers).
 * 4. Other distributors that combine and distribute commercially licensed
 *    software.
 *
 * The above list is non-exhaustive and illustrative only.
 *
 * IN CASE YOU WANT TO USE THE PRODUCT COMMERCIALLY, YOU MUST PURCHASE THE
 * APPROPRIATE LICENSE FROM "INFIMA" ONLINE STORE, STORE ADDRESS:
 *
 * 1. https://mathparser.org/order-commercial-license
 * 2. https://payhip.com/infima
 *
 * NON-COMMERCIAL LICENSE
 *
 * Redistribution and use of the PRODUCT in source and/or binary forms,
 * with or without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the unmodified content of
 *    the entire MathParser.org-mXparser DUAL LICENSE AGREEMENT, including
 *    the definition of NON-COMMERCIAL USE, the definition of COMMERCIAL USE,
 *    the NON-COMMERCIAL LICENSE conditions, the COMMERCIAL LICENSE conditions,
 *    and the following DISCLAIMER.
 * 2. Redistributions in binary form must reproduce the entire content of
 *    MathParser.org-mXparser DUAL LICENSE AGREEMENT in the documentation
 *    and/or other materials provided with the distribution, including the
 *    definition of NON-COMMERCIAL USE, the definition of COMMERCIAL USE, the
 *    NON-COMMERCIAL LICENSE conditions, the COMMERCIAL LICENSE conditions,
 *    and the following DISCLAIMER.
 * 3. Any form of redistribution requires confirmation and signature of
 *    the NON-COMMERCIAL USE by successfully calling the method:
 *       License.iConfirmNonCommercialUse(...)
 *    The method call is used only internally for logging purposes, and
 *    there is no connection with other external services, and no data is
 *    sent or collected. The lack of a method call (or its successful call)
 *    does not affect the operation of the PRODUCT in any way. Please see
 *    the API documentation.
 *
 * COMMERCIAL LICENSE
 *
 *  1. Before purchasing a commercial license, the AUTHOR & PUBLISHER allow
 *     you to download, install, and use up to three copies of the PRODUCT to
 *     perform integration tests, confirm the quality of the PRODUCT, and
 *     its suitability. The testing period should be limited to fourteen
 *     days. Tests should be performed under the test environments conditions
 *     and not for profit generation.
 *  2. Provided that you purchased a license from "INFIMA" online store
 *     (store address: https://mathparser.org/order-commercial-license or
 *     https://payhip.com/infima), and you comply with all terms and
 *     conditions below, and you have acknowledged and understood the
 *     following DISCLAIMER, the AUTHOR & PUBLISHER grant you a nonexclusive
 *     license with the following rights:
 *  3. The license is granted only to you, the person or entity that made
 *     the purchase, identified and confirmed by the data provided during
 *     the purchase.
 *  4. If you purchased a license in the "ONE-TIME PURCHASE" model, the
 *     license is granted only for the PRODUCT version specified in the
 *     purchase. The upgrade policy gives you additional rights, described
 *     in the dedicated section below.
 *  5. If you purchased a license in the "SUBSCRIPTION" model, you may
 *     install and use any version of the PRODUCT during the subscription
 *     validity period.
 *  6. If you purchased a "SINGLE LICENSE" you may install and use the
 *     PRODUCT on/from one workstation that is located/accessible at/from
 *     any of your premises.
 *  7. Additional copies of the PRODUCT may be installed and used on/from
 *     more than one workstation, limited to the number of workstations
 *     purchased per order.
 *  8. If you purchased a "SITE LICENSE", the PRODUCT may be installed
 *     and used on/from all workstations located/accessible at/from any
 *     of your premises.
 *  9. You may incorporate the unmodified PRODUCT into your own products
 *     and software.
 * 10. If you purchased a license with the "SOURCE CODE" option, you may
 *     modify the PRODUCT's source code and incorporate the modified source
 *     code into your own products and/or software.
 * 11. Provided that the license validity period has not expired, you may
 *     distribute your product and/or software with the incorporated
 *     PRODUCT royalty-free.
 * 12. You may make copies of the PRODUCT for backup and archival purposes.
 * 13. Any form of redistribution requires confirmation and signature of
 *     the COMMERCIAL USE by successfully calling the method:
 *        License.iConfirmCommercialUse(...)
 *     The method call is used only internally for logging purposes, and
 *     there is no connection with other external services, and no data is
 *     sent or collected. The lack of a method call (or its successful call)
 *     does not affect the operation of the PRODUCT in any way. Please see
 *     the API documentation.
 * 14. The AUTHOR & PUBLISHER reserve all rights not expressly granted to
 *     you in this agreement.
 *
 * ADDITIONAL CLARIFICATION ON WORKSTATION
 *
 * A workstation is a device, a remote device, or a virtual device, used by
 * you, your employees, or other entities to whom you have commissioned
 * tasks. For example, the number of workstations may refer to the number
 * of software developers, engineers, architects, scientists, and other
 * professionals who use the PRODUCT on your behalf. The number of
 * workstations is not the number of copies of your end-product that you
 * distribute to your end-users.
 *
 * By purchasing the COMMERCIAL LICENSE, you only pay for the number of
 * workstations, while the number of copies/users of your final product
 * (delivered to your end-users) is not limited.
 *
 * Below are some examples to help you select the right license size:
 *
 * Example 1: Single Workstation License
 * Only one developer works on the development of your application. You do
 * not use separate environments for testing, meaning you design, create,
 * test, and compile your final application on one environment. In this
 * case, you need a license for a single workstation.
 *
 * Example 2: Up to 5 Workstations License
 * Two developers are working on the development of your application.
 * Additionally, one tester conducts tests in a separate environment.
 * You use three workstations in total, so you need a license for up to
 * five workstations.
 *
 * Example 3: Up to 20 Workstations License
 * Ten developers are working on the development of your application.
 * Additionally, five testers conduct tests in separate environments.
 * You use fifteen workstations in total, so you need a license for
 * up to twenty workstations.
 *
 * Example 4: Site License
 * Several dozen developers and testers work on the development of your
 * application using multiple environments. You have a large,
 * multi-disciplinary team involved in creating your solution. As your team
 * is growing and you want to avoid licensing limitations, the best choice
 * would be a site license.
 *
 * UPGRADE POLICY
 *
 * The PRODUCT is versioned according to the following convention:
 *
 *    [MAJOR].[MINOR].[PATCH]
 *
 * 1. COMMERCIAL LICENSE holders can install and use the updated version
 *    for bug fixes free of charge, i.e. if you have purchased a license
 *    for the [MAJOR].[MINOR] version (e.g., 5.0), you can freely install
 *    all releases specified in the [PATCH] version (e.g., 5.0.2).
 *    The license terms remain unchanged after the update.
 * 2. COMMERCIAL LICENSE holders for the [MAJOR].[MINOR] version (e.g., 5.0)
 *    can install and use the updated version [MAJOR].[MINOR + 1] free of
 *    charge, i.e., plus one release in the [MINOR] range (e.g., 5.1). The
 *    license terms remain unchanged after the update.
 * 3. COMMERCIAL LICENSE holders who wish to upgrade their version, but are
 *    not eligible for the free upgrade, can claim a discount when
 *    purchasing the upgrade. For this purpose, please contact us via e-mail.
 *
 * DISCLAIMER
 *
 * THIS PRODUCT IS PROVIDED BY THE AUTHOR & PUBLISHER "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AUTHOR OR PUBLISHER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS PRODUCT, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE VIEWS AND CONCLUSIONS CONTAINED IN THE PRODUCT AND DOCUMENTATION ARE
 * THOSE OF THE AUTHORS AND SHOULD NOT BE INTERPRETED AS REPRESENTING
 * OFFICIAL POLICIES, EITHER EXPRESSED OR IMPLIED, OF THE AUTHOR OR PUBLISHER.
 *
 * CONTACT
 *
 * - e-mail: [email protected]
 * - website: https://mathparser.org
 * - source code: https://github.com/mariuszgromada/MathParser.org-mXparser
 * - online store: https://mathparser.org/order-commercial-license
 * - online store: https://payhip.com/infima
 */
package org.mariuszgromada.math.mxparser;

import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.util.*;

import org.mariuszgromada.math.mxparser.mathcollection.BinaryRelations;
import org.mariuszgromada.math.mxparser.mathcollection.BooleanAlgebra;
import org.mariuszgromada.math.mxparser.mathcollection.MathConstants;
import org.mariuszgromada.math.mxparser.mathcollection.MathFunctions;
import org.mariuszgromada.math.mxparser.mathcollection.NumberTheory;
import org.mariuszgromada.math.mxparser.mathcollection.Calculus;
import org.mariuszgromada.math.mxparser.mathcollection.ProbabilityDistributions;
import org.mariuszgromada.math.mxparser.mathcollection.SpecialFunctions;
import org.mariuszgromada.math.mxparser.mathcollection.Statistics;
import org.mariuszgromada.math.mxparser.mathcollection.Units;
import org.mariuszgromada.math.mxparser.parsertokens.BinaryRelation;
import org.mariuszgromada.math.mxparser.parsertokens.BitwiseOperator;
import org.mariuszgromada.math.mxparser.parsertokens.BooleanOperator;
import org.mariuszgromada.math.mxparser.parsertokens.CalculusOperator;
import org.mariuszgromada.math.mxparser.parsertokens.ConstantValue;
import org.mariuszgromada.math.mxparser.parsertokens.Function1Arg;
import org.mariuszgromada.math.mxparser.parsertokens.Function2Arg;
import org.mariuszgromada.math.mxparser.parsertokens.Function3Arg;
import org.mariuszgromada.math.mxparser.parsertokens.FunctionVariadic;
import org.mariuszgromada.math.mxparser.parsertokens.KeyWord;
import org.mariuszgromada.math.mxparser.parsertokens.Operator;
import org.mariuszgromada.math.mxparser.parsertokens.ParserSymbol;
import org.mariuszgromada.math.mxparser.parsertokens.RandomVariable;
import org.mariuszgromada.math.mxparser.parsertokens.Token;
import org.mariuszgromada.math.mxparser.parsertokens.Unit;
import org.mariuszgromada.math.mxparser.syntaxchecker.SyntaxChecker;

/**
 * Expression - base class for real expression definition.
 *
 * Examples:
 * 
    *
  • '1+2' *
  • 'sin(x)+1' *
  • 'asin(3*x)^10-log(4,8)' *
  • in general 'f(x1,x2,...,xn)' where x1,...,xn are real * arguments *
*

* Class provides easy way to define multivariate arithmetic expression. * * * @author Mariusz Gromada
* MathParser.org - mXparser project page
* mXparser on GitHub
* INFIMA place to purchase a commercial MathParser.org-mXparser software license
* [email protected]
* ScalarMath.org - a powerful math engine and math scripting language
* Scalar Lite
* Scalar Pro
* MathSpace.pl
* * @version 6.0.0 * * @see Argument * @see RecursiveArgument * @see Constant * @see Function */ public class Expression extends PrimitiveElement implements Serializable { private static final int serialClassID = 6; private static final long serialVersionUID = SerializationUtils.getSerialVersionUID(serialClassID); /** * Expression type id */ public static final int TYPE_ID = 100; public static String TYPE_DESC = ParserSymbol.NA; /** * FOUND / NOT_FOUND * used for matching purposes */ static final int NOT_FOUND = mXparser.NOT_FOUND; static final int FOUND = mXparser.FOUND; /** * Marker for internal processing */ static final boolean INTERNAL = true; /** * For verbose mode purposes */ private static final boolean WITH_EXP_STR = true; private static final boolean NO_EXP_STR = false; /** * Status of the syntax - no syntax error */ public static final boolean NO_SYNTAX_ERRORS = true; /** * Status of the syntax - syntax error or syntax status unknown */ public static final boolean SYNTAX_ERROR = false; /** * Status of the syntax - syntax error or syntax status unknown * * @deprecated Planned to be removed, use {@link #SYNTAX_ERROR} instead */ @Deprecated public static final boolean SYNTAX_ERROR_OR_STATUS_UNKNOWN = SYNTAX_ERROR; private static final boolean SYNTAX_STATUS_UNKNOWN = false; /** * Expression string (for example: "sin(x)+cos(y)") */ String expressionString = StringInvariant.EMPTY; /** * Expression string after attempt to clean */ private String expressionStringCleaned = StringInvariant.EMPTY; /** * Expression description */ private String description = StringInvariant.EMPTY; /** * List of arguments * * @see Argument * @see RecursiveArgument */ List argumentsList; /** * List of user defined functions * * @see Function */ List functionsList; /** * List of user defined constants * * @see Constant */ List constantsList; /** * List of keywords known by the parser */ private List keyWordsList; /** * List of expression tokens (words). * Token class defines all needed * attributes for recognizing the structure of * arithmetic expression. This is the key result when * initial parsing is finished (tokenizeExpressionString() - method). * Token keeps information about: * - token type (for example: function, operator, argument, number, etc...) * - token identifier within given type (sin, cos, operaotr, etc...) * - token value (if token is a number) * - token level - key information regarding sequence (order) of further parsing */ List initialTokens; private CompilationDetails initialCompilationDetails; /** * List of string tokens that should not be considered * while seeking for optional implied multiplication. * * Example: sum( x2y, 1, 10, 2*x2y) * * Here x2y should always stay as x2y */ private Set neverParseForImpliedMultiplication; /** * the initialTokens list keeps unchanged information about * found tokens. * * While parsing the tokensList is used. The tokensList is the same * as initialTokens list at the beginning of the calculation process. * Each math operation changes tokens list - it means that * tokens are parameters when performing math operation * and the result is also presented as token (usually as a number token) * At the end of the calculation the tokensList should contain only one * element - the result of all calculations. */ private List tokensList; private CompilationDetails compilationDetails; /** * List of related expressions, for example when * user defined function is used in the expression * or dependent argument was defined. Modification of * function expression calls the method expression modified * flag method to all related expressions. * * Related expression usually are used for * - dependent arguments * - recursive arguments * - user functions */ List relatedExpressionsList; /** * Keeps computing time */ double computingTime; /** * if true then new tokenizing is required * (the initialTokens list needs to be updated) */ boolean expressionWasModified; /** * If recursive mode is on the recursive calls are permitted. * It means there will be no null pointer exceptions * due to expression, and functions cloning. */ boolean recursiveMode; /** * Verbose mode prints processing info * calls System.out.print* methods */ private boolean verboseMode; /** * Implied multiplication mode */ private boolean impliedMultiplicationMode = mXparser.impliedMultiplicationMode; /** * Fires an error when impliedMultiplicationMode is on * and there is a missing multiplication operator */ private boolean impliedMultiplicationError = false; /** * Internal parameter for calculus expressions * to avoid decrease in accuracy. */ boolean disableRounding; static final boolean DISABLE_ROUNDING = true; static final boolean KEEP_ROUNDING_SETTINGS = false; /** * Status of the expression syntax * * Please referet to the: * - NO_SYNTAX_ERRORS * - SYNTAX_ERROR * - SYNTAX_STATUS_UNKNOWN */ private boolean syntaxStatus; private boolean isFullyCompiled; /** * Message after checking the syntax */ private String errorMessage; /** * Optional message from calculate method */ private String errorMessageCalculate; private static int ERROR_MESSAGE_CALCULATE_MAXIMUM_LENGTH = mXparser.ERROR_MESSAGE_MAXIMUM_LENGTH / 5; /** * Log used internally to mark started recursion * call on the current object, necessary to * avoid infinite loops while recursive syntax * checking (i.e. f to g and g to f) * or marking modified flags on the expressions * related to this expression. * * @see #setExpressionModifiedFlag() * @see #checkSyntax() */ private boolean recursionCallPending; /** * Internal counter to avoid infinite loops while calculating * expression defined in the way showed by below examples * * Argument x = new Argument("x = 2*y"); * Argument y = new Argument("y = 2*x"); * x.addDefinitions(y); * y.addDefinitions(x); * * Function f = new Function("f(x) = 2*g(x)"); * Function g = new Function("g(x) = 2*f(x)"); * f.addDefinitions(g); * g.addDefinitions(f); */ private int recursionCallsCounter; /** * Internal indicator for tokenization process * if true, then keywords such as constants * functions etc... will not be recognized * during tokenization */ private boolean parserKeyWordsOnly; /** * Internal indicator informing hte parser * that unicode know keywords are enabled * and will be recognized by the parser * as built-in functions or operators */ private boolean unicodeKeyWordsEnabled = mXparser.unicodeKeyWordsEnabled; /** * Internal indicator informing the parser * whether to try to fix the expression String. * For example, situations such as: * "++" change to "+", * "+-" changed tro "-" * "-+" changed tro "-" * "--" changed tro "+" */ private boolean attemptToFixExpStrEnabled = mXparser.attemptToFixExpStrEnabled; /** * Indicator whether expression was * automatically built for user defined * functions purpose * * @see Function */ boolean UDFExpression = false; /** * List of parameters provided by the user at run-time * * @see Function */ List UDFVariadicParamsAtRunTime; /** * Internal indicator for calculation process * Expression.Calculate() method * It shows whether to build again tokens list * if clone - build again * if not clone - build only at the beginning * * Indicator helps to solve the problem with * above definitions * * Function f = new Function("f(x) = 2*g(x)"); * Function g = new Function("g(x) = 2*f(x)"); * f.addDefinitions(g); * g.addDefinitions(f); */ private boolean internalClone; /** * An indicator of whether an error message * should be passed from the current expression * to the expression that called it. */ private boolean forwardErrorMessage = true; /** * mXparser options changeset * used in checkSyntax() method */ private int optionsChangesetNumber = -1; /*================================================= * * Related expressions handling * *================================================= */ /** * Adds related expression * The same expression could be added more than once * For example when * * @param expression the expression */ void addRelatedExpression(Expression expression) { if (expression == null || expression == this) return; if (!relatedExpressionsList.contains(expression)) relatedExpressionsList.add(expression); } /** * Removes related expression * * @param expression the expression */ void removeRelatedExpression(Expression expression) { relatedExpressionsList.remove(expression); } /** * Prints related expression list */ void showRelatedExpressions() { mXparser.consolePrintln(); mXparser.consolePrintln(description + StringInvariant.SPACE_EQUAL_SPACE + expressionString + StringInvariant.COLON); for (Expression e : relatedExpressionsList) mXparser.consolePrintln(StringInvariant.RIGHT_ARROW_SPACE + e.description + StringInvariant.SPACE_EQUAL_SPACE + e.expressionString); } /** * Method return error message after * calling checkSyntax() method or * calculate(). * * @return Error message as string. */ public String getErrorMessage() { return StringUtils.cleanNewLineAtTheEnd(errorMessage); } /** * Gets syntax status of the expression. * * @return true if there are no syntax errors, * false when syntax error was found or * syntax status is unknown */ public boolean getSyntaxStatus() { return this.syntaxStatus; } /** * Package level method for passing * information about errors identified * on the constructors level * * @param syntaxStatus Syntax status * @param errorMessage Error message * * @see Function */ void setSyntaxStatus(boolean syntaxStatus, String errorMessage) { this.syntaxStatus = syntaxStatus; this.errorMessage = errorMessage; this.expressionWasModified = false; markAsNotFullyCompiled(); } void markAsNotFullyCompiled() { isFullyCompiled = false; initialCompilationDetails = null; } /** * Sets expression status to modified * Calls setExpressionModifiedFlag() method * to all related expressions. */ void setExpressionModifiedFlag() { if (recursionCallPending) return; recursionCallPending = true; recursionCallsCounter = 0; internalClone = false; expressionWasModified = true; syntaxStatus = SYNTAX_STATUS_UNKNOWN; markAsNotFullyCompiled(); errorMessage = StringInvariant.EMPTY; for (Expression e : relatedExpressionsList) e.setExpressionModifiedFlag(); recursionCallPending = false; } /** * Common variables while expression initializing */ private void expressionInternalVarsInit() { description = StringInvariant.EMPTY; errorMessage = StringInvariant.EMPTY; errorMessageCalculate = StringInvariant.EMPTY; computingTime = 0; recursionCallPending = false; recursionCallsCounter = 0; internalClone = false; forwardErrorMessage = true; parserKeyWordsOnly = false; verboseMode = false; syntaxStatus = false; isFullyCompiled = false; impliedMultiplicationMode = mXparser.impliedMultiplicationMode; unicodeKeyWordsEnabled = mXparser.unicodeKeyWordsEnabled; attemptToFixExpStrEnabled = mXparser.attemptToFixExpStrEnabled; disableRounding = KEEP_ROUNDING_SETTINGS; } /** * Common elements while expression initializing */ private void expressionInit() { /* * New lists */ argumentsList = new ArrayList(); functionsList = new ArrayList(); constantsList = new ArrayList(); relatedExpressionsList = new ArrayList(); /* * Empty description * Silent mode * No recursive mode */ setSilentMode(); disableRecursiveMode(); expressionInternalVarsInit(); } /*================================================= * * Constructors * *================================================= */ /** * Default constructor - empty expression * * @param elements Optional elements list (variadic - comma separated) of types: Argument, Constant, Function * * @see PrimitiveElement */ public Expression(PrimitiveElement...elements) { super(Expression.TYPE_ID); expressionString = StringInvariant.EMPTY; expressionInit(); setExpressionModifiedFlag(); addDefinitions(elements); } /** * Constructor - creates new expression from expression string. * * @param expressionString definition of the expression * @param elements Optional elements list (variadic - comma separated) of types: Argument, Constant, Function * * @see PrimitiveElement * */ public Expression(String expressionString, PrimitiveElement...elements) { super(Expression.TYPE_ID); expressionInit(); this.expressionString = expressionString; setExpressionModifiedFlag(); addDefinitions(elements); } /** * Constructor - creates new expression from expression string. * @param expressionString definition of the expression * @param parserKeyWordsOnly if true then all keywords such as functions, * constants, arguments will not be recognized. */ Expression(String expressionString, boolean parserKeyWordsOnly) { super(Expression.TYPE_ID); expressionInit(); this.expressionString = expressionString; setExpressionModifiedFlag(); this.parserKeyWordsOnly = parserKeyWordsOnly; } /** * Package level constructor - creates new expression from subexpression * (sublist of the tokens list), arguments list, functions list and * constants list (used by the internal calculus operations, etc...). * * @param expressionString the expression string * @param initialTokens the tokens list (starting point - no tokenizing, * no syntax checking) * @param argumentsList the arguments list * @param functionsList the functions list * @param constantsList the constants list */ Expression( String expressionString ,List initialTokens ,List argumentsList ,List functionsList ,List constantsList ,boolean disableUlpRounding ,boolean UDFExpression ,List UDFVariadicParamsAtRunTime ) { super(Expression.TYPE_ID); this.expressionString = expressionString; this.initialTokens = initialTokens; this.argumentsList = argumentsList; this.functionsList = functionsList; this.constantsList = constantsList; relatedExpressionsList = new ArrayList(); expressionWasModified = false; syntaxStatus = NO_SYNTAX_ERRORS; isFullyCompiled = false; description = StringInvariant.INTERNAL; errorMessage = StringInvariant.EMPTY; errorMessageCalculate = StringInvariant.EMPTY; computingTime = 0; recursionCallPending = false; recursionCallsCounter = 0; internalClone = false; forwardErrorMessage = true; parserKeyWordsOnly = false; verboseMode = false; impliedMultiplicationMode = mXparser.impliedMultiplicationMode; unicodeKeyWordsEnabled = mXparser.unicodeKeyWordsEnabled; attemptToFixExpStrEnabled = mXparser.attemptToFixExpStrEnabled; this.UDFExpression = UDFExpression; this.UDFVariadicParamsAtRunTime = UDFVariadicParamsAtRunTime; this.disableRounding = disableUlpRounding; setSilentMode(); disableRecursiveMode(); } /** * Package level constructor - creates new expression from expression string, * arguments list, functions list and constants list (used by the * RecursiveArgument class). * * No related expressions at the beginning. * * @param expressionString the expression string * @param argumentsList the arguments list * @param functionsList the functions list * @param constantsList the constants list * @param internal the marker for internal processing * * @see Argument * @see RecursiveArgument * @see Function * @see Constant */ Expression(String expressionString, List argumentsList, List functionsList, List constantsList ,boolean internal, boolean UDFExpression, List UDFVariadicParamsAtRunTime) { super(Expression.TYPE_ID); this.expressionString = expressionString; expressionInternalVarsInit(); setSilentMode(); disableRecursiveMode(); this.argumentsList = argumentsList; this.functionsList = functionsList; this.constantsList = constantsList; this.UDFExpression = UDFExpression; this.UDFVariadicParamsAtRunTime = UDFVariadicParamsAtRunTime; relatedExpressionsList = new ArrayList(); setExpressionModifiedFlag(); } /* * Private constructor - expression cloning. */ private Expression(Expression expressionToClone, boolean isThreadSafeClone, CloneCache cloneCache) { super(Expression.TYPE_ID); expressionString = expressionToClone.expressionString; expressionStringCleaned = expressionToClone.expressionStringCleaned; description = expressionToClone.description; computingTime = expressionToClone.computingTime; expressionWasModified = expressionToClone.expressionWasModified; recursiveMode = expressionToClone.recursiveMode; verboseMode = expressionToClone.verboseMode; impliedMultiplicationMode = expressionToClone.impliedMultiplicationMode; impliedMultiplicationError = expressionToClone.impliedMultiplicationError; disableRounding = expressionToClone.disableRounding; syntaxStatus = expressionToClone.syntaxStatus; isFullyCompiled = expressionToClone.isFullyCompiled; errorMessage = expressionToClone.errorMessage; errorMessageCalculate = expressionToClone.errorMessageCalculate; recursionCallPending = expressionToClone.recursionCallPending; recursionCallsCounter = expressionToClone.recursionCallsCounter; parserKeyWordsOnly = expressionToClone.parserKeyWordsOnly; unicodeKeyWordsEnabled = expressionToClone.unicodeKeyWordsEnabled; attemptToFixExpStrEnabled = expressionToClone.attemptToFixExpStrEnabled; UDFExpression = expressionToClone.UDFExpression; forwardErrorMessage = expressionToClone.forwardErrorMessage; optionsChangesetNumber = expressionToClone.optionsChangesetNumber; keyWordsList = expressionToClone.keyWordsList; UDFVariadicParamsAtRunTime = expressionToClone.UDFVariadicParamsAtRunTime; neverParseForImpliedMultiplication = expressionToClone.neverParseForImpliedMultiplication; if (isThreadSafeClone) { internalClone = expressionToClone.internalClone; relatedExpressionsList = new ArrayList(); argumentsList = ExpressionUtils.cloneForThreadSafeArgumenstList(this, expressionToClone.argumentsList, cloneCache); functionsList = ExpressionUtils.cloneForThreadSafeFunctionsList(this, expressionToClone.functionsList, cloneCache); constantsList = ExpressionUtils.cloneForThreadSafeConstantsList(this, expressionToClone.constantsList, cloneCache); return; } internalClone = true; argumentsList = expressionToClone.argumentsList; functionsList = expressionToClone.functionsList; constantsList = expressionToClone.constantsList; relatedExpressionsList = expressionToClone.relatedExpressionsList; } /** * Sets (modifies expression) expression string. * * @param expressionString the expression string */ public void setExpressionString(String expressionString) { this.expressionString = expressionString; expressionStringCleaned = StringInvariant.EMPTY; setExpressionModifiedFlag(); } /** * Returns expression string * * @return Expression string definition. */ public String getExpressionString() { return expressionString; } /** * Returns expression string * * @return Expression string definition. */ public String getCanonicalExpressionString() { StringBuilder canonicalExpression = new StringBuilder(1000); for (Token t : getCopyOfInitialTokens()) canonicalExpression.append(t.tokenStr); return canonicalExpression.toString(); } /** * Clears expression string */ public void clearExpressionString() { expressionString = StringInvariant.EMPTY; expressionStringCleaned = StringInvariant.EMPTY; setExpressionModifiedFlag(); } /** * Sets expression description. * * @param description the description string */ public void setDescription(String description) { this.description = description; } /** * Gets expression description. * * @return String description. */ public String getDescription() { return description; } /** * Clears expression description */ public void clearDescription() { this.description = StringInvariant.EMPTY; } /** * Enables verbose mode. */ public void setVerboseMode() { verboseMode = true; } /** * Disables verbose mode (default silent mode). */ public void setSilentMode() { verboseMode = false; } /** * Returns verbose mode status. * * @return true if verbose mode is on, * otherwise returns false. */ public boolean getVerboseMode() { return verboseMode; } /** * Sets implied multiplication */ public void enableImpliedMultiplicationMode() { if (impliedMultiplicationMode) return; impliedMultiplicationMode = true; setExpressionModifiedFlag(); } /** * Disables implied multiplication */ public void disableImpliedMultiplicationMode() { if (!impliedMultiplicationMode) return; impliedMultiplicationMode = false; setExpressionModifiedFlag(); } /** * Gets implied multiplication status * * @return true if implied multiplication is enabled, * otherwise returns false. */ public boolean checkIfImpliedMultiplicationMode() { return impliedMultiplicationMode; } /** * Enables unicode built-in parser keywords, this flag * informs the parser that built-in unicode keywords * are supported and will be recognized as functions or * operators. */ public void enableUnicodeBuiltinKeyWordsMode() { if (unicodeKeyWordsEnabled) return; unicodeKeyWordsEnabled = true; setExpressionModifiedFlag(); } /** * Disables unicode built-in parser keywords, this flag * informs the parser that built-in unicode keywords * are not supported and will not be recognized as functions or * operators. */ public void disableUnicodeBuiltinKeyWordsMode() { if (!unicodeKeyWordsEnabled) return; unicodeKeyWordsEnabled = false; setExpressionModifiedFlag(); } /** * Gets unicode built-in parser keywords mode * * @return true if unicode built-in parser keywords is enabled, * otherwise returns false. */ public boolean checkIfUnicodeBuiltinKeyWordsMode() { return unicodeKeyWordsEnabled; } /** * Enables attempt to fix the expression String. * For example, situations such as: * "++" change to "+", * "+-" changed to "-" * "-+" changed to "-" * "--" changed to "+" */ public void enableAttemptToFixExpStrMode() { if (attemptToFixExpStrEnabled) return; attemptToFixExpStrEnabled = true; setExpressionModifiedFlag(); } /** * Disables attempt to fix the expression String. * For example, situations such as: * "++" change to "+", * "+-" changed to "-" * "-+" changed to "-" * "--" changed ro "+" */ public void disableAttemptToFixExpStrMode() { if (!attemptToFixExpStrEnabled) return; attemptToFixExpStrEnabled = false; setExpressionModifiedFlag(); } /** * Gets attempt to fix expression string mode * * @return true attempt to fix expression string mode is enabled, * otherwise returns false. */ public boolean checkIfAttemptToFixExpStrMode() { return attemptToFixExpStrEnabled; } /** * Sets recursive mode */ void setRecursiveMode() { recursiveMode = true; } /** * Disables recursive mode */ void disableRecursiveMode() { recursiveMode = false; } /** * Gets recursive mode status * * @return true if recursive mode is enabled, * otherwise returns false. */ public boolean getRecursiveMode() { return recursiveMode; } /** * An indicator of whether an error message * should be passed from the current expression * to the expression that called it. * * @param forward If true then message is being forwarded. */ void setForwardErrorMessage(boolean forward) { if (forward == forwardErrorMessage) return; errorMessage = StringInvariant.EMPTY; errorMessageCalculate = StringInvariant.EMPTY; forwardErrorMessage = forward; } /** * Gets computing time. * * @return computing time in seconds. */ public double getComputingTime() { return computingTime; } /** * Adds user defined elements (such as: Arguments, Constants, Functions) * to the expressions. * * @param elements Elements list (variadic), where Argument, Constant, Function * extend the same class PrimitiveElement * * @see PrimitiveElement */ public void addDefinitions(PrimitiveElement... elements) { for (PrimitiveElement e : elements) { if (e == null) continue; switch (e.getMyTypeId()) { case Argument.TYPE_ID: case RecursiveArgument.TYPE_ID_RECURSIVE: addArguments((Argument)e); break; case Constant.TYPE_ID: addConstants((Constant)e); break; case Function.TYPE_ID: addFunctions((Function)e); break; } } } /** * Removes user defined elements (such as: Arguments, Constants, Functions) * to the expressions. * * @param elements Elements list (variadic), where Argument, Constant, Function * extend the same class PrimitiveElement * * @see PrimitiveElement */ public void removeDefinitions(PrimitiveElement... elements) { for (PrimitiveElement e : elements) { if (e == null) continue; switch (e.getMyTypeId()) { case Argument.TYPE_ID: case RecursiveArgument.TYPE_ID_RECURSIVE: removeArguments((Argument)e); break; case Constant.TYPE_ID: removeConstants((Constant)e); break; case Function.TYPE_ID: removeFunctions((Function)e); break; } } } /*================================================= * * Arguments handling API * *================================================= */ /** * Adds arguments (variadic) to the expression definition. * * @param arguments the arguments list * (comma separated list) * @see Argument * @see RecursiveArgument */ public void addArguments(Argument... arguments) { for (Argument arg : arguments) { if (arg == null) continue; argumentsList.add(arg); if (arg.getArgumentBodyType() == Argument.BODY_RUNTIME) arg.addRelatedExpression(this); } setExpressionModifiedFlag(); } /** * Enables to define the arguments (associated with * the expression) based on the given arguments names. * * @param argumentsNames the arguments names (variadic) * comma separated list * * @see Argument * @see RecursiveArgument */ public void defineArguments(String... argumentsNames) { for (String argName : argumentsNames) { Argument arg = new Argument(argName); arg.addRelatedExpression(this); argumentsList.add(arg); } setExpressionModifiedFlag(); } /** * Enables to define the argument (associated with the expression) * based on the argument name and the argument value. * * @param argumentName the argument name * @param argumentValue the argument value * * @see Argument * @see RecursiveArgument */ public void defineArgument(String argumentName, double argumentValue) { Argument arg = new Argument(argumentName, argumentValue); arg.addRelatedExpression(this); argumentsList.add(arg); setExpressionModifiedFlag(); } /** * Gets argument index from the expression. * * @param argumentName the argument name * * @return The argument index if the argument name was found, * otherwise returns Argument.NOT_FOUND * * @see Argument * @see RecursiveArgument */ public int getArgumentIndex(String argumentName) { int argumentsNumber = argumentsList.size(); if (argumentsNumber == 0) return NOT_FOUND; for (int argumentIndex = 0; argumentIndex < argumentsNumber; argumentIndex++) if (argumentsList.get(argumentIndex).getArgumentName().equals(argumentName)) return argumentIndex; return NOT_FOUND; } /** * Gets argument from the expression. * * * @param argumentName the argument name * * @return The argument if the argument name was found, * otherwise returns null. * * @see Argument * @see RecursiveArgument */ public Argument getArgument(String argumentName) { int argumentIndex = getArgumentIndex(argumentName); if (argumentIndex == NOT_FOUND) return null; return argumentsList.get(argumentIndex); } /** * Gets argument from the expression. * * @param argumentIndex the argument index * * @return Argument if the argument index is between 0 and * the last available argument index (getArgumentsNumber()-1), * otherwise returns null. * * @see Argument * @see RecursiveArgument */ public Argument getArgument(int argumentIndex) { if (argumentIndex < 0 || argumentIndex >= argumentsList.size()) return null; return argumentsList.get(argumentIndex); } /** * Gets number of arguments associated with the expression. * * @return The number of arguments (int >= 0) * * @see Argument * @see RecursiveArgument */ public int getArgumentsNumber() { return argumentsList.size(); } /** * Sets argument value. * * @param argumentName the argument name * @param argumentValue the argument value */ public void setArgumentValue(String argumentName, double argumentValue) { int argumentIndex = getArgumentIndex(argumentName); if (argumentIndex != NOT_FOUND) argumentsList.get(argumentIndex).setArgumentValue(argumentValue); } /** * Gets argument vale. * * @param argumentName the argument name * * @return Argument value if argument name was found, * otherwise return Double.NaN. */ public double getArgumentValue(String argumentName) { int argumentIndex = getArgumentIndex(argumentName); if (argumentIndex == NOT_FOUND) return Double.NaN; return argumentsList.get(argumentIndex).getArgumentValue(); } /** * Removes first occurrences of the arguments * associated with the expression. * * @param argumentsNames the arguments names * (variadic parameters) comma separated * list * * @see Argument * @see RecursiveArgument */ public void removeArguments(String... argumentsNames) { for (String argumentName : argumentsNames) { int argumentIndex = getArgumentIndex(argumentName); if (argumentIndex == NOT_FOUND) continue; Argument arg = argumentsList.get(argumentIndex); arg.removeRelatedExpression(this); argumentsList.remove(argumentIndex); } setExpressionModifiedFlag(); } /** * Removes first occurrences of the arguments * associated with the expression. * * @param arguments the arguments (variadic parameters) * comma separated list * * @see Argument * @see RecursiveArgument */ public void removeArguments(Argument... arguments) { for (Argument argument : arguments) { if (argument == null) continue; argumentsList.remove(argument); argument.removeRelatedExpression(this); } setExpressionModifiedFlag(); } /** * Removes all arguments associated with the expression. * * @see Argument * @see RecursiveArgument */ public void removeAllArguments() { for (Argument arg : argumentsList) arg.removeRelatedExpression(this); argumentsList.clear(); setExpressionModifiedFlag(); } /*================================================= * * Constants handling API * *================================================= */ /** * Adds constants (variadic parameters) to the expression definition. * * @param constants the constants * (comma separated list) * * @see Constant */ public void addConstants(Constant... constants) { for (Constant constant : constants) { if (constant == null) continue; constantsList.add(constant); constant.addRelatedExpression(this); } setExpressionModifiedFlag(); } /** * Adds constants to the expression definition. * * @param constantsList the list of constants * * @see Constant */ public void addConstants(List constantsList) { for (Constant c : constantsList) { this.constantsList.add(c); c.addRelatedExpression(this); } setExpressionModifiedFlag(); } /** * Enables to define the constant (associated with * the expression) based on the constant name and * constant value. * * @param constantName the constant name * @param constantValue the constant value * * @see Constant */ public void defineConstant(String constantName, double constantValue) { Constant c = new Constant(constantName, constantValue); c.addRelatedExpression(this); constantsList.add(c); setExpressionModifiedFlag(); } /** * Gets constant index associated with the expression. * * @param constantName the constant name * * @return Constant index if constant name was found, * otherwise return Constant.NOT_FOUND. * * @see Constant */ public int getConstantIndex(String constantName) { int constantsNumber = constantsList.size(); if (constantsNumber == 0) return NOT_FOUND; for (int constantIndex = 0; constantIndex < constantsNumber; constantIndex++) if (constantsList.get(constantIndex).getConstantName().equals(constantName)) return constantIndex; return NOT_FOUND; } /** * Gets constant associated with the expression. * * @param constantName the constant name * * @return Constant if constant name was found, * otherwise return null. * * @see Constant */ public Constant getConstant(String constantName) { int constantIndex = getConstantIndex(constantName); if (constantIndex == NOT_FOUND) return null; return constantsList.get(constantIndex); } /** * Gets constant associated with the expression. * * @param constantIndex the constant index * * @return Constant if the constantIndex is between * 0 and the last available constant index * (getConstantsNumber() - 1), * otherwise it returns null. * * @see Constant */ public Constant getConstant(int constantIndex) { if (constantIndex < 0 || constantIndex >= constantsList.size()) return null; return constantsList.get(constantIndex); } /** * Gets number of constants associated with the expression. * * @return number of constants (int >= 0) * * @see Constant */ public int getConstantsNumber() { return constantsList.size(); } /** * Removes first occurrences of the constants * associated with the expression. * * @param constantsNames the constants names (variadic parameters) * comma separated list * * @see Constant */ public void removeConstants(String... constantsNames) { for (String constantName : constantsNames) { int constantIndex = getConstantIndex(constantName); if (constantIndex == NOT_FOUND) continue; Constant c = constantsList.get(constantIndex); c.removeRelatedExpression(this); constantsList.remove(constantIndex); } setExpressionModifiedFlag(); } /** * Removes first occurrences of the constants * associated with the expression * * @param constants the constants (variadic parameters) * comma separated list * * @see Constant */ public void removeConstants(Constant... constants) { for (Constant constant : constants) { if (constant == null) continue; constantsList.remove(constant); constant.removeRelatedExpression(this); setExpressionModifiedFlag(); } } /** * Removes all constants * associated with the expression * * @see Constant */ public void removeAllConstants() { for (Constant c : constantsList) c.removeRelatedExpression(this); constantsList.clear(); setExpressionModifiedFlag(); } /*================================================= * * Functions handling API * *================================================= */ /** * Adds functions (variadic parameters) to the expression definition. * * @param functions the functions * (variadic parameters) comma separated list * * @see Function */ public void addFunctions(Function... functions) { for (Function f : functions) { if (f == null) continue; functionsList.add(f); if (f.getFunctionBodyType() == Function.BODY_RUNTIME) f.addRelatedExpression(this); } setExpressionModifiedFlag(); } /** * Enables to define the function (associated with * the expression) based on the function name, * function expression string and arguments names (variadic parameters). * * @param functionName the function name * @param functionExpressionString the expression string * @param argumentsNames the function arguments names * (variadic parameters) * comma separated list * * @see Function */ public void defineFunction(String functionName, String functionExpressionString, String... argumentsNames) { Function f = new Function(functionName, functionExpressionString, argumentsNames); functionsList.add(f); f.addRelatedExpression(this); setExpressionModifiedFlag(); } /** * Gets index of function associated with the expression. * * @param functionName the function name * * @return Function index if function name was found, * otherwise returns Function.NOT_FOUND * * @see Function */ public int getFunctionIndex(String functionName) { int functionsNumber = functionsList.size(); if (functionsNumber == 0) return NOT_FOUND; for (int functionIndex = 0; functionIndex < functionsNumber; functionIndex++) if (functionsList.get(functionIndex).getFunctionName().equals(functionName)) return functionIndex; return NOT_FOUND; } /** * Gets function associated with the expression. * * @param functionName the function name * * @return Function if function name was found, * otherwise returns null. * * @see Function */ public Function getFunction(String functionName) { int functionIndex = getFunctionIndex(functionName); if (functionIndex == NOT_FOUND) return null; return functionsList.get(functionIndex); } /** * Gets function associated with the expression. * * @param functionIndex the function index * * @return Function if function index is between 0 and * the last available function index (getFunctionsNumber()-1), * otherwise returns null. * * @see Function */ public Function getFunction(int functionIndex) { if (functionIndex < 0 || functionIndex >= functionsList.size()) return null; return functionsList.get(functionIndex); } /** * Gets number of functions associated with the expression. * * @return number of functions (int >= 0) * * @see Function */ public int getFunctionsNumber() { return functionsList.size(); } /** * Removes first occurrences of the functions * associated with the expression. * * @param functionsNames the functions names (variadic parameters) * comma separated list * * @see Function */ public void removeFunctions(String... functionsNames) { for (String functionName : functionsNames) { int functionIndex = getFunctionIndex(functionName); if (functionIndex == NOT_FOUND) continue; Function f = functionsList.get(functionIndex); f.removeRelatedExpression(this); functionsList.remove(f); } setExpressionModifiedFlag(); } /** * Removes first occurrences of the functions * associated with the expression. * * @param functions the functions (variadic parameters) * comma separated list. * * @see Function */ public void removeFunctions(Function... functions) { for (Function function : functions) { if (function == null) continue; function.removeRelatedExpression(this); functionsList.remove(function); } setExpressionModifiedFlag(); } /** * Removes all functions * associated with the expression. * * @see Function */ public void removeAllFunctions() { for (Function f : functionsList) f.removeRelatedExpression(this); functionsList.clear(); setExpressionModifiedFlag(); } /*================================================= * * Common methods (supporting calculations) * *================================================= */ /** * Sets given token to the number type / value. * Method should be called only by the SetDecreaseRemove like methods * * @param pos the position on which token * should be updated to the given number * @param number the number * @param ulpRound If true, then if {@link mXparser#ulpRounding} = true * intelligent ULP rounding is applied. */ private void setToNumber(int pos, double number, boolean ulpRound) { Token token = tokensList.get(pos); token.tokenTypeId = ParserSymbol.NUMBER_TYPE_ID; token.tokenId = ParserSymbol.NUMBER_ID; token.keyWord = ParserSymbol.NUMBER_STR; if (!mXparser.ulpRounding || disableRounding || !ulpRound) { token.tokenValue = number; return; } if (Double.isNaN(number) || Double.isInfinite(number)) { token.tokenValue = number; return; } int precision = MathFunctions.ulpDecimalDigitsBefore(number); if (precision >= 0) token.tokenValue = MathFunctions.round(number, precision); else token.tokenValue = number; } private void setToNumber(int pos, double number) { setToNumber(pos, number, false); } /** * SetDecreaseRemove for 1 arg functions * * SetDecreaseRemove like methods are called by the methods * calculating values of the unary operation, binary relations * and functions. * * 3 things are done by this type of methods * 1) Set token type to number type / value * 2) Decrease level of the token * 3) Remove no longer needed tokens * * For example: * * Expression string: 1+cos(0) * will be tokened as follows: * * idx : 0 1 2 3 4 5 * token : 1 + cos ( 0 ) * level : 0 0 1 2 2 2 * * Partitions with the highest level will be handled first. * In the case presented above, it means, that the parenthesis will be removed * * idx : 0 1 2 3 * token : 1 + cos 0 * level : 0 0 1 2 * * Next step is to calculate cos(0) = 1 * * SetDecreaseRemove like methods * * 1) Set cos token to 1 (pos=2, result=1): * idx : 0 1 2 3 * token : 1 + 1 0 * level : 0 0 1 2 * * 2) Decrease level (pos=2): * idx : 0 1 2 3 * token : 1 + 1 0 * level : 0 0 0 2 * * 3) Remove no longer needed tokens (pos+1=3): * idx : 0 1 2 * token : 1 + 1 * level : 0 0 0 * * @param pos the position on which token * should be updated to the given number * @param result the number * @param ulpRound If true, then if {@link mXparser#ulpRounding} = true * intelligent ULP rounding is applied. */ private void f1SetDecreaseRemove(int pos, double result, boolean ulpRound) { setToNumber(pos, result, ulpRound); tokensList.get(pos).tokenLevel--; tokensList.remove(pos+1); } private void f1SetDecreaseRemove(int pos, double result) { f1SetDecreaseRemove(pos, result, false); } /** * SetDecreaseRemove for 2-args functions * * For detailed specification refer to the * f1SetDecreaseRemove() * * @param pos the position on which token * should be updated to the given number * @param result the number * @param ulpRound If true, then if {@link mXparser#ulpRounding} = true * intelligent ULP rounding is applied. */ private void f2SetDecreaseRemove(int pos, double result, boolean ulpRound) { setToNumber(pos, result, ulpRound); tokensList.get(pos).tokenLevel--; tokensList.remove(pos+2); tokensList.remove(pos+1); } private void f2SetDecreaseRemove(int pos, double result) { f2SetDecreaseRemove(pos, result, false); } /** * SetDecreaseRemove for 3-args functions * * For detailed specification refer to the * f1SetDecreaseRemove() * * @param pos the position on which token * should be updated to the given number * @param result the number * @param ulpRound If true, then if {@link mXparser#ulpRounding} = true * intelligent ULP rounding is applied. */ private void f3SetDecreaseRemove(int pos, double result, boolean ulpRound) { setToNumber(pos, result, ulpRound); tokensList.get(pos).tokenLevel--; tokensList.remove(pos+3); tokensList.remove(pos+2); tokensList.remove(pos+1); } private void f3SetDecreaseRemove(int pos, double result) { f3SetDecreaseRemove(pos, result, false); } /** * SetDecreaseRemove for operators * * For detailed specification refer to the * f1SetDecreaseRemove() * * @param pos the position on which token * should be updated to the given number * @param result the number * @param ulpRound If true, then if {@link mXparser#ulpRounding} = true * intelligent ULP rounding is applied. */ private void opSetDecreaseRemove(int pos, double result, boolean ulpRound) { setToNumber(pos, result, ulpRound); tokensList.remove(pos+1); tokensList.remove(pos-1); } private void opSetDecreaseRemove(int pos, double result) { opSetDecreaseRemove(pos, result, false); } /** * SetDecreaseRemove for calculus operators. * * For detailed specification refer to the * f1SetDecreaseRemove() * * @param pos the position on which token * should be updated to the given number * @param result the number * @param ulpRound If true, then if {@link mXparser#ulpRounding} = true * intelligent ULP rounding is applied. */ private void calcSetDecreaseRemove(int pos, double result, boolean ulpRound) { setToNumber(pos, result, ulpRound); tokensList.get(pos).tokenLevel--; /* * left parenthesis position */ int lPos = pos+1; /* * Evaluate right parenthesis position */ int rPos = lPos+1; while ( !(tokensList.get(rPos).tokenTypeId == ParserSymbol.TYPE_ID && tokensList.get(rPos).tokenId == ParserSymbol.RIGHT_PARENTHESES_ID && tokensList.get(rPos).tokenLevel == tokensList.get(lPos).tokenLevel) ) rPos++; for (int p = rPos; p >= lPos; p--) tokensList.remove(p); } private void calcSetDecreaseRemove(int pos, double result) { calcSetDecreaseRemove(pos, result, false); } /** * SetDecreaseRemove for special functions. * * For detailed specification refer to the * f1SetDecreaseRemove() * * @param pos the position on which token * should be updated to the given number * @param value the number * @param length the special function range * @param ulpRound If true, then if {@link mXparser#ulpRounding} = true * intelligent ULP rounding is applied. */ private void variadicSetDecreaseRemove(int pos, double value, int length, boolean ulpRound) { setToNumber(pos, value, ulpRound); tokensList.get(pos).tokenLevel--; for (int p = pos + length; p > pos; p--) tokensList.remove(p); } private void variadicSetDecreaseRemove(int pos, double value, int length) { variadicSetDecreaseRemove(pos, value, length, false); } /** * If set remove method for the if function. * * @param pos the position * @param ifCondition the result of if condition * @param ulpRound If true, then if {@link mXparser#ulpRounding} = true * intelligent ULP rounding is applied. */ private void ifSetRemove(int pos, double ifCondition, boolean ulpRound) { /* * left parethesis position */ int lPos = pos+1; int ifLevel = tokensList.get(lPos).tokenLevel; /* * Evaluate 1 comma position on the same level */ int c1Pos = lPos+1; while ( !(tokensList.get(c1Pos).tokenTypeId == ParserSymbol.TYPE_ID && tokensList.get(c1Pos).tokenId == ParserSymbol.COMMA_ID && tokensList.get(c1Pos).tokenLevel == ifLevel) ) c1Pos++; /* * Evaluate 2 comma position on the same level */ int c2Pos = c1Pos+1; while ( !(tokensList.get(c2Pos).tokenTypeId == ParserSymbol.TYPE_ID && tokensList.get(c2Pos).tokenId == ParserSymbol.COMMA_ID && tokensList.get(c2Pos).tokenLevel == ifLevel) ) c2Pos++; /* * Evaluate right parenthesis position */ int rPos = c2Pos+1; while ( !(tokensList.get(rPos).tokenTypeId == ParserSymbol.TYPE_ID && tokensList.get(rPos).tokenId == ParserSymbol.RIGHT_PARENTHESES_ID && tokensList.get(rPos).tokenLevel == ifLevel) ) rPos++; if (!Double.isNaN(ifCondition)) { if (ifCondition != 0) { setToNumber(c2Pos+1, Double.NaN); tokensList.get(c2Pos+1).tokenLevel = ifLevel; removeTokens(c2Pos+2, rPos-1); } else { setToNumber(c1Pos+1, Double.NaN); tokensList.get(c1Pos+1).tokenLevel = ifLevel; removeTokens(c1Pos+2, c2Pos-1); } } else { setToNumber(c1Pos+1, Double.NaN); setToNumber(c2Pos+1, Double.NaN); tokensList.get(c1Pos+1).tokenLevel = ifLevel; tokensList.get(c2Pos+1).tokenLevel = ifLevel; removeTokens(c2Pos+2, rPos-1); removeTokens(c1Pos+2, c2Pos-1); } setToNumber(lPos+1, ifCondition, ulpRound); tokensList.get(lPos+1).tokenLevel = ifLevel; removeTokens(lPos+2, c1Pos-1); tokensList.get(pos).tokenId = Function3Arg.IF_ID; } private void removeTokens(int from, int to) { if (from < to) { for (int p = to; p >= from; p--) tokensList.remove(p); } else if (from == to) tokensList.remove(from); } private void ifSetRemove(int pos, double ifCondition) { ifSetRemove(pos, ifCondition, false); } /** * Creates string tokens list from the subexpression. * * @param startPos start position (index) * @param endPos end position (index) * * @return tokens list representing requested subexpression. */ private List createInitialTokens(int startPos, int endPos, List tokensList) { List tokens = new ArrayList(); Token t; for (int p = startPos; p<= endPos; p++) { t = tokensList.get(p).clone(); tokens.add(t); } return tokens; } /** * Return number of functions parameters. * * @param pos the function position */ private int getParametersNumber(int pos) { int lPpos = pos+1; if (lPpos == initialTokens.size()) return -1; if (initialTokens.get(lPpos).tokenTypeId != ParserSymbol.TYPE_ID || initialTokens.get(lPpos).tokenId != ParserSymbol.LEFT_PARENTHESES_ID) return -1; int tokenLevel = initialTokens.get(lPpos).tokenLevel; /* * Evaluate right parenthesis position */ int endPos = lPpos+1; while ( !(initialTokens.get(endPos).tokenTypeId == ParserSymbol.TYPE_ID && initialTokens.get(endPos).tokenId == ParserSymbol.RIGHT_PARENTHESES_ID && initialTokens.get(endPos).tokenLevel == tokenLevel) ) endPos++; if (endPos == lPpos + 1) return 0; /* * Evaluate number of parameters by * counting number of ',' between parenthesis */ int numberOfCommas = 0; for (int p = lPpos; p < endPos; p++) { Token token = initialTokens.get(p); if (token.tokenTypeId == ParserSymbol.TYPE_ID && token.tokenId == ParserSymbol.COMMA_ID && token.tokenLevel == tokenLevel) numberOfCommas++; } return numberOfCommas + 1; } /** * Gets / returns argument representing given argument name. If * argument name exists on the list of known arguments * the initial status of the found argument is remembered, otherwise new * argument will be created. * * @param argumentName the argument name * * @return Argument parameter representing given argument name: * * * @see ArgumentParameter * @see Argument */ private ArgumentParameter getParamArgument(String argumentName) { ArgumentParameter argParam = new ArgumentParameter(); argParam.index = getArgumentIndex(argumentName); argParam.argument = getArgument(argParam.index); argParam.presence = FOUND; if (argParam.argument == null) { argParam.argument = new Argument(argumentName); argumentsList.add(argParam.argument); argParam.index = argumentsList.size()-1; argParam.presence = NOT_FOUND; return argParam; } argParam.initialValue = argParam.argument.argumentValue; argParam.initialType = argParam.argument.argumentType; argParam.argument.argumentValue = argParam.argument.getArgumentValue(); argParam.argument.argumentType = Argument.FREE_ARGUMENT; return argParam; } /** * Clears argument parameter. * * @param argParam the argument parameter. */ private void clearParamArgument(ArgumentParameter argParam) { if (argParam.presence == NOT_FOUND) { argumentsList.remove(argParam.index); return; } argParam.argument.argumentValue = argParam.initialValue; argParam.argument.argumentType = argParam.initialType; } /*================================================= * * Math implementations * *================================================= */ /** * Free Arguments handling. * * @param pos the token position */ private void FREE_ARGUMENT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.FREE_ARGUMENT, pos); Argument argument = argumentsList.get( tokensList.get(pos).tokenId); boolean argumentVerboseMode = argument.getVerboseMode(); if (verboseMode) argument.setVerboseMode(); setToNumber(pos, argument.getArgumentValue()); if (!argumentVerboseMode) argument.setSilentMode(); } /** * Dependent Arguments handling. * * @param pos the token position */ private void DEPENDENT_ARGUMENT(int pos, CalcStepsRegister calcStepsRegister) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.DEPENDENT_ARGUMENT, pos); Argument argument = argumentsList.get( tokensList.get(pos).tokenId); boolean argumentVerboseMode = argument.getVerboseMode(); if (verboseMode) argument.setVerboseMode(); /* * Handling possible recursive calls that can change * the structure of the tokens list, i.e. * * Argument x = new Argument("x = 2*y"); * Argument y = new Argument("y = 2*x"); * x.addDefinitions(y); * y.addDefinitions(x); * x.getArgumentValue(); */ int tokensListSizeBefore = tokensList.size(); Token tokenBefore = tokensList.get(pos); double argumentValue = argument.getArgumentValue(calcStepsRegister); if (forwardErrorMessage && this != argument.argumentExpression) { errorMessageCalculate = StringUtils.stringConcatenateMaxLength(errorMessageCalculate, argument.argumentExpression.errorMessageCalculate, ERROR_MESSAGE_CALCULATE_MAXIMUM_LENGTH); errorMessage = StringUtils.stringConcatenateMaxLength(errorMessage, argument.argumentExpression.errorMessageCalculate, mXparser.ERROR_MESSAGE_MAXIMUM_LENGTH); } int tokensListSizeAfter = tokensList.size(); if (tokensListSizeBefore == tokensListSizeAfter) { Token tokenAfter = tokensList.get(pos); if ( (tokenBefore.tokenTypeId == tokenAfter.tokenTypeId) && (tokenBefore.tokenId == tokenAfter.tokenId) ) { setToNumber(pos, argumentValue); } } if (!argumentVerboseMode) argument.setSilentMode(); } /** * User functions handling. * * @param pos the token position */ private void USER_FUNCTION(int pos, CalcStepsRegister calcStepsRegister) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.USER_FUNCTION, pos); Function function; Function fun = functionsList.get( tokensList.get(pos).tokenId ); if (fun.getRecursiveMode()) { function = fun.clone(); function.functionExpression.recursionCallsCounter = recursionCallsCounter; } else function = fun; function.functionExpression.UDFVariadicParamsAtRunTime = getNumbers(pos); int argsNumber = function.getParametersNumber(); if (!function.isVariadic) for (int argIdx = 0; argIdx < argsNumber; argIdx++) function.setArgumentValue(argIdx, tokensList.get(pos + argIdx + 1).tokenValue); boolean functionVerboseMode = function.getVerboseMode(); if (verboseMode) function.setVerboseMode(); /* * Handling possible recursive calls that can change * the structure of the tokens list, i.e. * * Function f = new Function("f(x) = 2*g(x)"); * Function g = new Function("g(x) = 2*f(x)"); * f.addDefinitions(g); * g.addDefinitions(f); */ int tokensListSizeBefore = tokensList.size(); Token tokenBefore = tokensList.get(pos); double value; try { value = function.calculate(calcStepsRegister); } catch (StackOverflowError soe) { value = Double.NaN; errorMessage = StringUtils.trimNotNull(soe.getMessage()); } if (forwardErrorMessage && this != function.functionExpression) { errorMessageCalculate = StringUtils.stringConcatenateMaxLength(errorMessageCalculate, function.functionExpression.errorMessageCalculate, ERROR_MESSAGE_CALCULATE_MAXIMUM_LENGTH); errorMessage = StringUtils.stringConcatenateMaxLength(errorMessage, function.functionExpression.errorMessageCalculate, mXparser.ERROR_MESSAGE_MAXIMUM_LENGTH); } int tokensListSizeAfter = tokensList.size(); if (tokensListSizeBefore == tokensListSizeAfter) { Token tokenAfter = tokensList.get(pos); if ( (tokenBefore.tokenTypeId == tokenAfter.tokenTypeId) && (tokenBefore.tokenId == tokenAfter.tokenId) ) { setToNumber(pos, value); tokensList.get(pos).tokenLevel--; for (int argIdx = argsNumber; argIdx > 0 ; argIdx--) tokensList.remove(pos+argIdx); } } if (!functionVerboseMode) function.setSilentMode(); } /** * User constants handling. * * @param pos the token position */ private void USER_CONSTANT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.USER_CONSTANT, pos); Constant constant = constantsList.get( tokensList.get(pos).tokenId ); setToNumber(pos, constant.getConstantValue()); } /** * Recursive arguments handling. * * @param pos the token position */ private void RECURSIVE_ARGUMENT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.RECURSIVE_ARGUMENT, pos); double index = tokensList.get(pos+1).tokenValue; RecursiveArgument argument = (RecursiveArgument)argumentsList.get( tokensList.get(pos).tokenId ); boolean argumentVerboseMode = argument.getVerboseMode(); if (verboseMode) argument.setVerboseMode(); double result = argument.getArgumentValue(index); if (forwardErrorMessage && this != argument.argumentExpression) { errorMessageCalculate = StringUtils.stringConcatenateMaxLength(errorMessageCalculate, argument.argumentExpression.errorMessageCalculate, ERROR_MESSAGE_CALCULATE_MAXIMUM_LENGTH); errorMessage = StringUtils.stringConcatenateMaxLength(errorMessage, argument.argumentExpression.errorMessageCalculate, mXparser.ERROR_MESSAGE_MAXIMUM_LENGTH); } f1SetDecreaseRemove(pos, result); if (!argumentVerboseMode) argument.setSilentMode(); } /** * Constants handling. * * @param pos the token position */ private void CONSTANT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.CONSTANT, pos); double constValue = Double.NaN; int constantValueId = tokensList.get(pos).tokenId; if (constantValueId == ConstantValue.NPAR_ID) constValue = UDFVariadicParamsAtRunTime.size(); else constValue = MathConstants.getConstantValue(constantValueId); setToNumber(pos, constValue); } /** * Constants handling. * * @param pos the token position */ private void UNIT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.UNIT, pos); setToNumber(pos, Units.getUnitValue(tokensList.get(pos).tokenId)); } /** * Random Variables handling. * * @param pos the token position */ private void RANDOM_VARIABLE(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.RANDOM_VARIABLE, pos); setToNumber(pos, ProbabilityDistributions.getRandomVariableValue(tokensList.get(pos).tokenId)); } /** * Gets token value * @param tokenIndex the token index * * @return the token value */ private double getTokenValue(int tokenIndex) { return tokensList.get(tokenIndex).tokenValue; } /** * Tetration handling. * * @param pos the token position */ private void TETRATION(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.TETRATION, pos); double a = getTokenValue(pos-1); double n = getTokenValue(pos+1); opSetDecreaseRemove(pos, MathFunctions.tetration(a, n), true); } /** * Power handling. * * @param pos the token position */ private void POWER(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.POWER, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, MathFunctions.power(a, b), true); } /** * Modulo handling. * * @param pos the token position */ private void MODULO(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.MODULO, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, MathFunctions.mod(a, b) ); } /** * Division handling. * * @param pos the token position */ private void DIVIDE(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.DIVIDE, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); if (disableRounding) { double result = Double.NaN; if (b != 0) result = a / b; opSetDecreaseRemove(pos, result, true); } else opSetDecreaseRemove(pos, MathFunctions.div(a, b), true); } /** * Integer division handling. * * @param pos the token position */ private void DIVIDE_QUOTIENT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.DIVIDE_QUOTIENT, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, MathFunctions.divQuotient(a, b), true); } /** * Multiplication handling. * * @param pos the token position */ private void MULTIPLY(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.MULTIPLY, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); if (disableRounding) opSetDecreaseRemove(pos, a * b, true); else opSetDecreaseRemove(pos, MathFunctions.multiply(a, b), true); } /** * Addition handling. * * @param pos the token position */ private void PLUS(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.PLUS, pos); Token b = tokensList.get(pos+1); if (pos == 0) { if (b.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) { setToNumber(pos,b.tokenValue); tokensList.remove(pos+1); } return; } Token a = tokensList.get(pos-1); if (a.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID && b.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) if (disableRounding) opSetDecreaseRemove(pos, a.tokenValue + b.tokenValue, true); else opSetDecreaseRemove(pos, MathFunctions.plus(a.tokenValue, b.tokenValue), true); else if (b.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) { setToNumber(pos,b.tokenValue); tokensList.remove(pos+1); } } /** * Subtraction handling * * @param pos the token position */ private void MINUS(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.MINUS, pos); Token b = tokensList.get(pos+1); if (pos == 0) { if (b.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) { setToNumber(pos,-b.tokenValue); tokensList.remove(pos+1); } return; } Token a = tokensList.get(pos-1); if (a.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID && b.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) if (disableRounding) opSetDecreaseRemove(pos, a.tokenValue - b.tokenValue, true); else opSetDecreaseRemove(pos, MathFunctions.minus(a.tokenValue, b.tokenValue), true); else if (b.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) { setToNumber(pos,-b.tokenValue); tokensList.remove(pos+1); } } /** * Logical AND * * @param pos the token position */ private void AND(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.and(a, b) ); } /** * Logical OR * * @param pos the token position */ private void OR(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.or(a, b) ); } /** * Logical NAND * * @param pos the token position */ private void NAND(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.nand(a, b) ); } /** * Logical NOR * * @param pos the token position */ private void NOR(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.nor(a, b) ); } /** * Logical XOR * * * @param pos the token position */ private void XOR(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.xor(a, b) ); } /** * Logical IMP * * * @param pos the token position */ private void IMP(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.imp(a, b) ); } /** * Logical CIMP * * @param pos the token position */ private void CIMP(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.cimp(a, b) ); } /** * Logical NIMP * * @param pos the token position */ private void NIMP(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.nimp(a, b) ); } /** * Logical CNIMP * * @param pos the token position */ private void CNIMP(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.cnimp(a, b) ); } /** * Logical EQV * * @param pos the token position */ private void EQV(int pos) { double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BooleanAlgebra.eqv(a, b) ); } /** * Logical negation * * @param pos the token position */ private void NEG(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.NEG, pos); double a = getTokenValue(pos+1); setToNumber(pos, BooleanAlgebra.not(a) ); tokensList.remove(pos+1); } /** * Equality relation. * * @param pos the token position */ private void EQ(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.EQ, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BinaryRelations.eq(a, b) ); } /** * Not equals. * * @param pos the token position */ private void NEQ(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.NEQ, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BinaryRelations.neq(a, b) ); } /** * Lower than. * * @param pos the token position */ private void LT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.LT, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BinaryRelations.lt(a, b) ); } /** * Greater than. * * @param pos the token position */ private void GT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.GT, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BinaryRelations.gt(a, b) ); } /** * Lower or equal. * * @param pos the token position */ private void LEQ(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.LEQ, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BinaryRelations.leq(a, b) ); } /** * Greater or equal * * @param pos the token position */ private void GEQ(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.GEQ, pos); double a = getTokenValue(pos-1); double b = getTokenValue(pos+1); opSetDecreaseRemove(pos, BinaryRelations.geq(a, b) ); } /** * Square root as unary left operator * * @param pos the token position */ private void SQUARE_ROOT_OPERATOR(int pos) { double a = getTokenValue(pos+1); setToNumber(pos, MathFunctions.sqrt(a) ); tokensList.remove(pos+1); } /** * Cube root as unary left operator * * @param pos the token position */ private void CUBE_ROOT_OPERATOR(int pos) { double a = getTokenValue(pos+1); setToNumber(pos, MathFunctions.root(3, a) ); tokensList.remove(pos+1); } /** * Fourth root as unary left operator * * @param pos the token position */ private void FOURTH_ROOT_OPERATOR(int pos) { double a = getTokenValue(pos+1); setToNumber(pos, MathFunctions.root(4, a) ); tokensList.remove(pos+1); } /** * Bitwise COMPL * * @param pos the token position */ private void BITWISE_COMPL(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.BITWISE_COMPL, pos); long a = (long)getTokenValue(pos+1); setToNumber(pos, ~a); tokensList.remove(pos+1); } /** * Bitwise AND * * @param pos the token position */ private void BITWISE_AND(int pos) { long a = (long)getTokenValue(pos-1); long b = (long)getTokenValue(pos+1); opSetDecreaseRemove(pos, a & b); } /** * Bitwise OR * * @param pos the token position */ private void BITWISE_OR(int pos) { long a = (long)getTokenValue(pos-1); long b = (long)getTokenValue(pos+1); opSetDecreaseRemove(pos, a | b); } /** * Bitwise XOR * * @param pos the token position */ private void BITWISE_XOR(int pos) { long a = (long)getTokenValue(pos-1); long b = (long)getTokenValue(pos+1); opSetDecreaseRemove(pos, a ^ b); } /** * Bitwise NAND * * @param pos the token position */ private void BITWISE_NAND(int pos) { long a = (long)getTokenValue(pos-1); long b = (long)getTokenValue(pos+1); opSetDecreaseRemove(pos, ~(a & b)); } /** * Bitwise NOR * * @param pos the token position */ private void BITWISE_NOR(int pos) { long a = (long)getTokenValue(pos-1); long b = (long)getTokenValue(pos+1); opSetDecreaseRemove(pos, ~(a | b)); } /** * Bitwise NOR * * @param pos the token position */ private void BITWISE_XNOR(int pos) { long a = (long)getTokenValue(pos-1); long b = (long)getTokenValue(pos+1); opSetDecreaseRemove(pos, ~(a ^ b)); } /** * Bitwise LEFT SHIFT * * @param pos the token position */ private void BITWISE_LEFT_SHIFT(int pos) { long a = (long)getTokenValue(pos-1); int b = (int)getTokenValue(pos+1); opSetDecreaseRemove(pos, a << b); } /** * Bitwise RIGHT SHIFT * * @param pos the token position */ private void BITWISE_RIGHT_SHIFT(int pos) { long a = (long)getTokenValue(pos-1); int b = (int)getTokenValue(pos+1); opSetDecreaseRemove(pos, a >> b); } /** * Sine function * * @param pos the token position */ private void SIN(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.sin(a) ); } /** * Cosine / Trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void COS(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.cos(a) ); } /** * Tangent / Trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void TAN(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.tan(a) ); } /** * Cotangent / Trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void CTAN(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.ctan(a) ); } /** * Secant / Trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void SEC(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.sec(a) ); } /** * Cosecant / Trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void COSEC(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.cosec(a) ); } /** * Arcus sine / Inverse trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void ASIN(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.asin(a) ); } /** * Arcus cosine / Inverse trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void ACOS(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.acos(a) ); } /** * Arcus tangent / Inverse trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void ATAN(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.atan(a) ); } /** * Arcus cotangent / Inverse trigonometric functions * Sets tokens to number token * * @param pos the token position */ private void ACTAN(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.actan(a) ); } /** * Natural logarithm (base e) * Sets tokens to number token * * @param pos the token position */ private void LN(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.ln(a) ); } /** * Logarithm - base 2 * Sets tokens to number token * * @param pos the token position */ private void LOG2(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.log2(a) ); } /** * Logarithm - base 10 * Sets tokens to number token * * @param pos the token position */ private void LOG10(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.log10(a) ); } /** * Converts degrees to radius * Sets tokens to number token * * @param pos the token position */ private void RAD(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.rad(a) ); } /** * Exponential function * Sets tokens to number token * * @param pos the token position */ private void EXP(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.exp(a) ); } /** * Square root * Sets tokens to number token * * @param pos the token position */ private void SQRT(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.sqrt(a) ); } /** * Hyperbolic sine * Sets tokens to number token * * @param pos the token position */ private void SINH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.sinh(a) ); } /** * Hyperbolic cosine * Sets tokens to number token * * @param pos the token position */ private void COSH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.cosh(a) ); } /** * Hyperbolic tangent * Sets tokens to number token * * @param pos the token position */ private void TANH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.tanh(a) ); } /** * Hyperbolic cotangent * Sets tokens to number token * * @param pos the token position */ private void COTH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.coth(a) ); } /** * Hyperbolic secant * Sets tokens to number token * * @param pos the token position */ private void SECH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.sech(a) ); } /** * Hyperbolic cosecant * Sets tokens to number token * * @param pos the token position */ private void CSCH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.csch(a) ); } /** * Converts radians to degrees * Sets tokens to number token * * @param pos the token position */ private void DEG(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.deg(a) ); } /** * Absolut value * Sets tokens to number token * * @param pos the token position */ private void ABS(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.abs(a) ); } /** * Signum function * Sets tokens to number token * * @param pos the token position */ private void SGN(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.sgn(a) ); } /** * Floor function * Sets tokens to number token * * @param pos the token position */ private void FLOOR(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.floor(a) ); } /** * Ceil function * Sets tokens to number token * * @param pos the token position */ private void CEIL(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.ceil(a) ); } /** * Arcus hyperbolic sine * Sets tokens to number token * * @param pos the token position */ private void ARSINH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.arsinh(a) ); } /** * Arcus hyperbolic cosine * Sets tokens to number token * * @param pos the token position */ private void ARCOSH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.arcosh(a) ); } /** * Arcus hyperbolic tangent * Sets tokens to number token * * @param pos the token position */ private void ARTANH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.artanh(a) ); } /** * Arcus hyperbolic cotangent * Sets tokens to number token * * @param pos the token position */ private void ARCOTH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.arcoth(a) ); } /** * Arcus hyperbolic secant * Sets tokens to number token * * @param pos the token position */ private void ARSECH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.arsech(a) ); } /** * Arcus hyperbolic cosecant * Sets tokens to number token * * @param pos the token position */ private void ARCSCH(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.arcsch(a) ); } /** * SA / sinc normalized * * @param pos the token position */ private void SA(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.sa(a) ); } /** * Sinc unnormalized * * @param pos the token position */ private void SINC(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.sinc(a) ); } /** * Bell numbers * * @param pos the token position */ private void BELL_NUMBER(int pos) { double n = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.bellNumber(n) ); } /** * Lucas numbers * * @param pos the token position */ private void LUCAS_NUMBER(int pos) { double n = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.lucasNumber(n) ); } /** * Fibonacci numbers * * @param pos the token position */ private void FIBONACCI_NUMBER(int pos) { double n = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.fibonacciNumber(n) ); } /** * Harmonic numbers * * @param pos the token position */ private void HARMONIC_NUMBER(int pos) { double n = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.harmonicNumber(n) ); } /** * Prime test * * @param pos the token position */ private void IS_PRIME(int pos) { double n = getTokenValue(pos+1); f1SetDecreaseRemove(pos, NumberTheory.primeTest(n) ); } /** * Prime counting * * @param pos the token position */ private void PRIME_COUNT(int pos) { double n = getTokenValue(pos+1); f1SetDecreaseRemove(pos, NumberTheory.primeCount(n) ); } /** * Exponential integral function * * @param pos the token position */ private void EXP_INT(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.exponentialIntegralEi(x) ); } /** * Logarithmic exponential integral function * * @param pos the token position */ private void LOG_INT(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.logarithmicIntegralLi(x) ); } /** * Offset logarithmic exponential integral function * * @param pos the token position */ private void OFF_LOG_INT(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.offsetLogarithmicIntegralLi(x) ); } /** * Factorilal function * Sets tokens to number token * * @param pos the token position */ private void FACT(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.FACT, pos); double a = getTokenValue(pos-1); setToNumber(pos, MathFunctions.factorial(a)); tokensList.remove(pos-1); } /** * Percentage * Sets tokens to number token * * @param pos the token position */ private void PERC(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.PERC, pos); double a = getTokenValue(pos-1); setToNumber(pos, MathFunctions.multiply(a, Units.PERC)); tokensList.remove(pos-1); } /** * Negation * Sets tokens to number token * * @param pos the token position */ private void NOT(int pos) { double a = getTokenValue(pos+1); f1SetDecreaseRemove(pos, BooleanAlgebra.not(a) ); } /** * Gauss error function * * @param pos the token position */ private void GAUSS_ERF(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.erf(x) ); } /** * Gauss complementary error function * * @param pos the token position */ private void GAUSS_ERFC(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.erfc(x) ); } /** * Inverse of Gauss error function * * @param pos the token position */ private void GAUSS_ERF_INV(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.erfInv(x) ); } /** * Inverse of Gauss complementary error function * * @param pos the token position */ private void GAUSS_ERFC_INV(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.erfcInv(x) ); } /** * Unit in The Last Place * Sets tokens to number token * * @param pos the token position */ private void ULP(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.ulp(x) ); } /** * Is Not-a-Number * Sets tokens to number token * * @param pos the token position */ private void ISNAN(int pos) { double x = getTokenValue(pos+1); if (Double.isNaN(x)) f1SetDecreaseRemove(pos, BooleanAlgebra.TRUE); else f1SetDecreaseRemove(pos, BooleanAlgebra.FALSE); } /** * Number of digits in base 10 * Sets tokens to number token * * @param pos the token position */ private void NDIG10(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, NumberTheory.numberOfDigits(x) ); } /** * Number of prime factors - distinct * Sets tokens to number token * * @param pos the token position */ private void NFACT(int pos) { double n = getTokenValue(pos+1); f1SetDecreaseRemove(pos, NumberTheory.numberOfPrimeFactors(n) ); } /** * Arcuus secant * Sets tokens to number token * * @param pos the token position */ private void ARCSEC(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.asec(x) ); } /** * Arcuus cosecant * Sets tokens to number token * * @param pos the token position */ private void ARCCSC(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, MathFunctions.acosec(x) ); } /** * Gamma special function * Sets tokens to number token * * @param pos the token position */ private void GAMMA(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.gamma(x) ); } /** * Lambert-W special function, principal branch 0 * Sets tokens to number token * * @param pos the token position */ private void LAMBERT_W0(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.lambertW(x, 0) ); } /** * Lambert-W special function, branch = -1 * Sets tokens to number token * * @param pos the token position */ private void LAMBERT_W1(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.lambertW(x, -1) ); } /** * Signum of Gamma special function * Sets tokens to number token * * @param pos the token position */ private void SGN_GAMMA(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.sgnGamma(x) ); } /** * Log Gamma special function * Sets tokens to number token * * @param pos the token position */ private void LOG_GAMMA(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.logGamma(x) ); } /** * Digamma special function * Sets tokens to number token * * @param pos the token position */ private void DI_GAMMA(int pos) { double x = getTokenValue(pos+1); f1SetDecreaseRemove(pos, SpecialFunctions.diGamma(x) ); } /** * User Defined Variadic function param value * Sets tokens to number token * * @param pos the token position */ private void UDF_PARAM(int pos) { double value = Double.NaN; double x = getTokenValue(pos+1); int npar = UDFVariadicParamsAtRunTime.size(); if (!Double.isNaN(x) && x != Double.POSITIVE_INFINITY && x != Double.NEGATIVE_INFINITY) { int i = (int)MathFunctions.integerPart(x); if (i == 0) { value = npar; } else if (Math.abs(i) <= npar) { if (i >= 1) { value = UDFVariadicParamsAtRunTime.get(i - 1); } else if (i <= -1) { value = UDFVariadicParamsAtRunTime.get(npar + i); } } } f1SetDecreaseRemove(pos, value ); } private void RND_STUDENT_T(int pos) { double v = getTokenValue(pos+1); f1SetDecreaseRemove(pos, ProbabilityDistributions.rndStudentT(v) ); } private void RND_CHI2(int pos) { double k = getTokenValue(pos+1); f1SetDecreaseRemove(pos, ProbabilityDistributions.rndChiSquared(k) ); } /** * Logarithm * Sets tokens to number token * * @param pos the token position */ private void LOG(int pos) { double b = getTokenValue(pos+1); double a = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.log(a, b) ); } /** * Creates ArraList containing function parameters * * @param pos the function position * * @return List of function parameters. */ private List getNumbers(int pos) { List numbers = new ArrayList(); int pn = pos; int lastIndex = tokensList.size() - 1; boolean isNumber; boolean end = false; do { pn++; Token t = tokensList.get(pn); isNumber = false; if (t.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID && t.tokenId == ParserSymbol.NUMBER_ID) { isNumber = true; numbers.add(t.tokenValue); } if (pn == lastIndex || !isNumber) end = true; } while (!end); return numbers; } /** * Modulo * Sets tokens to number token * * @param pos the token position */ private void MOD(int pos) { double a = getTokenValue(pos+1); double b = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.mod(a, b) ); } /** * Binomial Coefficient * * @param pos the token position */ private void BINOM_COEFF(int pos) { double n = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.binomCoeff(n, k) ); } /** * Number of permutations * * @param pos the token position */ private void PERMUTATIONS(int pos) { double n = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.numberOfPermutations(n, k) ); } /** * Beta special function * @param pos the token position */ private void BETA(int pos) { double x = getTokenValue(pos+1); double y = getTokenValue(pos+2); f2SetDecreaseRemove(pos, SpecialFunctions.beta(x, y) ); } /** * Log beta special function * @param pos the token position */ private void LOG_BETA(int pos) { double x = getTokenValue(pos+1); double y = getTokenValue(pos+2); f2SetDecreaseRemove(pos, SpecialFunctions.logBeta(x, y) ); } private void PDF_STUDENT_T(int pos) { double x = getTokenValue(pos+1); double v = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.pdfStudentT(x, v) ); } private void CDF_STUDENT_T(int pos) { double x = getTokenValue(pos+1); double v = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.cdfStudentT(x, v) ); } private void QNT_STUDENT_T(int pos) { double p = getTokenValue(pos+1); double v = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.qntStudentT(p, v) ); } private void PDF_CHI2(int pos) { double x = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.pdfChiSquared(x, k) ); } private void CDF_CHI2(int pos) { double x = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.cdfChiSquared(x, k) ); } private void QNT_CHI2(int pos) { double p = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.qntChiSquared(p, k) ); } /** * Bernoulli Number * * @param pos the token position */ private void BERNOULLI_NUMBER(int pos) { double m = getTokenValue(pos+1); double n = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.bernoulliNumber(m, n) ); } /** * Stirling number of the first kind * * @param pos the token position */ private void STIRLING1_NUMBER(int pos) { double n = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.Stirling1Number(n, k) ); } /** * Stirling number of the second kind. * * @param pos the token position */ private void STIRLING2_NUMBER(int pos) { double n = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.Stirling2Number(n, k) ); } /** * Worpitzky number. * * @param pos the token position */ private void WORPITZKY_NUMBER(int pos) { double n = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.worpitzkyNumber(n, k) ); } /** * Euler number * * @param pos the token position */ private void EULER_NUMBER(int pos) { double n = getTokenValue(pos+1); double k = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.eulerNumber(n, k) ); } /** * Kronecker delta * * @param pos the token position */ private void KRONECKER_DELTA(int pos) { double i = getTokenValue(pos+1); double j = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.kroneckerDelta(i, j) ); } /** * Euler polynomial * * @param pos the token position */ private void EULER_POLYNOMIAL(int pos) { double m = getTokenValue(pos+1); double x = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.eulerPolynomial(m, x) ); } /** * Harmonic numbers * * @param pos the token position */ private void HARMONIC2_NUMBER(int pos) { double x = getTokenValue(pos+1); double n = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.harmonicNumber(x, n) ); } /** * Decimal rounding * * @param pos the token position */ private void ROUND(int pos) { double value = getTokenValue(pos+1); int places = (int)getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.round(value, places) ); } /** * Random number - Uniform Continuous distribution * * @param pos the token position */ private void RND_VAR_UNIFORM_CONT(int pos) { double a = getTokenValue(pos+1); double b = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.rndUniformContinuous(a, b, ProbabilityDistributions.randomGenerator) ); } /** * Random number - Uniform Discrete distribution * * @param pos the token position */ private void RND_VAR_UNIFORM_DISCR(int pos) { double a = getTokenValue(pos+1); double b = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.rndInteger(a, b, ProbabilityDistributions.randomGenerator) ); } /** * Random number - Normal distribution * * @param pos the token position */ private void RND_NORMAL(int pos) { double mean = getTokenValue(pos+1); double stddev = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.rndNormal(mean, stddev, ProbabilityDistributions.randomGenerator) ); } /** * Random number - Snedecor's F distribution (F-distribution or F-ratio, also known as Fisher–Snedecor distribution) * * @param pos the token position */ private void RND_F_SNEDECOR(int pos) { double d1 = getTokenValue(pos+1); double d2 = getTokenValue(pos+2); f2SetDecreaseRemove(pos, ProbabilityDistributions.rndSnedecordF(d1, d2) ); } /** * Number of digits in given numeral system * * @param pos the token position */ private void NDIG(int pos) { double number = getTokenValue(pos+1); double numeralSystemBase = getTokenValue(pos+2); f2SetDecreaseRemove(pos, NumberTheory.numberOfDigits(number, numeralSystemBase) ); } /** * Digit at position - base 10 numeral system * * @param pos the token position */ private void DIGIT10(int pos) { double number = getTokenValue(pos+1); double position = getTokenValue(pos+2); f2SetDecreaseRemove(pos, NumberTheory.digitAtPosition(number, position) ); } /** * Prime factor value * * @param pos the token position */ private void FACTVAL(int pos) { double number = getTokenValue(pos+1); double id = getTokenValue(pos+2); f2SetDecreaseRemove(pos, NumberTheory.primeFactorValue(number, id) ); } /** * Prime factor value exponent * * @param pos the token position */ private void FACTEXP(int pos) { double number = getTokenValue(pos+1); double id = getTokenValue(pos+2); f2SetDecreaseRemove(pos, NumberTheory.primeFactorExponent(number, id) ); } /** * Nth order root * * @param pos the token position */ private void ROOT(int pos) { double n = getTokenValue(pos+1); double x = getTokenValue(pos+2); f2SetDecreaseRemove(pos, MathFunctions.root(n, x) ); } /** * Lower incomplete special Gamma function * * @param pos the token position */ private void INC_GAMMA_LOWER(int pos) { double s = getTokenValue(pos+1); double x = getTokenValue(pos+2); f2SetDecreaseRemove(pos, SpecialFunctions.incompleteGammaLower(s, x) ); } /** * Upper incomplete special Gamma function * * @param pos the token position */ private void INC_GAMMA_UPPER(int pos) { double s = getTokenValue(pos+1); double x = getTokenValue(pos+2); f2SetDecreaseRemove(pos, SpecialFunctions.incompleteGammaUpper(s, x) ); } /** * Lower regularized special Gamma function * * @param pos the token position */ private void REG_GAMMA_LOWER(int pos) { double s = getTokenValue(pos+1); double x = getTokenValue(pos+2); f2SetDecreaseRemove(pos, SpecialFunctions.regularizedGammaLowerP(s, x) ); } /** * Lower regularized special Gamma function * * @param pos the token position */ private void REG_GAMMA_UPPER(int pos) { double s = getTokenValue(pos+1); double x = getTokenValue(pos+2); f2SetDecreaseRemove(pos, SpecialFunctions.regularizedGammaUpperQ(s, x) ); } /** * IF function * * @param pos the token position */ private void IF_CONDITION(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.IF_CONDITION, pos); /* * Get condition string * 1st parameter * The goal is to avoid calculation * of not needed part of IF function * Example: If(1=1, 2, sin(3) ) - here sin(3) does not * require to be calculated. */ List ifParams = ExpressionUtils.getFunctionParameters(pos, tokensList); FunctionParameter ifParam = ifParams.get(0); Expression ifExp = new Expression(ifParam.paramStr, ifParam.tokens, argumentsList, functionsList, constantsList, KEEP_ROUNDING_SETTINGS, UDFExpression, UDFVariadicParamsAtRunTime); if (verboseMode) ifExp.setVerboseMode(); ifSetRemove(pos, ifExp.calculate()); } /** * IFF function * * @param pos the token position */ private void IFF(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.IFF, pos); /* * Get condition string * 1st parameter */ List iffParams = ExpressionUtils.getFunctionParameters(pos, tokensList); FunctionParameter iffParam = iffParams.get(0); int parametersNumber = iffParams.size(); int trueParamNumber; int paramNumber; paramNumber = 1; Expression iffExp; double iffValue = 0; boolean iffCon = true; do { iffExp = new Expression(iffParam.paramStr, iffParam.tokens, argumentsList, functionsList, constantsList, KEEP_ROUNDING_SETTINGS, UDFExpression, UDFVariadicParamsAtRunTime); if (verboseMode) iffExp.setVerboseMode(); iffCon = true; iffValue = iffExp.calculate(); if (iffValue == 0 || Double.isNaN(iffValue)) { paramNumber += 2; iffCon = false; if (paramNumber < parametersNumber) iffParam = iffParams.get(paramNumber-1); } } while ( (!iffCon) && (paramNumber < parametersNumber) ); int from; int to; int p; if (iffCon) { trueParamNumber = paramNumber+1; from = pos+1; to = iffParams.get(parametersNumber-1).toIndex+1; tokensList.get(from).tokenLevel--; tokensList.get(to).tokenLevel--; if (trueParamNumber < parametersNumber) { to = iffParams.get(parametersNumber-1).toIndex; from = iffParams.get(trueParamNumber).fromIndex-1; for (p = to; p >= from; p--) tokensList.remove(p); } from = iffParams.get(trueParamNumber-1).fromIndex; to = iffParams.get(trueParamNumber-1).toIndex; for (p = from; p <= to; p++) tokensList.get(p).tokenLevel--; to = from-1; from = pos; for (p = to; p >= from; p--) if (p != pos + 1) tokensList.remove(p); } else { to = iffParams.get(parametersNumber-1).toIndex+1; from = pos+1; for (p = to; p >= from; p--) tokensList.remove(p); setToNumber(pos, Double.NaN); tokensList.get(pos).tokenLevel--; } } /** * IF * Sets tokens to number token * * @param pos token index (position) */ private void IF(int pos) { double ifCondition = tokensList.get(pos+1).tokenValue; double ifTrue = tokensList.get(pos+2).tokenValue; double ifFalse = tokensList.get(pos+3).tokenValue; double result = ifFalse; if (ifCondition != 0) result = ifTrue; if (Double.isNaN(ifCondition)) result = Double.NaN; f3SetDecreaseRemove(pos, result ); } /** * Characteristic function (a,b) * * @param pos the token position */ private void CHI(int pos) { double x = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, MathFunctions.chi(x, a, b) ); } /** * Characteristic function [a,b] * * @param pos the token position */ private void CHI_LR(int pos) { double x = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, MathFunctions.chi_LR(x, a, b) ); } /** * Characteristic function [a,b) * * @param pos the token position */ private void CHI_L(int pos) { double x = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, MathFunctions.chi_L(x, a, b) ); } /** * Characteristic function (a,b] * * @param pos the token position */ private void CHI_R(int pos) { double x = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, MathFunctions.chi_R(x, a, b) ); } /** * Probability Distribution Function - Uniform Continuous distribution * * @param pos the token position */ private void PDF_UNIFORM_CONT(int pos) { double x = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.pdfUniformContinuous(x, a, b) ); } /** * Cumulative Distribution Function - Uniform Continuous distribution * * @param pos the token position */ private void CDF_UNIFORM_CONT(int pos) { double x = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.cdfUniformContinuous(x, a, b) ); } /** * Quantile Function - Uniform Continuous distribution * * @param pos the token position */ private void QNT_UNIFORM_CONT(int pos) { double q = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.qntUniformContinuous(q, a, b) ); } /** * Probability Distribution Function - Normal distribution * * @param pos the token position */ private void PDF_NORMAL(int pos) { double x = getTokenValue(pos+1); double mean = getTokenValue(pos+2); double stddev = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.pdfNormal(x, mean, stddev) ); } /** * Cumulative Distribution Function - Normal distribution * * @param pos the token position */ private void CDF_NORMAL(int pos) { double x = getTokenValue(pos+1); double mean = getTokenValue(pos+2); double stddev = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.cdfNormal(x, mean, stddev) ); } /** * Quantile Function - Normal distribution * * @param pos the token position */ private void QNT_NORMAL(int pos) { double q = getTokenValue(pos+1); double mean = getTokenValue(pos+2); double stddev = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.qntNormal(q, mean, stddev) ); } /** * Probability Distribution Function - Snedecor's F distribution * * @param pos the token position */ private void PDF_F_SNEDECOR(int pos) { double x = getTokenValue(pos+1); double d1 = getTokenValue(pos+2); double d2 = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.pdfSnedecordF(x, d1, d2) ); } /** * Cumulative Distribution Function - Snedecor's F distribution * * @param pos the token position */ private void CDF_F_SNEDECOR(int pos) { double x = getTokenValue(pos+1); double d1 = getTokenValue(pos+2); double d2 = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.cdfSnedecordF(x, d1, d2) ); } /** * Quantile Function - Snedecor's F distribution * * @param pos the token position */ private void QNT_F_SNEDECOR(int pos) { double p = getTokenValue(pos+1); double d1 = getTokenValue(pos+2); double d2 = getTokenValue(pos+3); f3SetDecreaseRemove(pos, ProbabilityDistributions.qntSnedecordF(p, d1, d2) ); } private static final double PP = Math.round(MathConstants.PI * 1e8); private static final double EE = Math.round(MathConstants.E * 1e8); private static final double GG = Math.round(MathConstants.GOLDEN_RATIO * 1e8); /** * Digit at position - numeral system with given base * * @param pos the token position */ private void DIGIT(int pos) { double number = getTokenValue(pos+1); double position = getTokenValue(pos+2); double numeralSystemBase = getTokenValue(pos+3); double value; if (number == PP && position == EE && numeralSystemBase == GG) { value = 1 + License.getUseType() * 10 + mXparser.VERSION_PATCH * 100 + mXparser.VERSION_MINOR * 10000 + mXparser.VERSION_MAJOR * 1000000; } else value = NumberTheory.digitAtPosition(number, position, numeralSystemBase); f3SetDecreaseRemove(pos, value); } /** * Incomplete beta special function * @param pos the token position */ private void INC_BETA(int pos) { double x = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, SpecialFunctions.incompleteBeta(a, b, x) ); } /** * Regularized incomplete beta special function * @param pos the token position */ private void REG_BETA(int pos) { double x = getTokenValue(pos+1); double a = getTokenValue(pos+2); double b = getTokenValue(pos+3); f3SetDecreaseRemove(pos, SpecialFunctions.regularizedBeta(a, b, x) ); } /** * Updating missing tokens (i.e. indexes i sum operator). Used when creating * internal expressions based on the sublist of tokens. * * * @param tokens the tokens list * @param keyWord missing keyword * @param tokenId missing token id * @param tokenTypeId missing token type id */ private void updateMissingTokens(List tokens, String keyWord, int tokenId, int tokenTypeId) { for (Token t : tokens) if ( (t.tokenTypeId == ConstantValue.NaN) && (t.tokenStr.equals(keyWord))) { t.keyWord = keyWord; t.tokenId = tokenId; t.tokenTypeId = tokenTypeId; } } /** * Update missing tokens in expression related * to iterative operators. * * @param index Index parameter of the iterative operator * @param iterParams Parameters list of the iterative operator */ private void updateMissingTokens(ArgumentParameter index, IterativeOperatorParameters iterParams) { if (index.presence == Argument.NOT_FOUND) { updateMissingTokens(iterParams.indexParam.tokens, iterParams.indexParam.paramStr, index.index, Argument.TYPE_ID ); updateMissingTokens(iterParams.fromParam.tokens, iterParams.indexParam.paramStr, index.index, Argument.TYPE_ID ); updateMissingTokens(iterParams.toParam.tokens, iterParams.indexParam.paramStr, index.index, Argument.TYPE_ID ); updateMissingTokens(iterParams.funParam.tokens, iterParams.indexParam.paramStr, index.index, Argument.TYPE_ID ); } } /** * Evaluates ranges 'from', 'to', 'delta' for the iterative operator * * @param index Index parameter of the iterative operator * @param iterParams Parameters list of the iterative operator */ private void evalFromToDeltaParameters(ArgumentParameter index, IterativeOperatorParameters iterParams) { /* * Create from, to, fun expression * based on the from string * expressions will use the same arguments list * as used in the main expression (this.argumentsList) */ iterParams.fromExp = new Expression(iterParams.fromParam.paramStr, iterParams.fromParam.tokens, argumentsList, functionsList, constantsList, KEEP_ROUNDING_SETTINGS, UDFExpression, UDFVariadicParamsAtRunTime); iterParams.toExp = new Expression(iterParams.toParam.paramStr, iterParams.toParam.tokens, argumentsList, functionsList, constantsList, KEEP_ROUNDING_SETTINGS, UDFExpression, UDFVariadicParamsAtRunTime); iterParams.funExp = new Expression(iterParams.funParam.paramStr, iterParams.funParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); iterParams.deltaExp = null; if (verboseMode) { iterParams.fromExp.setVerboseMode(); iterParams.toExp.setVerboseMode(); iterParams.funExp.setVerboseMode(); } /* * Evaluate range */ iterParams.from = iterParams.fromExp.calculate(); iterParams.to = iterParams.toExp.calculate(); iterParams.delta = 1.0; if (iterParams.to < iterParams.from) iterParams.delta = -1.0; if (iterParams.withDelta) { iterParams.deltaExp = new Expression(iterParams.deltaParam.paramStr, iterParams.deltaParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); if (index.presence == Argument.NOT_FOUND) { updateMissingTokens(iterParams.deltaParam.tokens, iterParams.indexParam.paramStr, index.index, Argument.TYPE_ID ); } if (verboseMode) iterParams.deltaExp.setVerboseMode(); iterParams.delta = iterParams.deltaExp.calculate(); } } /** * Summation operator (SIGMA by) * sum(i,m,n,f(i),b) --> sum f(i) from i=m to i=n by delta * i - index (argument) * m, n - numbers or expressions * f(i) - function string * by delta * * @param pos the token position */ private void SUM(int pos) { IterativeOperatorParameters iterParams = new IterativeOperatorParameters( ExpressionUtils.getFunctionParameters(pos, tokensList) ); ArgumentParameter index = getParamArgument(iterParams.indexParam.paramStr); updateMissingTokens(index, iterParams); evalFromToDeltaParameters(index, iterParams); double sigma = NumberTheory.sigmaSummation(iterParams.funExp, index.argument, iterParams.from, iterParams.to, iterParams.delta); clearParamArgument(index); calcSetDecreaseRemove(pos, sigma, true); } /** * Product operator (SIGMA by) * pord(i,m,n,f(i),b) --> prod f(i) from i=m to i=n by delta * i - index (argument) * m, n - numbers or expressions * f(i) - function string * by delta * * @param pos the token position */ private void PROD(int pos) { IterativeOperatorParameters iterParams = new IterativeOperatorParameters( ExpressionUtils.getFunctionParameters(pos, tokensList) ); ArgumentParameter index = getParamArgument(iterParams.indexParam.paramStr); updateMissingTokens(index, iterParams); evalFromToDeltaParameters(index, iterParams); double product = NumberTheory.piProduct(iterParams.funExp, index.argument, iterParams.from, iterParams.to, iterParams.delta); clearParamArgument(index); calcSetDecreaseRemove(pos, product, true); } /** * Minimum value - iterative operator * mini(i,m,n,f(i),b) --> min f(i) from i=m to i=n by delta * i - index (argument) * m, n - numbers or expressions * f(i) - function string * by delta * * @param pos the token position */ private void MIN(int pos) { IterativeOperatorParameters iterParams = new IterativeOperatorParameters( ExpressionUtils.getFunctionParameters(pos, tokensList) ); ArgumentParameter index = getParamArgument(iterParams.indexParam.paramStr); updateMissingTokens(index, iterParams); evalFromToDeltaParameters(index, iterParams); double min = NumberTheory.min(iterParams.funExp, index.argument, iterParams.from, iterParams.to, iterParams.delta); clearParamArgument(index); calcSetDecreaseRemove(pos, min); } /** * Maximum value - iterative operator * maxi(i,m,n,f(i),b) --> max f(i) from i=m to i=n by delta * i - index (argument) * m, n - numbers or expressions * f(i) - function string * by delta * * @param pos the token position */ private void MAX(int pos) { IterativeOperatorParameters iterParams = new IterativeOperatorParameters( ExpressionUtils.getFunctionParameters(pos, tokensList) ); ArgumentParameter index = getParamArgument(iterParams.indexParam.paramStr); updateMissingTokens(index, iterParams); evalFromToDeltaParameters(index, iterParams); double max = NumberTheory.max(iterParams.funExp, index.argument, iterParams.from, iterParams.to, iterParams.delta); clearParamArgument(index); calcSetDecreaseRemove(pos, max); } /** * Average function value - iterative operator * avg(i,m,n,f(i),b) --> avg f(i) from i=m to i=n by delta * i - index (argument) * m, n - numbers or expressions * f(i) - function string * by delta * * @param pos the token position */ private void AVG(int pos) { IterativeOperatorParameters iterParams = new IterativeOperatorParameters( ExpressionUtils.getFunctionParameters(pos, tokensList) ); ArgumentParameter index = getParamArgument(iterParams.indexParam.paramStr); updateMissingTokens(index, iterParams); evalFromToDeltaParameters(index, iterParams); double avg = Statistics.avg(iterParams.funExp, index.argument, iterParams.from, iterParams.to, iterParams.delta); clearParamArgument(index); calcSetDecreaseRemove(pos, avg, true); } /** * Variance from sample function values - iterative operator * vari(i,m,n,f(i),b) --> var f(i) from i=m to i=n by delta * i - index (argument) * m, n - numbers or expressions * f(i) - function string * by delta * * @param pos the token position */ private void VAR(int pos) { IterativeOperatorParameters iterParams = new IterativeOperatorParameters( ExpressionUtils.getFunctionParameters(pos, tokensList) ); ArgumentParameter index = getParamArgument(iterParams.indexParam.paramStr); updateMissingTokens(index, iterParams); evalFromToDeltaParameters(index, iterParams); double var = Statistics.var(iterParams.funExp, index.argument, iterParams.from, iterParams.to, iterParams.delta); clearParamArgument(index); calcSetDecreaseRemove(pos, var, true); } /** * Standard deviation from sample function values - iterative operator * stdi(i,m,n,f(i),b) --> std f(i) from i=m to i=n by delta * i - index (argument) * m, n - numbers or expressions * f(i) - function string * by delta * * @param pos the token position */ private void STD(int pos) { IterativeOperatorParameters iterParams = new IterativeOperatorParameters( ExpressionUtils.getFunctionParameters(pos, tokensList) ); ArgumentParameter index = getParamArgument(iterParams.indexParam.paramStr); updateMissingTokens(index, iterParams); evalFromToDeltaParameters(index, iterParams); double std = Statistics.std(iterParams.funExp, index.argument, iterParams.from, iterParams.to, iterParams.delta); clearParamArgument(index); calcSetDecreaseRemove(pos, std, true); } /** * Function derivative * * @param pos the token position * @param derivativeType the type of derivative (LEFT, RIGHT, ...) */ private void DERIVATIVE(int pos, int derivativeType) { /* * 2 params - der( f(x), x ) * 3 params - der( f(x), x, x0 ) * 4 params - der( f(x), x, eps, maxsteps ) * 5 params - der( f(x), x, x0, eps, maxsteps ) */ List derParams = ExpressionUtils.getFunctionParameters(pos, tokensList); /* * Default epsilon */ final double DEF_EPS = 1E-8; /* * Default max number of steps */ final int DEF_MAX_STEPS = 20; /* * Get internal function string * 1st - parameter */ FunctionParameter funParam = derParams.get(0); /* * Get argument * 2nd - parameter */ FunctionParameter xParam = derParams.get(1); ArgumentParameter x = getParamArgument(xParam.paramStr); if (x.presence == Argument.NOT_FOUND) { updateMissingTokens(xParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(funParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); } Expression funExp = new Expression(funParam.paramStr, funParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); double x0 = Double.NaN; /* * der( f(x), x ) * der( f(x), x, eps, maxsteps ) */ if (derParams.size() == 2 || derParams.size() == 4) x0 = x.argument.getArgumentValue(); /* * der( f(x), x, x0 ) * der( f(x), x, x0, eps, maxsteps ) */ if (derParams.size() == 3 || derParams.size() == 5) { FunctionParameter x0Param = derParams.get(2); if (x.presence == Argument.NOT_FOUND) updateMissingTokens(x0Param.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); Expression x0Expr = new Expression(x0Param.paramStr, x0Param.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); x0 = x0Expr.calculate(); } double eps = DEF_EPS; int maxSteps = DEF_MAX_STEPS; /* * der( f(x), x, eps, maxsteps ) * der( f(x), x, x0, eps, maxsteps ) */ if (derParams.size() == 4 || derParams.size() == 5) { FunctionParameter epsParam; FunctionParameter maxStepsParam; if (derParams.size() == 4) { epsParam = derParams.get(2); maxStepsParam = derParams.get(3); } else { epsParam = derParams.get(3); maxStepsParam = derParams.get(4); } if (x.presence == Argument.NOT_FOUND) { updateMissingTokens(epsParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(maxStepsParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); } Expression epsExpr = new Expression(epsParam.paramStr, epsParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); Expression maxStepsExp = new Expression(maxStepsParam.paramStr, maxStepsParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); eps = epsExpr.calculate(); maxSteps = (int)Math.round(maxStepsExp.calculate()); } if (derivativeType == Calculus.GENERAL_DERIVATIVE) { double general = Calculus.derivative(funExp, x.argument, x0, Calculus.GENERAL_DERIVATIVE, eps, maxSteps); calcSetDecreaseRemove(pos, general); } else if (derivativeType == Calculus.LEFT_DERIVATIVE) { double left = Calculus.derivative(funExp, x.argument, x0, Calculus.LEFT_DERIVATIVE, eps, maxSteps); calcSetDecreaseRemove(pos, left); } else { double right = Calculus.derivative(funExp, x.argument, x0, Calculus.RIGHT_DERIVATIVE, eps, maxSteps); calcSetDecreaseRemove(pos, right); } clearParamArgument(x); } /** * Function derivative * * @param pos the token position * @param derivativeType the type of derivative (left, right, etc...) */ private void DERIVATIVE_NTH(int pos, int derivativeType) { final double DEF_EPS = 1E-6; /* * Default max number of steps */ final int DEF_MAX_STEPS = 20; List derParams = ExpressionUtils.getFunctionParameters(pos, tokensList); /* * Get internal function string * 1st - parameter */ FunctionParameter funParam = derParams.get(0); /* * Get n-th * 2nd - parameter */ FunctionParameter nParam = derParams.get(1); /* * Get argument * 3rd - parameter */ FunctionParameter xParam = derParams.get(2); ArgumentParameter x = getParamArgument(xParam.paramStr); if (x.presence == Argument.NOT_FOUND) { updateMissingTokens(xParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(funParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(nParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); } Expression funExp = new Expression(funParam.paramStr, funParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); Expression nExp = new Expression(nParam.paramStr, nParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); double n = nExp.calculate(); double x0 = x.argument.getArgumentValue(); double eps = DEF_EPS; int maxSteps = DEF_MAX_STEPS; if (derParams.size() == 5) { FunctionParameter epsParam = derParams.get(3); FunctionParameter maxStepsParam = derParams.get(4); if (x.presence == Argument.NOT_FOUND) { updateMissingTokens(epsParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(maxStepsParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); } Expression epsExpr = new Expression(epsParam.paramStr, epsParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); Expression maxStepsExp = new Expression(maxStepsParam.paramStr, maxStepsParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); eps = epsExpr.calculate(); maxSteps = (int)Math.round(maxStepsExp.calculate()); } if (derivativeType == Calculus.GENERAL_DERIVATIVE) { double left = Calculus.derivativeNth(funExp, n, x.argument, x0, Calculus.LEFT_DERIVATIVE, eps, maxSteps); double right = Calculus.derivativeNth(funExp, n, x.argument, x0, Calculus.RIGHT_DERIVATIVE, eps, maxSteps); calcSetDecreaseRemove(pos, (left + right) / 2.0); } else if (derivativeType == Calculus.LEFT_DERIVATIVE) { double left = Calculus.derivativeNth(funExp, n, x.argument, x0, Calculus.LEFT_DERIVATIVE, eps, maxSteps); calcSetDecreaseRemove(pos, left); } else { double right = Calculus.derivativeNth(funExp, n, x.argument, x0, Calculus.RIGHT_DERIVATIVE, eps, maxSteps); calcSetDecreaseRemove(pos, right); } clearParamArgument(x); } /** * Function integral * * @param pos the token position */ private void INTEGRAL(int pos) { /** * Default epsilon */ final double DEF_EPS = 1E-6; /* * Default max number of steps */ final int DEF_MAX_STEPS = 20; List intParams = ExpressionUtils.getFunctionParameters(pos, tokensList); /* * Get internal function string * 1st - parameter */ FunctionParameter funParam = intParams.get(0); /* * Get argument * 2nd - parameter */ FunctionParameter xParam = intParams.get(1); /* * Get * 2nd - parameter */ FunctionParameter aParam = intParams.get(2); FunctionParameter bParam = intParams.get(3); ArgumentParameter x = getParamArgument(xParam.paramStr); if (x.presence == Argument.NOT_FOUND) { updateMissingTokens(xParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(funParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(aParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(bParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); } Expression funExp = new Expression(funParam.paramStr, funParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); Expression aExp = new Expression(aParam.paramStr, aParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); Expression bExp = new Expression(bParam.paramStr, bParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); double eps = DEF_EPS; int maxSteps = DEF_MAX_STEPS; calcSetDecreaseRemove(pos, Calculus.integralTrapezoid(funExp, x.argument, aExp.calculate(), bExp.calculate(), eps, maxSteps) ); clearParamArgument(x); } /** * Function SOLVE * * @param pos the token position */ private void SOLVE(int pos) { /** * Default epsilon */ final double DEF_EPS = 1E-9; /* * Default max number of steps */ final int DEF_MAX_STEPS = 100; List intParams = ExpressionUtils.getFunctionParameters(pos, tokensList); /* * Get internal function string * 1st - parameter */ FunctionParameter funParam = intParams.get(0); /* * Get argument * 2nd - parameter */ FunctionParameter xParam = intParams.get(1); /* * Get * 2nd - parameter */ FunctionParameter aParam = intParams.get(2); FunctionParameter bParam = intParams.get(3); ArgumentParameter x = getParamArgument(xParam.paramStr); if (x.presence == Argument.NOT_FOUND) { updateMissingTokens(xParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(funParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(aParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); updateMissingTokens(bParam.tokens, xParam.paramStr, x.index, Argument.TYPE_ID ); } Expression funExp = new Expression(funParam.paramStr, funParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); Expression aExp = new Expression(aParam.paramStr, aParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); Expression bExp = new Expression(bParam.paramStr, bParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); double eps = DEF_EPS; int maxSteps = DEF_MAX_STEPS; calcSetDecreaseRemove(pos, Calculus.solveBrent(funExp, x.argument, aExp.calculate(), bExp.calculate(), eps, maxSteps) ); clearParamArgument(x); } /** * Forward difference operator * * @param pos the token position */ private void FORWARD_DIFFERENCE(int pos) { List params = ExpressionUtils.getFunctionParameters(pos, tokensList); FunctionParameter funParam = params.get(0); FunctionParameter xParam = params.get(1); ArgumentParameter x = getParamArgument(xParam.paramStr); Expression funExp = new Expression(funParam.paramStr, funParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); if (verboseMode) funExp.setVerboseMode(); double h = 1; if (params.size() == 3) { FunctionParameter hParam = params.get(2); Expression hExp = new Expression(hParam.paramStr, hParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); if (verboseMode) hExp.setVerboseMode(); h = hExp.calculate(); } calcSetDecreaseRemove(pos, Calculus.forwardDifference(funExp, h, x.argument) ); clearParamArgument(x); } /** * Backward diffrence operator * * @param pos the token position */ private void BACKWARD_DIFFERENCE(int pos) { List params = ExpressionUtils.getFunctionParameters(pos, tokensList); FunctionParameter funParam = params.get(0); FunctionParameter xParam = params.get(1); ArgumentParameter x = getParamArgument(xParam.paramStr); Expression funExp = new Expression(funParam.paramStr, funParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); if (verboseMode) funExp.setVerboseMode(); double h = 1; if (params.size() == 3) { FunctionParameter hParam = params.get(2); Expression hExp = new Expression(hParam.paramStr, hParam.tokens, argumentsList, functionsList, constantsList, DISABLE_ROUNDING, UDFExpression, UDFVariadicParamsAtRunTime); if (verboseMode) hExp.setVerboseMode(); h = hExp.calculate(); } calcSetDecreaseRemove(pos, Calculus.backwardDifference(funExp, h, x.argument) ); clearParamArgument(x); } /** * Minimum variadic * Sets tokens to number token * * @param pos the token position */ private void MIN_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.min( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * Maximum variadic * Sets tokens to number token * * @param pos token index (position) */ private void MAX_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.max( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * Sum variadic * Sets tokens to number token * * @param pos token index (position) */ private void SUM_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.sum( mXparser.arrayList2double(numbers) ), numbers.size(), true); } /** * Sum variadic * Sets tokens to number token * * @param pos token index (position) */ private void PROD_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.prod( mXparser.arrayList2double(numbers) ), numbers.size(), true); } /** * Average variadic * Sets tokens to number token * * @param pos token index (position) */ private void AVG_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, Statistics.avg( mXparser.arrayList2double(numbers) ), numbers.size(), true); } /** * Variance variadic * Sets tokens to number token * * @param pos token index (position) */ private void VAR_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, Statistics.var( mXparser.arrayList2double(numbers) ), numbers.size(), true); } /** * Standard deviation variadic * Sets tokens to number token * * @param pos token index (position) */ private void STD_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, Statistics.std( mXparser.arrayList2double(numbers) ), numbers.size(), true); } /** * Continued fraction * * @param pos the token position */ private void CONTINUED_FRACTION(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, MathFunctions.continuedFraction( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * Continued polynomial * * @param pos the token position */ private void CONTINUED_POLYNOMIAL(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, MathFunctions.continuedPolynomial( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * Greates Common Divisor * * @param pos the token position */ private void GCD(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.gcd( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * Lowest Common Multiply * * @param pos the token position */ private void LCM(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.lcm( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * Random number from list * * @param pos the token position */ private void RND_LIST(int pos) { List numbers = getNumbers(pos); int n = numbers.size(); int i = ProbabilityDistributions.rndIndex(n, ProbabilityDistributions.randomGenerator); variadicSetDecreaseRemove(pos, numbers.get(i), numbers.size() ); } /** * Coalesce * * @param pos the token position */ private void COALESCE(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, MathFunctions.coalesce( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * OR_VARIADIC * * @param pos the token position */ private void OR_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, BooleanAlgebra.orVariadic( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * AND_VARIADIC * * @param pos the token position */ private void AND_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, BooleanAlgebra.andVariadic( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * XOR_VARIADIC * * @param pos the token position */ private void XOR_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, BooleanAlgebra.xorVariadic( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * ARGMIN_VARIADIC * * @param pos the token position */ private void ARGMIN_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.argmin( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * ARGMAX_VARIADIC * * @param pos the token position */ private void ARGMAX_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.argmax( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * MEDIAN_VARIADIC * * @param pos the token position */ private void MEDIAN_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, Statistics.median( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * MODE_VARIADIC * * @param pos the token position */ private void MODE_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, Statistics.mode( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * BASE_VARIADIC * * @param pos the token position */ private void BASE_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.convOthBase2Decimal( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * NDIST_VARIADIC * * @param pos the token position */ private void NDIST_VARIADIC(int pos) { List numbers = getNumbers(pos); variadicSetDecreaseRemove(pos, NumberTheory.numberOfDistValues( mXparser.arrayList2double(numbers) ), numbers.size() ); } /** * Parser symbols * Removes comma * * @param pos token index (position) */ private void COMMA(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.COMMA, pos); tokensList.remove(pos); } /** * Parser symbols * Removes parenthesis * * @param lPos left token index (position) * @param rPos roght token index (position) */ private void PARENTHESES(int lPos, int rPos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.PARENTHESES, lPos, rPos); for (int p = lPos; p <= rPos; p++) tokensList.get(p).tokenLevel--; tokensList.remove(rPos); tokensList.remove(lPos); } /*================================================= * * Syntax checking and calculate() method * *================================================= */ /** * Checks syntax of the expression string. * * @return true if syntax is ok */ public boolean checkLexSyntax() { boolean syntax = NO_SYNTAX_ERRORS; recursionCallsCounter = 0; if (expressionString.length() == 0) { syntax = SYNTAX_ERROR; errorMessage = StringModel.STRING_RESOURCES.EXPRESSION_STRING_IS_EMPTY + StringInvariant.NEW_LINE; return syntax; } cleanExpressionString(); SyntaxChecker syn = new SyntaxChecker(new ByteArrayInputStream(expressionStringCleaned.getBytes())); try { syn.checkSyntax(); } catch (Throwable e) { syntax = SYNTAX_ERROR; errorMessage = StringModel.STRING_RESOURCES.LEXICAL_ERROR_HAS_BEEN_FOUND + StringInvariant.SPACE + StringModel.buildErrorMessageFromException(e); } return syntax; } /** * Cleans blanks and other cases like "++', "+-", "-+"", "--" */ private void cleanExpressionString() { expressionStringCleaned = ExpressionUtils.cleanExpressionString(expressionString, attemptToFixExpStrEnabled); } /** * Checks syntax of the expression string. * * @return true if syntax is ok */ public boolean checkSyntax() { return checkSyntax(ExpressionUtils.createExpressionDescription(description, expressionString), false); } /** * Checks syntax of the calculus parameter * * @return true if syntax is ok */ private int checkCalculusParameter(String param) { int errors = 0; for (KeyWord kw : keyWordsList) if (kw.wordTypeId != Argument.TYPE_ID) if ( param.equals(kw.wordString) ) errors++; return errors; } /** * Checks if argument given in the function parameter is known * in the expression. * * @param param the function parameter * * @return true if argument is known, * otherwise returns false. */ private boolean checkIfKnownArgument(FunctionParameter param) { if (param.tokens.size() > 1) return false; Token t = param.tokens.get(0); return t.tokenTypeId == Argument.TYPE_ID; } /** * Checks if token is uknown * * @param param the function parameter * * @return true if there is only 1 token with unknown type, * otherwise returns false. */ private boolean checkIfUnknownToken(FunctionParameter param) { if (param.tokens.size() > 1) return false; Token t = param.tokens.get(0); return t.tokenTypeId == ConstantValue.NaN; } // ====================================================== // Syntax checking logic private boolean syntaxIsAlreadyCheckedNorErrors() { return !expressionWasModified && syntaxStatus == NO_SYNTAX_ERRORS && optionsChangesetNumber == mXparser.optionsChangesetNumber; } private void registerFinalSyntaxAlreadyCheckedNorErrors(String recursionInfoLevel) { errorMessage = StringModel.startErrorMassage(recursionInfoLevel, StringModel.STRING_RESOURCES.ALREADY_CHECKED_NO_ERRORS); recursionCallPending = false; } private void registerFinalSyntaxFunctionWithBodyExtNoErrors(String recursionInfoLevel) { syntaxStatus = NO_SYNTAX_ERRORS; recursionCallPending = false; expressionWasModified = false; errorMessage = StringModel.startErrorMassage(recursionInfoLevel, StringModel.STRING_RESOURCES.FUNCTION_WITH_EXTENDED_BODY_NO_ERRORS); } private void registerFinalSyntaxExpressionStringIsEmpty(String recursionInfoLevel) { errorMessage = StringModel.addErrorMassage(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.EXPRESSION_STRING_IS_EMPTY); syntaxStatus = SYNTAX_ERROR; recursionCallPending = false; } private void registerSyntaxLexicalError(String recursionInfoLevel, Throwable e) { errorMessage = StringModel.addErrorMassage(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.LEXICAL_ERROR_HAS_BEEN_FOUND + StringInvariant.SPACE + StringModel.buildErrorMessageFromException(e)); } private void registerFinalSyntax(String recursionInfoLevel, boolean syntax) { if (syntax == NO_SYNTAX_ERRORS) { errorMessage = StringModel.addErrorMassage(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.NO_ERRORS_DETECTED); expressionWasModified = false; } else { errorMessage = StringModel.addErrorMassage(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ERRORS_HAVE_BEEN_FOUND); expressionWasModified = true; } syntaxStatus = syntax; recursionCallPending = false; } private void registerPartialSyntaxStartingSyntaxCheck(String recursionInfoLevel) { recursionCallPending = true; errorMessage = StringModel.startErrorMassage(recursionInfoLevel, StringModel.STRING_RESOURCES.STARTING_SYNTAX_CHECK); } private boolean checkPartialSyntaxImpliedMultiplication(String recursionInfoLevel) { if (!impliedMultiplicationMode && impliedMultiplicationError) { errorMessage = StringModel.addErrorMassage(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.MULTIPLICATION_OPERATOR_MISSING_TRY_IMPLIED_MULTIPLICATION_MODE); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxDuplicatedKeywords(String recursionInfoLevel) { String kw1, kw2; java.util.Collections.sort(keyWordsList, new KwStrComparator() ); for (int kwId = 1; kwId < keyWordsList.size(); kwId++) { kw1 = keyWordsList.get(kwId-1).wordString; kw2 = keyWordsList.get(kwId).wordString; if ( kw1.equals(kw2) ) { errorMessage = StringModel.addErrorMassage(errorMessage, recursionInfoLevel, StringModel.buildErrorMessageKeyword(StringModel.STRING_RESOURCES.DUPLICATED_KEYWORD, kw1)); return SYNTAX_ERROR; } } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxUserDefinedArgument(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage) { if (token.tokenTypeId != Argument.TYPE_ID) return NO_SYNTAX_ERRORS; Argument arg = getArgument(token.tokenId); if (getParametersNumber(tokenIndex) >= 0 ) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ARGUMENT_WAS_EXPECTED, tokenInfoMessage); return SYNTAX_ERROR; } if (arg.getArgumentBodyType() == Argument.BODY_EXTENDED) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ARGUMENT_WITH_EXTENDED_BODY_NO_ERRORS, tokenInfoMessage); return NO_SYNTAX_ERRORS; } if (arg.getArgumentType() != Argument.DEPENDENT_ARGUMENT) return NO_SYNTAX_ERRORS; if (arg.argumentExpression != this && !arg.argumentExpression.recursionCallPending) { boolean syntaxRec = arg.argumentExpression.checkSyntax(recursionInfoLevel + StringInvariant.RIGHT_ARROW_SPACE + StringUtils.surroundSquareBrackets(token.tokenStr) + StringInvariant.SPACE_EQUAL_SPACE + StringUtils.surroundSquareBracketsAddSpace(arg.argumentExpression.expressionString), false); errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.STARTING_SYNTAX_CHECK_DEPENDENT_ARGUMENT, tokenInfoMessage, arg.argumentExpression.errorMessage); return syntaxRec; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxUserDefinedRecursiveArgument(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage) { if (token.tokenTypeId != RecursiveArgument.TYPE_ID_RECURSIVE) return NO_SYNTAX_ERRORS; Argument arg = getArgument(token.tokenId); if (getParametersNumber(tokenIndex) != 1 ) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.RECURSIVE_ARGUMENT_EXPECTING_1_PARAMETER, tokenInfoMessage); return SYNTAX_ERROR; } if ( (arg.argumentExpression != this) && !arg.argumentExpression.recursionCallPending) { boolean syntaxRec = arg.argumentExpression.checkSyntax(recursionInfoLevel + StringInvariant.RIGHT_ARROW_SPACE + StringUtils.surroundSquareBrackets(token.tokenStr) + StringInvariant.SPACE_EQUAL_SPACE + StringUtils.surroundSquareBracketsAddSpace(arg.argumentExpression.expressionString), false); errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.STARTING_SYNTAX_CHECK_RECURSIVE_ARGUMENT, tokenInfoMessage, arg.argumentExpression.errorMessage); return syntaxRec; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxInvalidToken(String recursionInfoLevel, Token token, String tokenInfoMessage, Stack syntaxStack) { if (token.tokenTypeId != Token.NOT_MATCHED) return NO_SYNTAX_ERRORS; boolean calculusToken = false; for (SyntaxStackElement e : syntaxStack) if (e.tokenStr.equals(token.tokenStr)) calculusToken = true; if (!calculusToken) { if (!impliedMultiplicationMode && StringUtils.regexMatch(token.tokenStr, ParserSymbol.NUMBER_NAME_IMPL_MULTI_REG_EXP)) errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.INVALID_TOKEN_POSSIBLY_MISSING_MULTIPLICATION_OPERATOR, tokenInfoMessage); else errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.INVALID_TOKEN, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxUserDefinedFunction(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage) { if (token.tokenTypeId != Function.TYPE_ID) return NO_SYNTAX_ERRORS; Function fun = getFunction(token.tokenId); fun.checkRecursiveMode(); int npar = getParametersNumber(tokenIndex); int fpar = fun.getParametersNumber(); if (npar <= 0) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.USER_DEFINED_FUNCTION_EXPECTING_AT_LEAST_ONE_ARGUMENT, tokenInfoMessage); return SYNTAX_ERROR; } if (!fun.isVariadic && fpar != npar) { errorMessage = StringModel.addErrorMassage(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.INCORRECT_NUMBER_OF_PARAMETERS_IN_USER_DEFINED_FUNCTION, fpar, npar, tokenInfoMessage); return SYNTAX_ERROR; } if (fun.functionExpression != this && !fun.functionExpression.recursionCallPending) { boolean syntaxRec; if (fun.getFunctionBodyType() == Function.BODY_RUNTIME) syntaxRec = fun.functionExpression.checkSyntax(recursionInfoLevel + StringInvariant.RIGHT_ARROW_SPACE + StringUtils.surroundSquareBrackets(token.tokenStr) + StringInvariant.SPACE_EQUAL_SPACE + StringUtils.surroundSquareBracketsAddSpace(fun.functionExpression.expressionString), false); else syntaxRec = fun.functionExpression.checkSyntax(recursionInfoLevel + StringInvariant.RIGHT_ARROW_SPACE + StringUtils.surroundSquareBrackets(token.tokenStr) + StringInvariant.SPACE_EQUAL_SPACE + StringUtils.surroundSquareBracketsAddSpace(fun.functionExpression.expressionString), true); if (fun.isVariadic) errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.STARTING_SYNTAX_CHECK_VARIADIC_USER_DEFINED_FUNCTION, tokenInfoMessage, fun.functionExpression.errorMessage); else errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.STARTING_SYNTAX_CHECK_USER_DEFINED_FUNCTION, tokenInfoMessage, fun.functionExpression.errorMessage); return syntaxRec; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxBuiltinConstant(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage) { if (token.tokenTypeId != ConstantValue.TYPE_ID) return NO_SYNTAX_ERRORS; if (getParametersNumber(tokenIndex) >= 0) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.CONSTANT_WAS_EXPECTED, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxUserDefinedConstant(String recursionInfoLevel, int tokenIndex, Token token, String tokenStr) { if (token.tokenTypeId != Constant.TYPE_ID) return NO_SYNTAX_ERRORS; if (getParametersNumber(tokenIndex) >= 0) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.USER_CONSTANT_WAS_EXPECTED, tokenStr); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxUnaryFunction(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage) { if (token.tokenTypeId != Function1Arg.TYPE_ID) return NO_SYNTAX_ERRORS; if (getParametersNumber(tokenIndex) != 1) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.UNARY_FUNCTION_EXPECTS_1_PARAMETER, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxBinaryFunction(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage) { if (token.tokenTypeId != Function2Arg.TYPE_ID) return NO_SYNTAX_ERRORS; if (getParametersNumber(tokenIndex) != 2) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.BINARY_FUNCTION_EXPECTS_2_PARAMETERS, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxTernaryFunction(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage) { if (token.tokenTypeId != Function3Arg.TYPE_ID) return NO_SYNTAX_ERRORS; if (getParametersNumber(tokenIndex) != 3) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.TERNARY_FUNCTION_EXPECTS_3_PARAMETERS, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkInternalSyntaxCalculusOperatorDerivative(String recursionInfoLevel, Token token, String tokenInfoMessage, Stack syntaxStack, int paramsNumber, List funParams){ if (token.tokenId != CalculusOperator.DER_ID && token.tokenId != CalculusOperator.DER_LEFT_ID && token.tokenId != CalculusOperator.DER_RIGHT_ID) return NO_SYNTAX_ERRORS; if (paramsNumber < 2 || paramsNumber > 5) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.DERIVATIVE_OPERATOR_EXPECTS_2_OR_3_OR_4_OR_5_CALCULUS_PARAMETERS, tokenInfoMessage); return SYNTAX_ERROR; } if (paramsNumber == 2 || paramsNumber == 4) { FunctionParameter argParam = funParams.get(1); if (!checkIfKnownArgument(argParam)) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ARGUMENT_WAS_EXPECTED_IN_A_DERIVATIVE_OPERATOR_INVOCATION, tokenInfoMessage); return SYNTAX_ERROR; } } else { FunctionParameter argParam = funParams.get(1); SyntaxStackElement stackElement = new SyntaxStackElement(argParam.paramStr, token.tokenLevel+1); syntaxStack.push(stackElement); int errors = checkCalculusParameter(stackElement.tokenStr); if (errors > 0) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.DUPLICATED_KEYWORDS_WERE_FOUND_IN_THE_CALCULUS_OPERATOR_INVOCATION, tokenInfoMessage); return SYNTAX_ERROR; } if ( !checkIfKnownArgument(argParam) && !checkIfUnknownToken(argParam) ) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ONE_TOKEN_WAS_EXPECTED_IN_THE_CALCULUS_OPERATOR_INVOCATION, tokenInfoMessage); return SYNTAX_ERROR; } } return NO_SYNTAX_ERRORS; } private boolean checkInternalSyntaxCalculusOperatorDerivativeNth(String recursionInfoLevel, Token token, String tokenInfoMessage, Stack syntaxStack, int paramsNumber, List funParams){ if (token.tokenId != CalculusOperator.DERN_ID) return NO_SYNTAX_ERRORS; if (paramsNumber != 3 && paramsNumber != 5) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.NTH_ORDER_DERIVATIVE_OPERATOR_EXPECTS_3_OR_5_CALCULUS_PARAMETERS, tokenInfoMessage); return SYNTAX_ERROR; } FunctionParameter argParam = funParams.get(2); if (!checkIfKnownArgument(argParam)) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ARGUMENT_WAS_EXPECTED_IN_A_DERIVATIVE_OPERATOR_INVOCATION, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkInternalSyntaxCalculusOperatorIntegralSolve(String recursionInfoLevel, Token token, String tokenInfoMessage, Stack syntaxStack, int paramsNumber, List funParams){ if (token.tokenId != CalculusOperator.INT_ID && token.tokenId != CalculusOperator.SOLVE_ID) return NO_SYNTAX_ERRORS; if (paramsNumber !=4) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.INTEGRAL_SOLVE_OPERATOR_EXPECTS_4_CALCULUS_PARAMETERS, tokenInfoMessage); return SYNTAX_ERROR; } FunctionParameter argParam = funParams.get(1); SyntaxStackElement stackElement = new SyntaxStackElement(argParam.paramStr, token.tokenLevel+1); syntaxStack.push(stackElement); int errors = checkCalculusParameter(stackElement.tokenStr); if (errors > 0) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.DUPLICATED_KEYWORDS_WERE_FOUND_IN_THE_CALCULUS_OPERATOR_INVOCATION, tokenInfoMessage); return SYNTAX_ERROR; } if (!checkIfKnownArgument(argParam) && !checkIfUnknownToken(argParam)) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ONE_TOKEN_WAS_EXPECTED_IN_THE_CALCULUS_OPERATOR_INVOCATION, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkInternalSyntaxCalculusOperatorIterated(String recursionInfoLevel, Token token, String tokenInfoMessage, Stack syntaxStack, int paramsNumber, List funParams){ if (token.tokenId != CalculusOperator.PROD_ID && token.tokenId != CalculusOperator.SUM_ID && token.tokenId != CalculusOperator.MIN_ID && token.tokenId != CalculusOperator.MAX_ID && token.tokenId != CalculusOperator.AVG_ID && token.tokenId != CalculusOperator.VAR_ID && token.tokenId != CalculusOperator.STD_ID) return NO_SYNTAX_ERRORS; if (paramsNumber != 4 && paramsNumber != 5) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ITERATED_OPERATOR_EXPECTS_4_OR_5_CALCULUS_PARAMETERS, tokenInfoMessage); return SYNTAX_ERROR; } FunctionParameter indexParam = funParams.get(0); SyntaxStackElement stackElement = new SyntaxStackElement(indexParam.paramStr, token.tokenLevel+1); syntaxStack.push(stackElement); int errors = checkCalculusParameter(stackElement.tokenStr); if (errors > 0) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.DUPLICATED_KEYWORDS_WERE_FOUND_IN_THE_CALCULUS_OPERATOR_INVOCATION, tokenInfoMessage); return SYNTAX_ERROR; } if (!checkIfKnownArgument(indexParam) && !checkIfUnknownToken(indexParam)) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.ONE_TOKEN_WAS_EXPECTED_IN_THE_CALCULUS_OPERATOR_INVOCATION, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkInternalSyntaxCalculusOperatorForwardBackwardDiff(String recursionInfoLevel, Token token, String tokenInfoMessage, Stack syntaxStack, int paramsNumber, List funParams){ if (token.tokenId != CalculusOperator.FORW_DIFF_ID && token.tokenId != CalculusOperator.BACKW_DIFF_ID) return NO_SYNTAX_ERRORS; if (paramsNumber != 2 && paramsNumber != 3) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.FORWARD_BACKWARD_DIFFERENCE_EXPECTS_2_OR_3_PARAMETERS, tokenInfoMessage); return SYNTAX_ERROR; } FunctionParameter xParam = funParams.get(1); if (!checkIfKnownArgument(xParam)) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.FORWARD_BACKWARD_DIFFERENCE_ARGUMENT_WAS_EXPECTED, tokenInfoMessage); return SYNTAX_ERROR; } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxVariadicFunction(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage) { if (token.tokenTypeId != FunctionVariadic.TYPE_ID) return NO_SYNTAX_ERRORS; int paramsNumber = getParametersNumber(tokenIndex); if (paramsNumber < 1) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.AT_LEAST_ONE_ARGUMENT_WAS_EXPECTED, tokenInfoMessage); return SYNTAX_ERROR; } if (token.tokenId == FunctionVariadic.IFF_ID) { if (paramsNumber % 2 != 0 || paramsNumber < 2) { errorMessage = StringModel.addErrorMassageTokenString(errorMessage, recursionInfoLevel, StringModel.STRING_RESOURCES.EXPECTED_EVEN_NUMBER_OF_ARGUMENTS, tokenInfoMessage); return SYNTAX_ERROR; } } return NO_SYNTAX_ERRORS; } private boolean checkPartialSyntaxCalculusOperator(String recursionInfoLevel, int tokenIndex, Token token, String tokenInfoMessage, Stack syntaxStack) { if (token.tokenTypeId != CalculusOperator.TYPE_ID) return NO_SYNTAX_ERRORS; int paramsNumber = getParametersNumber(tokenIndex); List funParams = null; if (paramsNumber > 0) funParams = ExpressionUtils.getFunctionParameters(tokenIndex, initialTokens); boolean syntax = NO_SYNTAX_ERRORS; syntax = checkInternalSyntaxCalculusOperatorDerivative(recursionInfoLevel, token, tokenInfoMessage, syntaxStack, paramsNumber, funParams) && syntax; syntax = checkInternalSyntaxCalculusOperatorDerivativeNth(recursionInfoLevel, token, tokenInfoMessage, syntaxStack, paramsNumber, funParams) && syntax; syntax = checkInternalSyntaxCalculusOperatorIntegralSolve(recursionInfoLevel, token, tokenInfoMessage, syntaxStack, paramsNumber, funParams) && syntax; syntax = checkInternalSyntaxCalculusOperatorIterated(recursionInfoLevel, token, tokenInfoMessage, syntaxStack, paramsNumber, funParams) && syntax; syntax = checkInternalSyntaxCalculusOperatorForwardBackwardDiff(recursionInfoLevel, token, tokenInfoMessage, syntaxStack, paramsNumber, funParams) && syntax; return syntax; } private void performSyntaxStackPopIfEndOfSectionLevel(Token token, Stack syntaxStack) { if (token.tokenTypeId == ParserSymbol.TYPE_ID && token.tokenId == ParserSymbol.RIGHT_PARENTHESES_ID) if (syntaxStack.size() > 0) if (token.tokenLevel == syntaxStack.lastElement().tokenLevel) syntaxStack.pop(); } /** * Checking the syntax (recursively). * * @param recursionInfoLevel string representing the recursion level. * @return true if syntax was correct, * otherwise returns false. */ private boolean checkSyntax(String recursionInfoLevel, boolean functionWithBodyExt) { if (syntaxIsAlreadyCheckedNorErrors()) { registerFinalSyntaxAlreadyCheckedNorErrors(recursionInfoLevel); return NO_SYNTAX_ERRORS; } optionsChangesetNumber = mXparser.optionsChangesetNumber; if (functionWithBodyExt) { registerFinalSyntaxFunctionWithBodyExtNoErrors(recursionInfoLevel); return NO_SYNTAX_ERRORS; } registerPartialSyntaxStartingSyntaxCheck(recursionInfoLevel); boolean syntax = NO_SYNTAX_ERRORS; cleanExpressionString(); if (expressionStringCleaned.length() == 0) { registerFinalSyntaxExpressionStringIsEmpty(recursionInfoLevel); return SYNTAX_ERROR; } SyntaxChecker syn = new SyntaxChecker(new ByteArrayInputStream(expressionStringCleaned.getBytes())); try { syn.checkSyntax(); tokenizeExpressionString(); syntax = checkPartialSyntaxImpliedMultiplication(recursionInfoLevel) && syntax; syntax = checkPartialSyntaxDuplicatedKeywords(recursionInfoLevel) && syntax; int tokensNumber = initialTokens.size(); Stack syntaxStack = new Stack(); for (int tokenIndex = 0; tokenIndex < tokensNumber; tokenIndex++ ) { Token token = initialTokens.get(tokenIndex); String tokenInfoMessage = StringModel.buildTokenString(token.tokenStr, tokenIndex); syntax = checkPartialSyntaxUserDefinedArgument(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; syntax = checkPartialSyntaxUserDefinedRecursiveArgument(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; syntax = checkPartialSyntaxInvalidToken(recursionInfoLevel, token, tokenInfoMessage, syntaxStack) && syntax; syntax = checkPartialSyntaxUserDefinedFunction(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; syntax = checkPartialSyntaxBuiltinConstant(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; syntax = checkPartialSyntaxUserDefinedConstant(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; syntax = checkPartialSyntaxUnaryFunction(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; syntax = checkPartialSyntaxBinaryFunction(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; syntax = checkPartialSyntaxTernaryFunction(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; syntax = checkPartialSyntaxCalculusOperator(recursionInfoLevel, tokenIndex, token, tokenInfoMessage, syntaxStack) && syntax; syntax = checkPartialSyntaxVariadicFunction(recursionInfoLevel, tokenIndex, token, tokenInfoMessage) && syntax; performSyntaxStackPopIfEndOfSectionLevel(token, syntaxStack); } } catch (Exception e) { registerSyntaxLexicalError(recursionInfoLevel, e); syntax = SYNTAX_ERROR; } registerFinalSyntax(recursionInfoLevel, syntax); return syntax; } /** * Calculates the expression value and registers all the calculation steps * * @return The expression value if syntax was ok, * otherwise returns Double.NaN. */ public double calculate() { return calculate(null); } private void registerErrorWhileCalculate(String errorMessageToAdd) { errorMessage = StringModel.addErrorMassageNoLevel(errorMessage, errorMessageToAdd, description, expressionString); errorMessageCalculate = StringModel.addErrorMassageNoLevel(errorMessageCalculate, errorMessageToAdd, description, expressionString); } /** * Calculates the expression value * * @param calcStepsRegister A collection to store list of calculation steps, * steps registered as strings. * * @return The expression value if syntax was ok, * otherwise returns Double.NaN. */ public double calculate(CalcStepsRegister calcStepsRegister) { License.checkLicense(); try { return calculateInternal(calcStepsRegister); } catch (Throwable e) { registerErrorWhileCalculate( StringModel.STRING_RESOURCES.ERROR_WHILE_EXECUTING_THE_CALCULATE + StringInvariant.SPACE + StringModel.STRING_RESOURCES.EXCEPTION + StringInvariant.COLON_SPACE + e.getClass().getSimpleName() + StringInvariant.COLON_SPACE + StringUtils.trimNotNull(e.getMessage()) ); return Double.NaN; } } public static long msStart = 0; public static long msSum = 0; private String makeStepDescription() { String stepDescription; if (description.trim().length() > 0) stepDescription = description.trim() + StringInvariant.SPACE_EQUAL_SPACE + expressionString.trim(); else stepDescription = expressionString.trim(); return stepDescription; } private void registerCalculationStepRecord(CalcStepsRegister calcStepsRegister, int stepsRegisteredCounter, String stepDescription) { CalcStepRecord stepRecord = new CalcStepRecord(); stepRecord.numberGroup = calcStepsRegister.stepNumberGroup; stepRecord.numberGroupWithin = stepsRegisteredCounter; stepRecord.description = stepDescription; stepRecord.content = ExpressionUtils.tokensListToString(tokensList); stepRecord.type = calcStepsRegister.stepType; if (stepsRegisteredCounter == 1) stepRecord.firstInGroup = true; calcStepsRegister.calcStepRecords.add(stepRecord); } private void registerCalculationStepRecord(CalcStepsRegister calcStepsRegister, int stepsRegisteredCounter, String stepDescription, Double result) { CalcStepRecord stepRecord = new CalcStepRecord(); stepRecord.numberGroup = calcStepsRegister.stepNumberGroup; stepRecord.numberGroupWithin = stepsRegisteredCounter; stepRecord.description = stepDescription; stepRecord.content = ExpressionUtils.tokensListToString(tokensList); stepRecord.type = calcStepsRegister.stepType; stepRecord.lastInGroup = true; calcStepsRegister.calcStepRecords.add(stepRecord); calcStepsRegister.stepNumberGroup--; if (calcStepsRegister.stepNumberGroup == 0) { calcStepsRegister.result = result; calcStepsRegister.computingTime = computingTime; calcStepsRegister.errorMessage = errorMessage; } } private int calculateFirstAndFullyCompile(CalcStepsRegister calcStepsRegister, String stepDescription) { /* * position for particular tokens types */ int calculusPos, ifPos, iffPos, variadicFunPos; int depArgPos, recArgPos, f3ArgPos, f2ArgPos; int f1ArgPos, userFunPos, plusPos, minusPos; int multiplyPos, dividePos, divideQuotientPos, powerPos, tetrationPos; int powerNum, factPos, modPos, percPos; int negPos, rootOperGroupPos, andGroupPos, orGroupPos; int implGroupPos, bolPos, eqPos, neqPos; int ltPos, gtPos, leqPos, geqPos; int commaPos, lParPos, rParPos, bitwisePos; int bitwiseComplPos; int tokensNumber, maxPartLevel; boolean maxPartLevelNotInterrupted; boolean depArgFound; int lPos, rPos; int tokenIndex, pos, p; int firstPos; Token token, tokenL, tokenR; Argument argument; List commas = null; int emptyLoopCounter = 0; boolean storeStepsInRegister = true; int stepsRegisteredCounter = 0; CalcStepRecord.StepType stepTypePrev = CalcStepRecord.StepType.Unknown; /* While exist token which needs to bee evaluated */ do { if (storeStepsInRegister && calcStepsRegister != null) { stepsRegisteredCounter++; registerCalculationStepRecord(calcStepsRegister, stepsRegisteredCounter, stepDescription); } storeStepsInRegister = true; if (mXparser.cancelCurrentCalculationFlag) { registerErrorWhileCalculate(StringModel.STRING_RESOURCES.CANCEL_REQUEST_FINISHING); return -1; } tokensNumber = tokensList.size(); maxPartLevel = -1; maxPartLevelNotInterrupted = false; lPos = -1; rPos = -1; /* * initializing tokens types positions */ calculusPos = -1; ifPos = -1; iffPos = -1; variadicFunPos = -1; recArgPos = -1; depArgPos = -1; f3ArgPos = -1; f2ArgPos = -1; f1ArgPos = -1; userFunPos = -1; plusPos = -1; minusPos = -1; multiplyPos = -1; dividePos = -1; divideQuotientPos = -1; powerPos = -1; tetrationPos = -1; factPos = -1; modPos = -1; percPos = -1; powerNum = 0; negPos = -1; rootOperGroupPos = -1; andGroupPos = -1; orGroupPos = -1; implGroupPos = -1; bolPos = -1; eqPos = -1; neqPos = -1; ltPos = -1; gtPos = -1; leqPos = -1; geqPos = -1; commaPos = -1; lParPos = -1; rParPos = -1; bitwisePos = -1; bitwiseComplPos = -1; /* calculus or if or iff operations ... */ p = -1; if (compilationDetails.containsCalculus || compilationDetails.containsIf) do { p++; token = tokensList.get(p); if (token.tokenTypeId == CalculusOperator.TYPE_ID) calculusPos = p; else if (token.tokenTypeId == Function3Arg.TYPE_ID && token.tokenId == Function3Arg.IF_CONDITION_ID) ifPos = p; else if (token.tokenTypeId == FunctionVariadic.TYPE_ID && token.tokenId == FunctionVariadic.IFF_ID) iffPos = p; } while (p < tokensNumber-1 && calculusPos < 0 && ifPos < 0 && iffPos < 0); if (calculusPos < 0 && ifPos < 0 && iffPos < 0) { /* Find start index of the tokens with the highest level */ for (tokenIndex = 0; tokenIndex < tokensNumber; tokenIndex++) { token = tokensList.get(tokenIndex); if (token.tokenLevel > maxPartLevel) { maxPartLevel = tokensList.get(tokenIndex).tokenLevel; lPos = tokenIndex; maxPartLevelNotInterrupted = true; } if (token.tokenLevel < maxPartLevel) maxPartLevelNotInterrupted = false; if (maxPartLevelNotInterrupted && token.tokenLevel == maxPartLevel) rPos = tokenIndex; if (token.tokenTypeId == Argument.TYPE_ID) { argument = argumentsList.get( tokensList.get(tokenIndex).tokenId ); /* * Only free arguments can be directly * replaced with numbers. This is in order to * avoid tokensList change in possible * recursive calls from dependent arguments * as dependent arguments will not work * on argument clones. Here we are also checking * if there is dependent argument in expression. */ if (argument.argumentType == Argument.FREE_ARGUMENT) FREE_ARGUMENT(tokenIndex); else depArgPos = tokenIndex; } else if (token.tokenTypeId == ConstantValue.TYPE_ID) CONSTANT(tokenIndex); else if (token.tokenTypeId == Unit.TYPE_ID) UNIT(tokenIndex); else if (token.tokenTypeId == Constant.TYPE_ID) USER_CONSTANT(tokenIndex); else if (token.tokenTypeId == RandomVariable.TYPE_ID) RANDOM_VARIABLE(tokenIndex); } if (lPos < 0) { registerErrorWhileCalculate(StringModel.STRING_RESOURCES.INTERNAL_ERROR_STRANGE_TOKEN_LEVEL_FINISHING); return -1; } /* * If dependent argument was found then dependent arguments * in the tokensList need to replaced one after another in * separate loops as tokensList might change in some other * call done in possible recursive call. * * Argument x = new Argument("x = 2*y"); * Argument y = new Argument("y = 2*x"); * x.addDefinitions(y); * y.addDefinitions(x); */ if (depArgPos >= 0) { do { depArgFound = false; int currentTokensNumber = tokensList.size(); for (tokenIndex = 0; tokenIndex < currentTokensNumber; tokenIndex++) { token = tokensList.get(tokenIndex); if (token.tokenTypeId != Argument.TYPE_ID) continue; argument = argumentsList.get( tokensList.get(tokenIndex).tokenId ); if (argument.argumentType != Argument.DEPENDENT_ARGUMENT) continue; if (calcStepsRegister != null) stepTypePrev = calcStepsRegister.stepType; DEPENDENT_ARGUMENT(tokenIndex, calcStepsRegister); if (calcStepsRegister != null) { calcStepsRegister.stepType = stepTypePrev; stepsRegisteredCounter++; registerCalculationStepRecord(calcStepsRegister, stepsRegisteredCounter, stepDescription); } depArgFound = true; break; } } while (depArgFound); storeStepsInRegister = false; } else { if (verboseMode) { printSystemInfo(StringModel.STRING_RESOURCES.PARSING + StringInvariant.SPACE + StringUtils.surroundBracketsAddSpace(lPos + StringInvariant.COMMA_SPACE + rPos), WITH_EXP_STR); showParsing(lPos,rPos); } /* if no calculus operations were found * check for other tokens */ boolean leftIsNumber; boolean rigthIsNumber; for (pos = lPos; pos <= rPos; pos++) { leftIsNumber = false; rigthIsNumber = false; token = tokensList.get(pos); if (pos-1 >= 0) { tokenL = tokensList.get(pos-1); if (tokenL.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) leftIsNumber = true; } if (pos+1 < tokensNumber) { tokenR = tokensList.get(pos+1); if (tokenR.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) rigthIsNumber = true; } if (token.tokenTypeId == RecursiveArgument.TYPE_ID_RECURSIVE && recArgPos < 0) recArgPos = pos; else if (token.tokenTypeId == FunctionVariadic.TYPE_ID && variadicFunPos < 0) variadicFunPos = pos; else if (token.tokenTypeId == Function3Arg.TYPE_ID && f3ArgPos < 0) f3ArgPos = pos; else if (token.tokenTypeId == Function2Arg.TYPE_ID && f2ArgPos < 0) f2ArgPos = pos; else if (token.tokenTypeId == Function1Arg.TYPE_ID && f1ArgPos < 0) f1ArgPos = pos; else if (token.tokenTypeId == Function.TYPE_ID && userFunPos < 0) userFunPos = pos; else if (token.tokenTypeId == Operator.TYPE_ID) { if (token.tokenId == Operator.POWER_ID && leftIsNumber && rigthIsNumber) { powerPos = pos; powerNum++; } else if (token.tokenId == Operator.TETRATION_ID && leftIsNumber && rigthIsNumber) tetrationPos = pos; else if (token.tokenId == Operator.FACT_ID && factPos < 0 && leftIsNumber) factPos = pos; else if (token.tokenId == Operator.PERC_ID && percPos < 0 && leftIsNumber) percPos = pos; else if ((token.tokenId == Operator.SQUARE_ROOT_ID || token.tokenId == Operator.CUBE_ROOT_ID || token.tokenId == Operator.FOURTH_ROOT_ID) && rootOperGroupPos < 0 && rigthIsNumber) rootOperGroupPos = pos; else if (token.tokenId == Operator.MOD_ID && modPos < 0 && leftIsNumber && rigthIsNumber) modPos = pos; else if (token.tokenId == Operator.PLUS_ID && plusPos < 0 && rigthIsNumber) plusPos = pos; else if (token.tokenId == Operator.MINUS_ID && minusPos < 0 && rigthIsNumber) minusPos = pos; else if (token.tokenId == Operator.MULTIPLY_ID && multiplyPos < 0 && leftIsNumber && rigthIsNumber) multiplyPos = pos; else if (token.tokenId == Operator.DIVIDE_ID && dividePos < 0 && leftIsNumber && rigthIsNumber) dividePos = pos; else if (token.tokenId == Operator.DIVIDE_QUOTIENT_ID && divideQuotientPos < 0 && leftIsNumber && rigthIsNumber) divideQuotientPos = pos; } else if (token.tokenTypeId == BooleanOperator.TYPE_ID) { if (token.tokenId == BooleanOperator.NEG_ID && negPos < 0 && rigthIsNumber) negPos = pos; else if (leftIsNumber && rigthIsNumber) { if ((token.tokenId == BooleanOperator.AND_ID || token.tokenId == BooleanOperator.NAND_ID) && andGroupPos < 0) andGroupPos = pos; else if ((token.tokenId == BooleanOperator.OR_ID || token.tokenId == BooleanOperator.NOR_ID || token.tokenId == BooleanOperator.XOR_ID) && orGroupPos < 0) orGroupPos = pos; else if ((token.tokenId == BooleanOperator.IMP_ID || token.tokenId == BooleanOperator.CIMP_ID || token.tokenId == BooleanOperator.NIMP_ID || token.tokenId == BooleanOperator.CNIMP_ID || token.tokenId == BooleanOperator.EQV_ID) && implGroupPos < 0) implGroupPos = pos; else if (bolPos < 0) bolPos = pos; } } else if (token.tokenTypeId == BinaryRelation.TYPE_ID) { if (token.tokenId == BinaryRelation.EQ_ID && eqPos < 0 && leftIsNumber && rigthIsNumber) eqPos = pos; else if (token.tokenId == BinaryRelation.NEQ_ID && neqPos < 0 && leftIsNumber && rigthIsNumber) neqPos = pos; else if (token.tokenId == BinaryRelation.LT_ID && ltPos < 0 && leftIsNumber && rigthIsNumber) ltPos = pos; else if (token.tokenId == BinaryRelation.GT_ID && gtPos < 0 && leftIsNumber && rigthIsNumber) gtPos = pos; else if (token.tokenId == BinaryRelation.LEQ_ID && leqPos < 0 && leftIsNumber && rigthIsNumber) leqPos = pos; else if (token.tokenId == BinaryRelation.GEQ_ID && geqPos < 0 && leftIsNumber && rigthIsNumber) geqPos = pos; } else if (token.tokenTypeId == BitwiseOperator.TYPE_ID) { if (token.tokenId == BitwiseOperator.COMPL_ID && bitwiseComplPos < 0 && rigthIsNumber) bitwiseComplPos = pos; else if (bitwisePos < 0 && leftIsNumber && rigthIsNumber) bitwisePos = pos; } else if (token.tokenTypeId == ParserSymbol.TYPE_ID) { if (token.tokenId == ParserSymbol.COMMA_ID) { if (commaPos < 0) commas = new ArrayList(); commas.add(pos); commaPos = pos; } else if (token.tokenId == ParserSymbol.LEFT_PARENTHESES_ID && lParPos < 0) lParPos = pos; else if (token.tokenId == ParserSymbol.RIGHT_PARENTHESES_ID && rParPos < 0) rParPos = pos; } } /* * powering should be done using backwards sequence */ if (powerNum > 1) { powerPos = -1; p = rPos+1; do { p--; token = tokensList.get(p); if (token.tokenTypeId == Operator.TYPE_ID && token.tokenId == Operator.POWER_ID) powerPos = p; } while (p>lPos && powerPos == -1); } } } if (calculusPos >= 0) calculusCalc(calculusPos); else if (ifPos >= 0) IF_CONDITION(ifPos); else if (iffPos >= 0) IFF(iffPos); else if (recArgPos >= 0) RECURSIVE_ARGUMENT(recArgPos); else if (variadicFunPos >= 0) variadicFunCalc(variadicFunPos); else if (f3ArgPos >= 0) f3ArgCalc(f3ArgPos); else if (f2ArgPos >= 0) f2ArgCalc(f2ArgPos); else if (f1ArgPos >= 0) f1ArgCalc(f1ArgPos); else if (userFunPos >= 0) { if (calcStepsRegister != null) stepTypePrev = calcStepsRegister.stepType; USER_FUNCTION(userFunPos, calcStepsRegister); if (calcStepsRegister != null) calcStepsRegister.stepType = stepTypePrev; } else if (tetrationPos >= 0) TETRATION(tetrationPos); else if (powerPos >= 0) POWER(powerPos); else if (factPos >= 0) FACT(factPos); else if (percPos >= 0) PERC(percPos); else if (modPos >= 0) MODULO(modPos); else if (negPos >= 0) NEG(negPos); else if (rootOperGroupPos >= 0) rootOperCalc(rootOperGroupPos); else if (bitwiseComplPos >= 0) BITWISE_COMPL(bitwiseComplPos); else if (multiplyPos >= 0 || dividePos >= 0 || divideQuotientPos >= 0) { firstPos = ExpressionUtils.findNonNegativeMinimum(multiplyPos, dividePos, divideQuotientPos); if (multiplyPos == firstPos) MULTIPLY(multiplyPos); else if (dividePos == firstPos) DIVIDE(dividePos); else if (divideQuotientPos == firstPos) DIVIDE_QUOTIENT(divideQuotientPos); } else if (minusPos >= 0 || plusPos >= 0) { firstPos = ExpressionUtils.findNonNegativeMinimum(minusPos, plusPos); if (minusPos == firstPos) MINUS(minusPos); else if (plusPos == firstPos) PLUS(plusPos); } else if (neqPos >= 0) NEQ(neqPos); else if (eqPos >= 0) EQ(eqPos); else if (ltPos >= 0) LT(ltPos); else if (gtPos >= 0) GT(gtPos); else if (leqPos >= 0) LEQ(leqPos); else if (geqPos >= 0) GEQ(geqPos); else if (commaPos >= 0) { for (int i = commas.size()-1; i >= 0; i--) COMMA(commas.get(i)); storeStepsInRegister = false; } else if (andGroupPos >= 0) bolCalc(andGroupPos); else if (orGroupPos >= 0) bolCalc(orGroupPos); else if (implGroupPos >= 0) bolCalc(implGroupPos); else if (bolPos >= 0) bolCalc(bolPos); else if (bitwisePos >= 0) bitwiseCalc(bitwisePos); else if (lParPos >= 0 && rParPos > lParPos) { PARENTHESES(lParPos,rParPos); storeStepsInRegister = false; } if (verboseMode) { showParsing(0,tokensList.size()-1); printSystemInfo(StringInvariant.SPACE + StringModel.STRING_RESOURCES.DONE + StringInvariant.NEW_LINE, NO_EXP_STR); } if (tokensList.size() == tokensNumber) emptyLoopCounter++; else emptyLoopCounter = 0; if (emptyLoopCounter > 10) { registerErrorWhileCalculate(StringModel.STRING_RESOURCES.FATAL_ERROR_DO_NOT_KNOW_WHAT_TO_DO_WITH_THE_ENCOUNTERED_TOKEN); return -1; } } while (tokensList.size() > 1); if (!compilationDetails.containsIf) isFullyCompiled = true; return stepsRegisteredCounter; } private int applySequenceFromCompilation(CalcStepsRegister calcStepsRegister, String stepDescription) { int stepsRegisteredCounter = 0; CalcStepRecord.StepType stepTypePrev = CalcStepRecord.StepType.Unknown; boolean storeStepsInRegister = true; for (CompiledElement compiledElement : initialCompilationDetails.compiledElements) { if (mXparser.cancelCurrentCalculationFlag) { registerErrorWhileCalculate(StringModel.STRING_RESOURCES.CANCEL_REQUEST_FINISHING); return -1; } if (storeStepsInRegister && calcStepsRegister != null) { stepsRegisteredCounter++; registerCalculationStepRecord(calcStepsRegister, stepsRegisteredCounter, stepDescription); } storeStepsInRegister = true; int pos = compiledElement.position1; if (verboseMode) { printSystemInfo(StringModel.STRING_RESOURCES.PARSING + StringInvariant.SPACE + StringUtils.surroundBracketsAddSpace(pos + StringInvariant.COMMA_SPACE + pos), WITH_EXP_STR); showParsing(pos,pos); } switch (compiledElement.toCall) { case FREE_ARGUMENT: FREE_ARGUMENT(pos); storeStepsInRegister = false; break; case CONSTANT: CONSTANT(pos); storeStepsInRegister = false; break; case UNIT: UNIT(pos); storeStepsInRegister = false; break; case USER_CONSTANT: USER_CONSTANT(pos); storeStepsInRegister = false; break; case RANDOM_VARIABLE: RANDOM_VARIABLE(pos); storeStepsInRegister = false; break; case DEPENDENT_ARGUMENT: if (calcStepsRegister != null) stepTypePrev = calcStepsRegister.stepType; DEPENDENT_ARGUMENT(pos, calcStepsRegister); if (calcStepsRegister != null) calcStepsRegister.stepType = stepTypePrev; break; case calculusCalc: calculusCalc(pos); break; case IF_CONDITION: IF_CONDITION(pos); break; case IFF: IFF(pos); break; case RECURSIVE_ARGUMENT: RECURSIVE_ARGUMENT(pos); break; case variadicFunCalc: variadicFunCalc(pos); break; case f3ArgCalc: f3ArgCalc(pos); break; case f2ArgCalc: f2ArgCalc(pos); break; case f1ArgCalc: f1ArgCalc(pos); break; case USER_FUNCTION: if (calcStepsRegister != null) stepTypePrev = calcStepsRegister.stepType; USER_FUNCTION(pos, calcStepsRegister); if (calcStepsRegister != null) calcStepsRegister.stepType = stepTypePrev; break; case TETRATION: TETRATION(pos); break; case POWER: POWER(pos); break; case FACT: FACT(pos); break; case PERC: PERC(pos); break; case MODULO: MODULO(pos); break; case NEG: NEG(pos); break; case rootOperCalc: rootOperCalc(pos); break; case BITWISE_COMPL: BITWISE_COMPL(pos); break; case MULTIPLY: MULTIPLY(pos); break; case DIVIDE: DIVIDE(pos); break; case DIVIDE_QUOTIENT: DIVIDE_QUOTIENT(pos); break; case MINUS: MINUS(pos); break; case PLUS: PLUS(pos); break; case NEQ: NEQ(pos); break; case EQ: EQ(pos); break; case LT: LT(pos); break; case GT: GT(pos); break; case LEQ: LEQ(pos); break; case GEQ: GEQ(pos); break; case COMMA: COMMA(pos); storeStepsInRegister = false; break; case bolCalc: bolCalc(pos); break; case bitwiseCalc: bitwiseCalc(pos); break; case PARENTHESES: PARENTHESES(compiledElement.position1, compiledElement.position2); storeStepsInRegister = false; break; } if (verboseMode) { showParsing(0,tokensList.size()-1); printSystemInfo(StringInvariant.SPACE + StringModel.STRING_RESOURCES.DONE + StringInvariant.NEW_LINE, NO_EXP_STR); } } return stepsRegisteredCounter; } private double calculateInternal(CalcStepsRegister calcStepsRegister) { computingTime = 0; long startTime = System.currentTimeMillis(); if (verboseMode) { printSystemInfo(StringInvariant.NEW_LINE, NO_EXP_STR); printSystemInfo(StringInvariant.NEW_LINE, WITH_EXP_STR); printSystemInfo(StringModel.STRING_RESOURCES.STARTING + StringInvariant.NEW_LINE, WITH_EXP_STR); showArguments(); } /* * check expression syntax and * evaluate expression string tokens * */ if (expressionWasModified || syntaxStatus != NO_SYNTAX_ERRORS) syntaxStatus = checkSyntax(); if (syntaxStatus == SYNTAX_ERROR) { if (verboseMode) printSystemInfo(StringModel.STRING_RESOURCES.PROBLEM_WITH_EXPRESSION_SYNTAX + StringInvariant.NEW_LINE, NO_EXP_STR); /* * Recursive counter to avoid infinite loops in expressions * created in they way showed in below examples * * Argument x = new Argument("x = 2*y"); * Argument y = new Argument("y = 2*x"); * x.addDefinitions(y); * y.addDefinitions(x); * * Function f = new Function("f(x) = 2*g(x)"); * Function g = new Function("g(x) = 2*f(x)"); * f.addDefinitions(g); * g.addDefinitions(f); * */ recursionCallsCounter = 0; return Double.NaN; } /* * Building initial tokens only if this is first recursion call * or we have expression clone, helps to solve problem with * definitions similar to the below example * * * Function f = new Function("f(x) = 2*g(x)"); * Function g = new Function("g(x) = 2*f(x)"); * f.addDefinitions(g); * g.addDefinitions(f); */ if (recursionCallsCounter == 0 || internalClone) copyInitialTokens(); /* * if nothing to calculate return Double.NaN */ if (tokensList.size() == 0) { registerErrorWhileCalculate(StringModel.STRING_RESOURCES.EXPRESSION_DOES_NOT_CONTAIN_ANY_TOKENS); if (verboseMode) printSystemInfo(StringModel.STRING_RESOURCES.EXPRESSION_DOES_NOT_CONTAIN_ANY_TOKENS + StringInvariant.NEW_LINE, NO_EXP_STR); recursionCallsCounter = 0; return Double.NaN; } /* * Incrementing recursive counter to avoid infinite loops in expressions * created in they way showed in below examples * * Argument x = new Argument("x = 2*y"); * Argument y = new Argument("y = 2*x"); * x.addDefinitions(y); * y.addDefinitions(x); * * Function f = new Function("f(x) = 2*g(x)"); * Function g = new Function("g(x) = 2*f(x)"); * f.addDefinitions(g); * g.addDefinitions(f); * */ if (recursionCallsCounter >= mXparser.MAX_RECURSION_CALLS) { if (verboseMode) printSystemInfo(StringModel.STRING_RESOURCES.RECURSION_CALLS_COUNTER_EXCEEDED + StringInvariant.NEW_LINE, NO_EXP_STR); recursionCallsCounter--; registerErrorWhileCalculate(StringModel.STRING_RESOURCES.RECURSION_CALLS_COUNTER_EXCEEDED); return Double.NaN; } recursionCallsCounter++; if (verboseMode) printSystemInfo(StringModel.STRING_RESOURCES.STARTING_CALCULATION_LOOP + StringInvariant.NEW_LINE, WITH_EXP_STR); CalcStepsRegister.stepNumberGroupIncrease(calcStepsRegister, this); String stepDescription = StringInvariant.EMPTY; if (calcStepsRegister != null) stepDescription = makeStepDescription(); if (verboseMode) printSystemInfo(StringModel.STRING_RESOURCES.FULLY_COMPILED + StringInvariant.SPACE_EQUAL_SPACE + isFullyCompiled + StringInvariant.NEW_LINE, WITH_EXP_STR); int stepsRegisteredCounter; if (isFullyCompiled) { stepsRegisteredCounter = applySequenceFromCompilation(calcStepsRegister, stepDescription); } else { stepsRegisteredCounter = calculateFirstAndFullyCompile(calcStepsRegister, stepDescription); if (stepsRegisteredCounter < 0) return Double.NaN; } if (verboseMode) { printSystemInfo(StringModel.STRING_RESOURCES.CALCULATED_VALUE + StringInvariant.COLON_SPACE + tokensList.get(0).tokenValue + StringInvariant.NEW_LINE, WITH_EXP_STR); printSystemInfo(StringModel.STRING_RESOURCES.EXITING + StringInvariant.NEW_LINE, WITH_EXP_STR); printSystemInfo(StringInvariant.NEW_LINE, NO_EXP_STR); } long endTime = System.currentTimeMillis(); computingTime = (endTime - startTime)/1000.0; recursionCallsCounter--; double result = tokensList.get(0).tokenValue; if (!disableRounding) { if (mXparser.almostIntRounding) { double resultInt = Math.round(result); if (Math.abs(result-resultInt) <= BinaryRelations.getEpsilon()) result = resultInt; } if (mXparser.canonicalRounding) result = MathFunctions.lengthRound(result); } if (calcStepsRegister != null) { stepsRegisteredCounter++; registerCalculationStepRecord(calcStepsRegister, stepsRegisteredCounter, stepDescription, result); } return result; } private void registerCompiledElement(CompiledElement.ToCall toCall, int position) { CompiledElement compiledElement = new CompiledElement(); compiledElement.toCall = toCall; compiledElement.position1 = position; initialCompilationDetails.compiledElements.add(compiledElement); } private void registerCompiledElement(CompiledElement.ToCall toCall, int position1, int position2) { CompiledElement compiledElement = new CompiledElement(); compiledElement.toCall = toCall; compiledElement.position1 = position1; compiledElement.position2 = position2; initialCompilationDetails.compiledElements.add(compiledElement); } /** * Calculates unary function * @param pos token position */ private void f1ArgCalc(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.f1ArgCalc, pos); switch (tokensList.get(pos).tokenId) { case Function1Arg.SIN_ID: SIN(pos); break; case Function1Arg.COS_ID: COS(pos); break; case Function1Arg.TAN_ID: TAN(pos); break; case Function1Arg.CTAN_ID: CTAN(pos); break; case Function1Arg.SEC_ID: SEC(pos); break; case Function1Arg.COSEC_ID: COSEC(pos); break; case Function1Arg.ASIN_ID: ASIN(pos); break; case Function1Arg.ACOS_ID: ACOS(pos); break; case Function1Arg.ATAN_ID: ATAN(pos); break; case Function1Arg.ACTAN_ID: ACTAN(pos); break; case Function1Arg.LN_ID: LN(pos); break; case Function1Arg.LOG2_ID: LOG2(pos); break; case Function1Arg.LOG10_ID: LOG10(pos); break; case Function1Arg.RAD_ID: RAD(pos); break; case Function1Arg.EXP_ID: EXP(pos); break; case Function1Arg.SQRT_ID: SQRT(pos); break; case Function1Arg.SINH_ID: SINH(pos); break; case Function1Arg.COSH_ID: COSH(pos); break; case Function1Arg.TANH_ID: TANH(pos); break; case Function1Arg.COTH_ID: COTH(pos); break; case Function1Arg.SECH_ID: SECH(pos); break; case Function1Arg.CSCH_ID: CSCH(pos); break; case Function1Arg.DEG_ID: DEG(pos); break; case Function1Arg.ABS_ID: ABS(pos); break; case Function1Arg.SGN_ID: SGN(pos); break; case Function1Arg.FLOOR_ID: FLOOR(pos); break; case Function1Arg.CEIL_ID: CEIL(pos); break; case Function1Arg.NOT_ID: NOT(pos); break; case Function1Arg.ARSINH_ID: ARSINH(pos); break; case Function1Arg.ARCOSH_ID: ARCOSH(pos); break; case Function1Arg.ARTANH_ID: ARTANH(pos); break; case Function1Arg.ARCOTH_ID: ARCOTH(pos); break; case Function1Arg.ARSECH_ID: ARSECH(pos); break; case Function1Arg.ARCSCH_ID: ARCSCH(pos); break; case Function1Arg.SA_ID: SA(pos); break; case Function1Arg.SINC_ID: SINC(pos); break; case Function1Arg.BELL_NUMBER_ID: BELL_NUMBER(pos); break; case Function1Arg.LUCAS_NUMBER_ID: LUCAS_NUMBER(pos); break; case Function1Arg.FIBONACCI_NUMBER_ID: FIBONACCI_NUMBER(pos); break; case Function1Arg.HARMONIC_NUMBER_ID: HARMONIC_NUMBER(pos); break; case Function1Arg.IS_PRIME_ID: IS_PRIME(pos); break; case Function1Arg.PRIME_COUNT_ID: PRIME_COUNT(pos); break; case Function1Arg.EXP_INT_ID: EXP_INT(pos); break; case Function1Arg.LOG_INT_ID: LOG_INT(pos); break; case Function1Arg.OFF_LOG_INT_ID: OFF_LOG_INT(pos); break; case Function1Arg.GAUSS_ERF_ID: GAUSS_ERF(pos); break; case Function1Arg.GAUSS_ERFC_ID: GAUSS_ERFC(pos); break; case Function1Arg.GAUSS_ERF_INV_ID: GAUSS_ERF_INV(pos); break; case Function1Arg.GAUSS_ERFC_INV_ID: GAUSS_ERFC_INV(pos); break; case Function1Arg.ULP_ID: ULP(pos); break; case Function1Arg.ISNAN_ID: ISNAN(pos); break; case Function1Arg.NDIG10_ID: NDIG10(pos); break; case Function1Arg.NFACT_ID: NFACT(pos); break; case Function1Arg.ARCSEC_ID: ARCSEC(pos); break; case Function1Arg.ARCCSC_ID: ARCCSC(pos); break; case Function1Arg.GAMMA_ID: GAMMA(pos); break; case Function1Arg.LAMBERT_W0_ID: LAMBERT_W0(pos); break; case Function1Arg.LAMBERT_W1_ID: LAMBERT_W1(pos); break; case Function1Arg.SGN_GAMMA_ID: SGN_GAMMA(pos); break; case Function1Arg.LOG_GAMMA_ID: LOG_GAMMA(pos); break; case Function1Arg.DI_GAMMA_ID: DI_GAMMA(pos); break; case Function1Arg.PARAM_ID: UDF_PARAM(pos); break; case Function1Arg.RND_STUDENT_T_ID: RND_STUDENT_T(pos); break; case Function1Arg.RND_CHI2_ID: RND_CHI2(pos); break; } } /** * Calculates binary function * @param pos Token position */ private void f2ArgCalc(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.f2ArgCalc, pos); switch (tokensList.get(pos).tokenId) { case Function2Arg.LOG_ID: LOG(pos); break; case Function2Arg.MOD_ID: MOD(pos); break; case Function2Arg.BINOM_COEFF_ID: BINOM_COEFF(pos); break; case Function2Arg.BERNOULLI_NUMBER_ID: BERNOULLI_NUMBER(pos); break; case Function2Arg.STIRLING1_NUMBER_ID: STIRLING1_NUMBER(pos); break; case Function2Arg.STIRLING2_NUMBER_ID: STIRLING2_NUMBER(pos); break; case Function2Arg.WORPITZKY_NUMBER_ID: WORPITZKY_NUMBER(pos); break; case Function2Arg.EULER_NUMBER_ID: EULER_NUMBER(pos); break; case Function2Arg.KRONECKER_DELTA_ID: KRONECKER_DELTA(pos); break; case Function2Arg.EULER_POLYNOMIAL_ID: EULER_POLYNOMIAL(pos); break; case Function2Arg.HARMONIC_NUMBER_ID: HARMONIC2_NUMBER(pos); break; case Function2Arg.RND_UNIFORM_CONT_ID: RND_VAR_UNIFORM_CONT(pos); break; case Function2Arg.RND_UNIFORM_DISCR_ID: RND_VAR_UNIFORM_DISCR(pos); break; case Function2Arg.ROUND_ID: ROUND(pos); break; case Function2Arg.RND_NORMAL_ID: RND_NORMAL(pos); break; case Function2Arg.NDIG_ID: NDIG(pos); break; case Function2Arg.DIGIT10_ID: DIGIT10(pos); break; case Function2Arg.FACTVAL_ID: FACTVAL(pos); break; case Function2Arg.FACTEXP_ID: FACTEXP(pos); break; case Function2Arg.ROOT_ID: ROOT(pos); break; case Function2Arg.INC_GAMMA_LOWER_ID: INC_GAMMA_LOWER(pos); break; case Function2Arg.INC_GAMMA_UPPER_ID: INC_GAMMA_UPPER(pos); break; case Function2Arg.REG_GAMMA_LOWER_ID: REG_GAMMA_LOWER(pos); break; case Function2Arg.REG_GAMMA_UPPER_ID: REG_GAMMA_UPPER(pos); break; case Function2Arg.PERMUTATIONS_ID: PERMUTATIONS(pos); break; case Function2Arg.BETA_ID: BETA(pos); break; case Function2Arg.LOG_BETA_ID: LOG_BETA(pos); break; case Function2Arg.PDF_STUDENT_T_ID: PDF_STUDENT_T(pos); break; case Function2Arg.CDF_STUDENT_T_ID: CDF_STUDENT_T(pos); break; case Function2Arg.QNT_STUDENT_T_ID: QNT_STUDENT_T(pos); break; case Function2Arg.PDF_CHI2_ID: PDF_CHI2(pos); break; case Function2Arg.CDF_CHI2_ID: CDF_CHI2(pos); break; case Function2Arg.QNT_CHI2_ID: QNT_CHI2(pos); break; case Function2Arg.RND_F_SNEDECOR_ID: RND_F_SNEDECOR(pos); break; } } /** * Calculates function with 3 arguments * @param pos Token position */ private void f3ArgCalc(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.f3ArgCalc, pos); switch (tokensList.get(pos).tokenId) { case Function3Arg.IF_ID: IF(pos); break; case Function3Arg.CHI_ID: CHI(pos); break; case Function3Arg.CHI_LR_ID: CHI_LR(pos); break; case Function3Arg.CHI_L_ID: CHI_L(pos); break; case Function3Arg.CHI_R_ID: CHI_R(pos); break; case Function3Arg.PDF_UNIFORM_CONT_ID: PDF_UNIFORM_CONT(pos); break; case Function3Arg.CDF_UNIFORM_CONT_ID: CDF_UNIFORM_CONT(pos); break; case Function3Arg.QNT_UNIFORM_CONT_ID: QNT_UNIFORM_CONT(pos); break; case Function3Arg.PDF_NORMAL_ID: PDF_NORMAL(pos); break; case Function3Arg.CDF_NORMAL_ID: CDF_NORMAL(pos); break; case Function3Arg.QNT_NORMAL_ID: QNT_NORMAL(pos); break; case Function3Arg.DIGIT_ID: DIGIT(pos); break; case Function3Arg.INC_BETA_ID: INC_BETA(pos); break; case Function3Arg.REG_BETA_ID: REG_BETA(pos); break; case Function3Arg.PDF_F_SNEDECOR_ID: PDF_F_SNEDECOR(pos); break; case Function3Arg.CDF_F_SNEDECOR_ID: CDF_F_SNEDECOR(pos); break; case Function3Arg.QNT_F_SNEDECOR_ID: QNT_F_SNEDECOR(pos); break; } } /** * Calculates Variadic function * @param pos Token position */ private void variadicFunCalc(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.variadicFunCalc, pos); switch (tokensList.get(pos).tokenId) { case FunctionVariadic.IFF_ID: IFF(pos); break; case FunctionVariadic.MIN_ID: MIN_VARIADIC(pos); break; case FunctionVariadic.MAX_ID: MAX_VARIADIC(pos); break; case FunctionVariadic.SUM_ID: SUM_VARIADIC(pos); break; case FunctionVariadic.PROD_ID: PROD_VARIADIC(pos); break; case FunctionVariadic.AVG_ID: AVG_VARIADIC(pos); break; case FunctionVariadic.VAR_ID: VAR_VARIADIC(pos); break; case FunctionVariadic.STD_ID: STD_VARIADIC(pos); break; case FunctionVariadic.CONT_FRAC_ID: CONTINUED_FRACTION(pos); break; case FunctionVariadic.CONT_POL_ID: CONTINUED_POLYNOMIAL(pos); break; case FunctionVariadic.GCD_ID: GCD(pos); break; case FunctionVariadic.LCM_ID: LCM(pos); break; case FunctionVariadic.RND_LIST_ID: RND_LIST(pos); break; case FunctionVariadic.COALESCE_ID: COALESCE(pos); break; case FunctionVariadic.OR_ID: OR_VARIADIC(pos); break; case FunctionVariadic.AND_ID: AND_VARIADIC(pos); break; case FunctionVariadic.XOR_ID: XOR_VARIADIC(pos); break; case FunctionVariadic.ARGMIN_ID: ARGMIN_VARIADIC(pos); break; case FunctionVariadic.ARGMAX_ID: ARGMAX_VARIADIC(pos); break; case FunctionVariadic.MEDIAN_ID: MEDIAN_VARIADIC(pos); break; case FunctionVariadic.MODE_ID: MODE_VARIADIC(pos); break; case FunctionVariadic.BASE_ID: BASE_VARIADIC(pos); break; case FunctionVariadic.NDIST_ID: NDIST_VARIADIC(pos); break; } } /** * Calculates calculus operators * @param pos */ private void calculusCalc(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.calculusCalc, pos); switch (tokensList.get(pos).tokenId) { case CalculusOperator.SUM_ID: SUM(pos); break; case CalculusOperator.PROD_ID: PROD(pos); break; case CalculusOperator.MIN_ID: MIN(pos); break; case CalculusOperator.MAX_ID: MAX(pos); break; case CalculusOperator.AVG_ID: AVG(pos); break; case CalculusOperator.VAR_ID: VAR(pos); break; case CalculusOperator.STD_ID: STD(pos); break; case CalculusOperator.INT_ID: INTEGRAL(pos); break; case CalculusOperator.SOLVE_ID: SOLVE(pos); break; case CalculusOperator.DER_ID: DERIVATIVE(pos, Calculus.GENERAL_DERIVATIVE); break; case CalculusOperator.DER_LEFT_ID: DERIVATIVE(pos, Calculus.LEFT_DERIVATIVE); break; case CalculusOperator.DER_RIGHT_ID: DERIVATIVE(pos, Calculus.RIGHT_DERIVATIVE); break; case CalculusOperator.DERN_ID: DERIVATIVE_NTH(pos, Calculus.GENERAL_DERIVATIVE); break; case CalculusOperator.FORW_DIFF_ID: FORWARD_DIFFERENCE(pos); break; case CalculusOperator.BACKW_DIFF_ID: BACKWARD_DIFFERENCE(pos); break; } } /** * Unicode root operators * @param pos */ private void rootOperCalc(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.rootOperCalc, pos); switch (tokensList.get(pos).tokenId) { case Operator.SQUARE_ROOT_ID: SQUARE_ROOT_OPERATOR(pos); break; case Operator.CUBE_ROOT_ID: CUBE_ROOT_OPERATOR(pos); break; case Operator.FOURTH_ROOT_ID: FOURTH_ROOT_OPERATOR(pos); break; } } /** * Calculates boolean operators * @param pos */ private void bolCalc(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.bolCalc, pos); switch (tokensList.get(pos).tokenId) { case BooleanOperator.AND_ID: AND(pos); break; case BooleanOperator.CIMP_ID: CIMP(pos); break; case BooleanOperator.CNIMP_ID: CNIMP(pos); break; case BooleanOperator.EQV_ID: EQV(pos); break; case BooleanOperator.IMP_ID: IMP(pos); break; case BooleanOperator.NAND_ID: NAND(pos); break; case BooleanOperator.NIMP_ID: NIMP(pos); break; case BooleanOperator.NOR_ID: NOR(pos); break; case BooleanOperator.OR_ID: OR(pos); break; case BooleanOperator.XOR_ID: XOR(pos); break; } } /** * Calculates Bitwise operators * @param pos */ private void bitwiseCalc(int pos) { if (!isFullyCompiled) registerCompiledElement(CompiledElement.ToCall.bitwiseCalc, pos); switch (tokensList.get(pos).tokenId) { case BitwiseOperator.AND_ID: BITWISE_AND(pos); break; case BitwiseOperator.OR_ID: BITWISE_OR(pos); break; case BitwiseOperator.XOR_ID: BITWISE_XOR(pos); break; case BitwiseOperator.NAND_ID: BITWISE_NAND(pos); break; case BitwiseOperator.NOR_ID: BITWISE_NOR(pos); break; case BitwiseOperator.XNOR_ID: BITWISE_XNOR(pos); break; case BitwiseOperator.LEFT_SHIFT_ID: BITWISE_LEFT_SHIFT(pos); break; case BitwiseOperator.RIGHT_SHIFT_ID: BITWISE_RIGHT_SHIFT(pos); break; } } /*================================================= * * Parser methods * *================================================= */ /** * Creates parser keywords list */ private void initParserKeyWords() { keyWordsList = new ArrayList(); ExpressionUtils.addParserKeyWords(parserKeyWordsOnly, UDFExpression, unicodeKeyWordsEnabled, keyWordsList); modifyParserKeyWords(); validateParserKeyWords(); addUserDefinedKeyWords(); } private void modifyParserKeyWords() { boolean toRemove = mXparser.tokensToRemove.size() > 0; boolean toModify = mXparser.tokensToModify.size() > 0; if (!toRemove && !toModify) return; List keyWordsToRemove = new ArrayList(); for (int i = 0; i < keyWordsList.size(); i++) { KeyWord kw = keyWordsList.get(i); if ( kw.wordTypeId == Function1Arg.TYPE_ID || kw.wordTypeId == Function2Arg.TYPE_ID || kw.wordTypeId == Function3Arg.TYPE_ID || kw.wordTypeId == FunctionVariadic.TYPE_ID || kw.wordTypeId == CalculusOperator.TYPE_ID || kw.wordTypeId == ConstantValue.TYPE_ID || kw.wordTypeId == RandomVariable.TYPE_ID || kw.wordTypeId == Unit.TYPE_ID ) { if (toRemove) if (mXparser.tokensToRemove.contains(kw.wordString)) keyWordsToRemove.add(kw); String wordString; String wordDescription; String wordSyntax; if (toModify) { for (TokenModification tm : mXparser.tokensToModify) { if (!tm.currentToken.equals(kw.wordString)) continue; wordString = tm.newToken; wordDescription = kw.description; if (tm.newTokenDescription != null) wordDescription = tm.newTokenDescription; wordSyntax = kw.syntax.replace(tm.currentToken, tm.newToken); KeyWord newKw = new KeyWord(wordString, wordDescription, kw.wordId, wordSyntax, kw.since, kw.wordTypeId); keyWordsList.set(i, newKw); } } } } if (toRemove && keyWordsToRemove.size() > 0) for (KeyWord kw : keyWordsToRemove) keyWordsList.remove(kw); } /** * Final validation of keywords */ private void validateParserKeyWords() { if (!mXparser.overrideBuiltinTokens) return; /* * Building list of user defined tokens */ List userDefinedTokens = new ArrayList(); for (Argument arg : argumentsList) userDefinedTokens.add( arg.getArgumentName() ); for (Function fun : functionsList) userDefinedTokens.add( fun.getFunctionName() ); for (Constant cons : constantsList) userDefinedTokens.add( cons.getConstantName() ); /* * If no user defined tokens then exit */ if (userDefinedTokens.isEmpty()) return; /* * Building list of built-in tokens to remove */ List keyWordsToOverride = new ArrayList(); for (KeyWord kw : keyWordsList) if (userDefinedTokens.contains(kw.wordString)) keyWordsToOverride.add(kw); /* * If nothing to remove then exit */ if (keyWordsToOverride.isEmpty()) return; /* * Performing final override */ for (KeyWord kw : keyWordsToOverride) keyWordsList.remove(kw); } /** * Method used in case of implied multiplication, where x2x can be understood as x2*x * * sum( x2x, 1, 20, 2*x2x) * * x2x is not known and it will be prevented from split into x2*x * * @param token The token * @return Returns true in case calculus argument was found */ private boolean checkArgumentNameInCalculusOperator(Token token) { if (neverParseForImpliedMultiplication.contains(token.tokenStr)) { initialTokens.add(token); return true; } int initialTokensSize = initialTokens.size(); if (initialTokensSize < 2) return false; boolean argumentNameFound = false; if (initialTokensSize >= 2) { Token tokenMinus2 = initialTokens.get(initialTokensSize - 2); if (tokenMinus2.tokenTypeId == CalculusOperator.TYPE_ID) { switch (tokenMinus2.tokenId) { case CalculusOperator.SUM_ID: case CalculusOperator.PROD_ID: case CalculusOperator.AVG_ID: case CalculusOperator.VAR_ID: case CalculusOperator.STD_ID: case CalculusOperator.MIN_ID: case CalculusOperator.MAX_ID: argumentNameFound = true; break; } } } if (initialTokensSize >= 4 && !argumentNameFound) { Token tokenMinus4 = initialTokens.get(initialTokensSize - 4); if (tokenMinus4.tokenTypeId == CalculusOperator.TYPE_ID) { switch (tokenMinus4.tokenId) { case CalculusOperator.INT_ID: case CalculusOperator.DER_ID: case CalculusOperator.DER_LEFT_ID: case CalculusOperator.DER_RIGHT_ID: case CalculusOperator.DERN_ID: case CalculusOperator.FORW_DIFF_ID: case CalculusOperator.BACKW_DIFF_ID: case CalculusOperator.SOLVE_ID: argumentNameFound = true; break; } } } if (argumentNameFound) { initialTokens.add(token); neverParseForImpliedMultiplication.add(token.tokenStr); } return argumentNameFound; } /** * Check whether we have a case of '[abc]' * * @param token The token * @return Returns true in case token is in a form of '[abc]' * otherwise returns false. */ private boolean checkSpecialConstantName(Token token) { int tokenStrLenght = token.tokenStr.length(); if (tokenStrLenght < 2) return false; if (token.tokenStr.charAt(0) != '[') return false; if (token.tokenStr.charAt(tokenStrLenght-1) != ']') return false; initialTokensAdd(token); return true; } /** * Checks whether unknown token represents number literal * provided in different numeral base system, where * base is between 1 and 36. * * @param token The token not know to the parser */ private boolean checkOtherNumberBases(Token token) { int dotPos = 0; int tokenStrLength = token.tokenStr.length(); /* find dot position */ if (tokenStrLength >= 2) { if (token.tokenStr.charAt(1) == '.') dotPos = 1; } if (dotPos == 0 && tokenStrLength >= 3) { if (token.tokenStr.charAt(2) == '.') dotPos = 2; } if (dotPos == 0 && tokenStrLength >= 4) { if (token.tokenStr.charAt(3) == '.') dotPos = 3; } if (dotPos == 0) return false; /* check if there is base indicator */ String baseInd = token.tokenStr.substring(0, dotPos).toLowerCase(); String numberLiteral = StringInvariant.EMPTY; if (tokenStrLength > dotPos+1) numberLiteral = token.tokenStr.substring(dotPos+1); int numeralSystemBase = ExpressionUtils.getNumeralSystemBaseFromBaseInd(baseInd); /* if base was found, perform conversion */ if (numeralSystemBase > 0 && numeralSystemBase <= 36) { double tokenValue = NumberTheory.convOthBase2Decimal(numberLiteral, numeralSystemBase); if (Double.isNaN(tokenValue)) return false; token.tokenTypeId = ParserSymbol.NUMBER_TYPE_ID; token.tokenId = ParserSymbol.NUMBER_ID; token.tokenValue = tokenValue; initialTokensAdd(token); return true; } return false; } /** * Adds fraction token to the tokens list * @param token The token */ private void addFractionToken(Token token) { int underscore1stPos = token.tokenStr.indexOf('_'); int underscore2ndPos = token.tokenStr.indexOf('_', underscore1stPos + 1); boolean mixedFraction = underscore2ndPos > 0; double fractionValue; if (mixedFraction) { String wholeStr = token.tokenStr.substring(0, underscore1stPos); String numeratorStr = token.tokenStr.substring(underscore1stPos + 1, underscore2ndPos); String denominatorStr = token.tokenStr.substring(underscore2ndPos + 1); double whole = Double.parseDouble(wholeStr); double numerator = Double.parseDouble(numeratorStr); double denominator = Double.parseDouble(denominatorStr); if (denominator == 0) { fractionValue = Double.NaN; } else { fractionValue = whole + numerator / denominator; } } else { String numeratorStr = token.tokenStr.substring(0, underscore1stPos); String denominatorStr = token.tokenStr.substring(underscore1stPos + 1); double numerator = Double.parseDouble(numeratorStr); double denominator = Double.parseDouble(denominatorStr); if (denominator == 0) fractionValue = Double.NaN; else { fractionValue = numerator / denominator; } } token.tokenTypeId = ParserSymbol.NUMBER_TYPE_ID; token.tokenId = ParserSymbol.NUMBER_ID; token.tokenValue = fractionValue; initialTokensAdd(token); } /** * Checks whether unknown token represents fraction * provided as fraction or mixed fraction * * @param token The token not know to the parser */ private boolean checkFraction(Token token) { if (token.tokenStr.length() < 3) return false; if (!StringUtils.regexMatch(token.tokenStr, ParserSymbol.FRACTION)) return false; addFractionToken(token); return true; } /** * Handles implied multiplication while adding single token to the tokens list * is checking preceding token * @param token The token */ private void initialTokensAdd(Token token) { if (initialTokens.size() == 0) { initialTokens.add(token); return; } /* Start: Implied Multiplication related part*/ Token precedingToken = initialTokens.get(initialTokens.size() - 1); if (token.isSpecialTokenName()) { /* Special constant case [...] * Excluding: '([a]', ';[a]', ',[a]', '+[a]', .... */ if (!precedingToken.isLeftParenthesis() && !precedingToken.isBinaryOperator() && !precedingToken.isParameterSeparator() && !precedingToken.isUnaryLeftOperator()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } } else if (precedingToken.isSpecialTokenName()) { if (!token.isRightParenthesis() && !token.isBinaryOperator() && !token.isParameterSeparator() && !token.isUnaryRightOperator()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } } else if (token.isLeftParenthesis()) { // ')(' case if (precedingToken.isRightParenthesis()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } // '2(' case if (precedingToken.isNumber()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } // 'e(', 'pi(' cases if (precedingToken.isIdentifier()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } } else if (precedingToken.isRightParenthesis()) { // ')2', ')h.1212', ')1_2_3' cases if (token.isNumber()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } // ')x', ')sin(x)', ')[sdf]' cases if (!token.isParameterSeparator() && !token.isBinaryOperator() && !token.isUnaryRightOperator() && !token.isRightParenthesis()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } } else if (token.isUnicodeRootOperator()) { /* Unicode root operator */ if (!precedingToken.isLeftParenthesis() && !precedingToken.isBinaryOperator() && !precedingToken.isParameterSeparator() && !precedingToken.isUnaryLeftOperator()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } } else if (!token.isLeftParenthesis() && !token.isRightParenthesis() && !token.isBinaryOperator() && !token.isParameterSeparator() && !token.isUnaryRightOperator()) { /* Blank support: '2 x', 'n x', 'n sin(x)' */ if (!precedingToken.isLeftParenthesis() && !precedingToken.isRightParenthesis() && !precedingToken.isBinaryOperator() && !precedingToken.isParameterSeparator() && !precedingToken.isUnaryLeftOperator()) { if (impliedMultiplicationMode) { initialTokens.add(Token.makeMultiplyToken()); initialTokens.add(token); return; } else impliedMultiplicationError = true; } } /* End: Implied Multiplication related part*/ initialTokens.add(token); } /** * Assign found known keyword to the token * * @param token The token * @param keyWord The keyword */ private void assignKnownKeyword(Token token, KeyWord keyWord) { token.tokenTypeId = keyWord.wordTypeId; token.tokenId = keyWord.wordId; if (token.tokenTypeId == Argument.TYPE_ID) token.tokenValue = argumentsList.get(token.tokenId).argumentValue; } /** * Tries to find known keyword for a given token * via string matching * * @param tokenStr Token string * @return known keyword if match found, otherwise not matched keyword */ private KeyWord tryFindKnownKeyword(String tokenStr) { for (KeyWord kw : keyWordsList) { if (kw.wordString.equals(tokenStr)) return kw; } return new KeyWord(); } /** * Tries to find known keyword for a given token * via string matching * * @param token The token * @return true if keyword matched, otherwise false */ private boolean tryAssignKnownKeyword(Token token) { KeyWord keyWord = tryFindKnownKeyword(token.tokenStr); if (keyWord.wordTypeId != KeyWord.NO_DEFINITION) { assignKnownKeyword(token, keyWord); return true; } return false; } /** * Handles adding token part after single token split * in the proces of parsing implied multiplication. * * @param tokenPart The token part to be added to the token list */ private void initialTokensAddTokenPart(TokenPart tokenPart) { Token token = new Token(); token.tokenStr = tokenPart.str; switch (tokenPart.type) { case TokenPart.INTEGER: case TokenPart.DECIMAL: token.tokenValue = Double.valueOf(token.tokenStr); token.tokenTypeId = ParserSymbol.NUMBER_TYPE_ID; token.tokenId = ParserSymbol.NUMBER_ID; initialTokensAdd(token); break; case TokenPart.FRACTION: addFractionToken(token); break; case TokenPart.OTHER_NUMERAL_BASE: checkOtherNumberBases(token); break; case TokenPart.KNOWN_KEYWORD: assignKnownKeyword(token, tokenPart.keyWord); initialTokensAdd(token); break; case TokenPart.UNKNOWN_NAME: initialTokensAdd(token); break; } } /** * Handles implied multiplication logic in case of a single * continuous string, e.g. no brackets * * @param token The token * @return returns true in case there was a reason to parse, otherwise returns false */ private boolean checkNumberNameManyImpliedMultiplication(Token token, boolean parenthesisIsOnTheRight) { int tokenStrLength = token.tokenStr.length(); if (tokenStrLength < 2) return false; char c; boolean canStartDecimal, decimalFound; boolean canStartOtherNumberBase, otherNumberBaseFound; boolean canStartFraction, fractionFound; boolean canStartKeyword, keywordFound; boolean isDigit; String substr = StringInvariant.EMPTY; KeyWord parserKeyword, kw; int lPos = 0; int rPos; int lastConsumedPos = -1; List tokenParts = new ArrayList(); TokenPart tokenPart = null; do { canStartDecimal = false; canStartOtherNumberBase = false; c = token.tokenStr.charAt(lPos); isDigit = StringUtils.is0To9Digit(c); if (isDigit || c == '.' || c == '+' || c == '-') canStartDecimal = true; canStartFraction = isDigit; if (c == 'b' || c == 'B' || c == 'o' || c == 'O' || c == 'h' || c == 'H') canStartOtherNumberBase = true; canStartKeyword = !canStartDecimal; decimalFound = false; otherNumberBaseFound = false; fractionFound = false; keywordFound = false; parserKeyword = null; for (rPos = tokenStrLength; rPos > lPos; rPos--) { substr = token.tokenStr.substring(lPos, rPos); // Longest possible decimal checking if (canStartDecimal) { decimalFound = StringUtils.regexMatch(substr, ParserSymbol.DECIMAL_REG_EXP); if (decimalFound) break; } // Longest possible fraction checking if (canStartFraction) { fractionFound = StringUtils.regexMatch(substr, ParserSymbol.FRACTION); if (fractionFound) break; } // Longest possible other numeral base checking if (canStartOtherNumberBase) { otherNumberBaseFound = StringUtils.regexMatch(substr, ParserSymbol.BASE_OTHER_REG_EXP); if (otherNumberBaseFound) break; } // Longest possible keyword checking if (canStartKeyword) { kw = tryFindKnownKeyword(substr); if (kw.wordTypeId != KeyWord.NO_DEFINITION) { if (!KeyWord.isFunctionForm(kw) || (rPos == tokenStrLength && parenthesisIsOnTheRight)) { parserKeyword = kw; keywordFound = true; break; } } else if (neverParseForImpliedMultiplication.contains(substr)) { keywordFound = true; parserKeyword = new KeyWord(); break; } } } if (decimalFound || fractionFound || otherNumberBaseFound || keywordFound) { // Checking if not recognized token was present if (lPos - lastConsumedPos > 1) { tokenPart = new TokenPart(); tokenPart.str = token.tokenStr.substring(lastConsumedPos + 1, lPos); tokenPart.type = TokenPart.UNKNOWN_NAME; tokenParts.add(tokenPart); } tokenPart = new TokenPart(); tokenPart.str = substr; if (decimalFound) { if (StringUtils.regexMatch(tokenPart.str, ParserSymbol.INTEGER)) tokenPart.type = TokenPart.INTEGER; else tokenPart.type = TokenPart.DECIMAL; } if (fractionFound) tokenPart.type = TokenPart.FRACTION; if (otherNumberBaseFound) tokenPart.type = TokenPart.OTHER_NUMERAL_BASE; if (keywordFound) { tokenPart.type = TokenPart.KNOWN_KEYWORD; tokenPart.keyWord = parserKeyword; } tokenParts.add(tokenPart); if (rPos > lPos) { lastConsumedPos = rPos - 1; lPos = rPos; } else { lastConsumedPos = tokenStrLength - 1; lPos = tokenStrLength; } } else { lPos++; } } while (lPos < tokenStrLength); if (lPos - lastConsumedPos > 1) { tokenPart = new TokenPart(); tokenPart.str = token.tokenStr.substring(lastConsumedPos + 1, lPos); tokenPart.type = TokenPart.UNKNOWN_NAME; tokenParts.add(tokenPart); } if (tokenParts.size() == 1) { initialTokensAddTokenPart(tokenParts.get(0)); return true; } TokenPart partAtPos = null; TokenPart partAtPosPlus1 = null; boolean foundNameFolloweByInteger; do { foundNameFolloweByInteger = false; int namePos; for (namePos = 0; namePos < tokenParts.size() - 1; namePos++) { partAtPos = tokenParts.get(namePos); partAtPosPlus1 = tokenParts.get(namePos+1); if ( (partAtPos.type == TokenPart.KNOWN_KEYWORD || partAtPos.type == TokenPart.UNKNOWN_NAME) && partAtPosPlus1.type == TokenPart.INTEGER ) { foundNameFolloweByInteger = true; break; } } if (foundNameFolloweByInteger) { partAtPos.str = partAtPos.str + partAtPosPlus1.str; partAtPos.type = TokenPart.UNKNOWN_NAME; partAtPos.keyWord = null; tokenParts.remove(namePos+1); } } while (foundNameFolloweByInteger); if (tokenParts.size() == 1) { initialTokensAddTokenPart(tokenParts.get(0)); return true; } for (int i = 0; i < tokenParts.size(); i++) { if (i > 0) initialTokens.add(Token.makeMultiplyToken()); initialTokensAddTokenPart(tokenParts.get(i)); } return true; } /** * Adds expression token * Method is called by the tokenExpressionString() * while parsing string expression * * @param tokenStr the token string * @param keyWord the keyword */ private void addToken(String tokenStr, KeyWord keyWord, boolean parenthesisIsOnTheRight) { Token token = new Token(); token.tokenStr = tokenStr; token.keyWord = keyWord.wordString; token.tokenTypeId = keyWord.wordTypeId; token.tokenId = keyWord.wordId; if (token.tokenTypeId != Token.NOT_MATCHED) initialTokensAdd(token); if (token.tokenTypeId == Argument.TYPE_ID) { token.tokenValue = argumentsList.get(token.tokenId).argumentValue; } else if (token.tokenTypeId == ParserSymbol.NUMBER_TYPE_ID) { token.tokenValue = Double.parseDouble(token.tokenStr); token.keyWord = ParserSymbol.NUMBER_STR; } else if (token.tokenTypeId == Token.NOT_MATCHED) { boolean alternativeMatchFound = checkArgumentNameInCalculusOperator(token); if (!alternativeMatchFound) alternativeMatchFound = checkSpecialConstantName(token); if (!alternativeMatchFound) alternativeMatchFound = checkOtherNumberBases(token); if (!alternativeMatchFound) alternativeMatchFound = checkFraction(token); if (impliedMultiplicationMode && !alternativeMatchFound) alternativeMatchFound = checkNumberNameManyImpliedMultiplication(token, parenthesisIsOnTheRight); if (!alternativeMatchFound) initialTokensAdd(token); } } private void addToken(String tokenStr, KeyWord keyWord) { addToken(tokenStr, keyWord, false); } private void addUserDefinedKeyWords() { if (parserKeyWordsOnly) return; ExpressionUtils.addArgumentsKeyWords(argumentsList, keyWordsList); ExpressionUtils.addFunctionsKeyWords(functionsList, keyWordsList); ExpressionUtils.addConstantsKeyWords(constantsList, keyWordsList); } /** * Tokenizing expression string */ private void tokenizeExpressionString() { impliedMultiplicationError = false; /* * Add parser and argument keywords */ initParserKeyWords(); java.util.Collections.sort(keyWordsList, new DescKwLenComparator()); /* * Evaluate position after sorting for the following keywords types * number * plus operator * minus operator * * Above-mentioned information is required * when distinguishing between numbers (regexp) and operators * * For example * * 1-2 : two numbers and one operator, but -2 is also a valid number * (-2)+3 : two number and one operator */ int numberKwId = ConstantValue.NaN; int plusKwId = ConstantValue.NaN; int minusKwId = ConstantValue.NaN; for (int kwId = 0; kwId < keyWordsList.size(); kwId++) { if ( keyWordsList.get(kwId).wordTypeId == ParserSymbol.NUMBER_TYPE_ID) numberKwId = kwId; if ( keyWordsList.get(kwId).wordTypeId == Operator.TYPE_ID) { if (keyWordsList.get(kwId).wordId == Operator.PLUS_ID) plusKwId = kwId; if (keyWordsList.get(kwId).wordId == Operator.MINUS_ID) minusKwId = kwId; } } initialTokens = new ArrayList(); neverParseForImpliedMultiplication = new HashSet(); int expLen = expressionString.length(); if (expLen == 0) return; /* * Clearing expression string from spaces */ if (syntaxStatus == SYNTAX_ERROR || syntaxStatus == SYNTAX_STATUS_UNKNOWN) cleanExpressionString(); String newExpressionString = expressionStringCleaned; /* * words list and tokens list */ if (newExpressionString.length() == 0) return; int lastPos = 0; /* position of the keyword previously added*/ int pos = 0; /* current position */ String tokenStr = StringInvariant.EMPTY; boolean matchFoundPrev = false; /* unknown keyword (previous) */ boolean matchFound = false; /* unknown keyword (current) */ KeyWord kw = null; String sub = StringInvariant.EMPTY; String kwStr = StringInvariant.EMPTY; char precedingChar; char followingChar; char firstChar; char c; boolean specialConstFound = false; String specialConstStr = StringInvariant.EMPTY; /* * Check all available positions in the expression tokens list */ do { /* * 1st step * * compare with the regExp for real numbers * find the longest word which could be matched * with the given regExp */ int numEnd = -1; /* * Number has to start with digit or dot */ firstChar = newExpressionString.charAt(pos); if ( firstChar == '+' || firstChar == '-' || firstChar == '.' || StringUtils.is0To9Digit(firstChar) ) { for (int i = pos; i < newExpressionString.length(); i++) { /* * Escaping if encountering char that can not * be included in number */ if (i > pos) { c = newExpressionString.charAt(i); if ( c != '+' && c != '-' && !StringUtils.is0To9Digit(c) && c != '.' && c != 'e' && c != 'E' ) break; } /* * Checking if substring represents number */ String str = newExpressionString.substring(pos, i+1); if (StringUtils.regexMatch(str, ParserSymbol.DECIMAL_REG_EXP)) numEnd = i; } } /* * If number was found */ if (numEnd >= 0) if (pos > 0) { precedingChar = newExpressionString.charAt(pos-1); if (!StringUtils.canBeSeparatingChar(precedingChar)) numEnd = -1; } if (numEnd >= 0) if (numEnd < newExpressionString.length()-1) { followingChar = newExpressionString.charAt(numEnd+1); if (!StringUtils.canBeSeparatingChar(followingChar)) numEnd = -1; } if (numEnd >= 0) { /* * If preceding word was unknown * * For example: * 'abc-2' * * number starts with '-', preceding word 'abc' * is not known by the parser */ if (!matchFoundPrev && pos > 0) { /* * add preceding word to the list of tokens * as unknown keyword word */ tokenStr = newExpressionString.substring(lastPos, pos); addToken(tokenStr, new KeyWord(), StringUtils.charIsLeftParenthesis(newExpressionString, pos)); } /* * Check leading operators ('-' or '+') * * For example: * '2-1' : 1(num) -(op) 2(num) = 1(num) * -1+2 : -1(num) +(op) 2(num) = 1(num) */ firstChar = newExpressionString.charAt(pos); boolean leadingOp = true; if (firstChar == '-' || firstChar == '+') { if (initialTokens.size() > 0) { Token lastToken = initialTokens.get(initialTokens.size()-1); if ( (lastToken.tokenTypeId == Operator.TYPE_ID && lastToken.tokenId != Operator.FACT_ID && lastToken.tokenId != Operator.PERC_ID) || lastToken.tokenTypeId == BinaryRelation.TYPE_ID || lastToken.tokenTypeId == BooleanOperator.TYPE_ID || lastToken.tokenTypeId == BitwiseOperator.TYPE_ID || (lastToken.tokenTypeId == ParserSymbol.TYPE_ID && lastToken.tokenId == ParserSymbol.LEFT_PARENTHESES_ID)) leadingOp = false; else leadingOp = true; } else leadingOp = false; } else leadingOp = false; /* * If leading operator was found */ if (leadingOp) { /* * Add leading operator to the tokens list */ if (firstChar == '-') addToken("-", keyWordsList.get(minusKwId)); if (firstChar == '+') addToken("+", keyWordsList.get(plusKwId)); pos++; } /* * Add found number to the tokens list */ tokenStr = newExpressionString.substring(pos, numEnd+1); addToken(tokenStr, keyWordsList.get(numberKwId)); /* * change current position (just after the number ends) */ pos = numEnd+1; lastPos = pos; /* * Mark match status indicators */ matchFound = true; matchFoundPrev = true; } else { /* * If there is no number which starts with current position * Check for known keywords */ int kwId = -1; matchFound = false; firstChar = newExpressionString.charAt(pos); do { kwId++; kw = keyWordsList.get(kwId); kwStr = kw.wordString; if (pos + kwStr.length() <= newExpressionString.length()) { sub = newExpressionString.substring(pos, pos + kwStr.length() ); if (sub.equals(kwStr)) matchFound = true; /* * If keyword is known by the parser * and keyword is not a special keyword of the form [...] */ if (matchFound && firstChar != '[') { /* * If keyword is in the form of identifier * then check preceding and following characters */ if ( kw.wordTypeId == Argument.TYPE_ID || kw.wordTypeId == RecursiveArgument.TYPE_ID_RECURSIVE || kw.wordTypeId == Function1Arg.TYPE_ID || kw.wordTypeId == Function2Arg.TYPE_ID || kw.wordTypeId == Function3Arg.TYPE_ID || kw.wordTypeId == FunctionVariadic.TYPE_ID || kw.wordTypeId == ConstantValue.TYPE_ID || kw.wordTypeId == Constant.TYPE_ID || kw.wordTypeId == RandomVariable.TYPE_ID || kw.wordTypeId == Unit.TYPE_ID || kw.wordTypeId == Function.TYPE_ID || kw.wordTypeId == CalculusOperator.TYPE_ID ) { /* * Checking preceding character */ if (pos > 0) { precedingChar = newExpressionString.charAt(pos-1); if (!StringUtils.canBeSeparatingChar(precedingChar)) matchFound = false; } /* * Checking following character */ if (matchFound && pos + kwStr.length() < newExpressionString.length()) { followingChar = newExpressionString.charAt(pos + kwStr.length()); if (!StringUtils.canBeSeparatingChar(followingChar)) matchFound = false; } } } } } while (kwId < keyWordsList.size()-1 && !matchFound); /* * If keyword was unknown to the parser * but it might be a special constant keyword in the for [...] */ specialConstFound = false; if (!matchFound) { if (firstChar == '[') { for (int i = pos+1; i < newExpressionString.length(); i++) { /* * Escaping if encountering char ']' */ c = newExpressionString.charAt(i); if (c == ']') { specialConstFound = true; specialConstStr = newExpressionString.substring(pos, i+1); break; } } } } /* * If keyword known by the parser was found */ if (matchFound || specialConstFound) { /* * if preceding word was not known by the parser */ if (!matchFoundPrev && pos > 0) { /* * Add preceding word to the tokens list * as unknown keyword */ tokenStr = newExpressionString.substring(lastPos, pos); addToken(tokenStr, new KeyWord(), StringUtils.charIsLeftParenthesis(newExpressionString, pos)); } matchFoundPrev = true; /* * Add current (known by the parser or special constant) * keyword to the tokens list */ if (matchFound) { tokenStr = newExpressionString.substring(pos, pos+kwStr.length()); if ( !(kw.wordTypeId == ParserSymbol.TYPE_ID && kw.wordId == ParserSymbol.BLANK_ID) ) { addToken(tokenStr, kw); } } else { tokenStr = specialConstStr; addToken(tokenStr, new KeyWord()); } /* * Remember position where last added word ends + 1 */ lastPos = pos+tokenStr.length(); /* * Change current position; */ pos = pos + tokenStr.length(); } else { /* * Update preceding word indicator */ matchFoundPrev = false; /* * Increment position if possible */ if (pos < newExpressionString.length()) pos++; } } /* * while there is still something to analyse */ } while (pos < newExpressionString.length()); /* * If keyword was not known by the parser * and end with the string end * it needs to be added to the tokens list * as unknown keyword */ if (!matchFound) { tokenStr = newExpressionString.substring(lastPos, pos); addToken(tokenStr, new KeyWord(), StringUtils.charIsLeftParenthesis(newExpressionString, pos)); } /* * Evaluate tokens levels * * token level identifies the sequence of parsing */ ExpressionUtils.evaluateTokensLevels(initialTokens); } /** * copy initial tokens list to tokens list and prepares initial compilation details */ private void copyInitialTokens() { boolean prepareInitialTokensListInfo = false; if (initialCompilationDetails == null) { initialCompilationDetails = new CompilationDetails(); initialCompilationDetails.compiledElements = new ArrayList(); prepareInitialTokensListInfo = true; } tokensList = new ArrayList(); if (prepareInitialTokensListInfo) { for (Token token : initialTokens) { tokensList.add(token.clone()); if (token.tokenTypeId == CalculusOperator.TYPE_ID) initialCompilationDetails.containsCalculus = true; else if (token.tokenTypeId == Function3Arg.TYPE_ID && token.tokenId == Function3Arg.IF_CONDITION_ID) initialCompilationDetails.containsIf = true; else if (token.tokenTypeId == FunctionVariadic.TYPE_ID && token.tokenId == FunctionVariadic.IFF_ID) initialCompilationDetails.containsIf = true; } } else { for (Token token : initialTokens) tokensList.add(token.clone()); } if (compilationDetails == null) compilationDetails = new CompilationDetails(); compilationDetails.containsCalculus = initialCompilationDetails.containsCalculus; compilationDetails.containsIf = initialCompilationDetails.containsIf; compilationDetails.compiledElements = initialCompilationDetails.compiledElements; } /** * Tokenizes expression string and returns tokens list, * including: string, type, level. * * @return Copy of initial tokens. * * @see Token * @see mXparser#consolePrintTokens(List) */ public List getCopyOfInitialTokens() { tokenizeExpressionString(); return ExpressionUtils.getCopyOfInitialTokens(expressionString, initialTokens); } /** * Prints to the console copy of initial tokens. Presents how * expression string is interpreted by the parser. * * @see #getCopyOfInitialTokens() */ public void consolePrintCopyOfInitialTokens() { mXparser.consolePrintTokens(getCopyOfInitialTokens()); } /** * Returns missing user defined arguments names, i.e. * sin(x) + cos(y) where x and y are not defined * function will return x and y. * * @return Array of missing user defined arguments names * - distinct strings. */ public String[] getMissingUserDefinedArguments() { return ExpressionUtils.getMissingUserDefinedArguments(getCopyOfInitialTokens()); } /** * Returns missing user defined units names, i.e. * 2*[w] + [q] where [w] and [q] are not defined * function will return [w] and [q]. * * @return Array of missing user defined units names * - distinct strings. */ public String[] getMissingUserDefinedUnits() { return ExpressionUtils.getMissingUserDefinedUnits(getCopyOfInitialTokens()); } /** * Returns missing user defined functions names, i.e. * sin(x) + fun(x,y) where fun is not defined * function will return fun. * * @return Array of missing user defined functions names * - distinct strings. */ public String[] getMissingUserDefinedFunctions() { return ExpressionUtils.getMissingUserDefinedFunctions(getCopyOfInitialTokens()); } /** * Gets initial tokens and returns copied list * * @see Function */ List getInitialTokens() { return initialTokens; } /** * Shows parsing (verbose mode purposes). * */ private void showParsing(int lPos, int rPos) { ExpressionUtils.showParsing(lPos, rPos, tokensList); } /** * shows known keywords */ void showKeyWords() { ExpressionUtils.showKeyWords(keyWordsList); } /** * Returns detailed user help on the syntax of mathematical expressions. * * @return One string value containing all the help. */ public String getHelp() { return getHelp(StringInvariant.EMPTY); } /** * Returns detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @return One string value containing all the help. */ public String getHelp(String query) { initParserKeyWords(); return ExpressionUtils.getHelp(query, keyWordsList); } /** * Returns detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param addHeader Indicator whether to add a header. * @param addCaption Indicator whether to add caption. * @param caption If a non-standard caption is to be added, any string other than "" * will replace the standard caption with the one specified by the user. * * @return One string value containing all the help. */ public String getHelp(boolean addHeader, boolean addCaption, String caption) { initParserKeyWords(); return ExpressionUtils.getHelp(StringInvariant.EMPTY, keyWordsList, addHeader, addCaption, caption); } /** * Returns detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @param addHeader Indicator whether to add a header. * @param addCaption Indicator whether to add caption. * @param caption If a non-standard caption is to be added, any string other than "" * * @return One string value containing all the help. */ public String getHelp(String query, boolean addHeader, boolean addCaption, String caption) { initParserKeyWords(); return ExpressionUtils.getHelp(query, keyWordsList, addHeader, addCaption, caption); } /** * Returns (as CSV) detailed user help on the syntax of mathematical expressions. * * @return One string value in CSV format containing all the help. */ public String getHelpAsCsv() { initParserKeyWords(); return ExpressionUtils.getHelpAsCsv(keyWordsList, StringInvariant.QUOTE, StringInvariant.SEMICOLON, true, StringInvariant.EMPTY); } /** * Returns (as CSV) detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @return One string value in CSV format containing all the help. */ public String getHelpAsCsv(String query) { initParserKeyWords(); return ExpressionUtils.getHelpAsCsv(keyWordsList, StringInvariant.QUOTE, StringInvariant.SEMICOLON, true, query); } /** * Returns (as CSV) detailed user help on the syntax of mathematical expressions. * * @param quote Text qualifier. * @param delimiter Delimiter. * @param addHeader Indicator whether to add a header. * * @return One string value in CSV format containing all the help. */ public String getHelpAsCsv(String quote, String delimiter, boolean addHeader) { initParserKeyWords(); return ExpressionUtils.getHelpAsCsv(keyWordsList, quote, delimiter, addHeader, StringInvariant.EMPTY); } /** * Returns (as CSV) detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @param quote Text qualifier. * @param delimiter Delimiter. * @param addHeader Indicator whether to add a header. * * @return One string value in CSV format containing all the help. */ public String getHelpAsCsv(String query, String quote, String delimiter, boolean addHeader) { initParserKeyWords(); return ExpressionUtils.getHelpAsCsv(keyWordsList, quote, delimiter, addHeader, query); } /** * Returns (as HTML table) detailed user help on the syntax of mathematical expressions. * * @return One string value containing all the help. String in HTML table format. */ public String getHelpAsHtmlTable() { initParserKeyWords(); return ExpressionUtils.getHelpAsHtmlTable(keyWordsList, true, StringInvariant.EMPTY); } /** * Returns (as HTML table) detailed user help on the syntax of mathematical expressions. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @return One string value containing all the help. String in HTML table format. */ public String getHelpAsHtmlTable(String query) { initParserKeyWords(); return ExpressionUtils.getHelpAsHtmlTable(keyWordsList, true, query); } /** * Returns (as HTML table) detailed user help on the syntax of mathematical expressions. * * @param addHeader Indicator whether to add a header. * @param addCaption Indicator whether to add caption. * @param addFigure Indicator whether to add a FIGURE tag. * @param caption If a non-standard caption is to be added, use any string other than "". * @param cssClass If CSS class is to be added, use any string other than "". * * @return One string value containing all the help. String in HTML table format. */ public String getHelpAsHtmlTable(boolean addHeader, boolean addCaption, boolean addFigure, String caption, String cssClass) { initParserKeyWords(); return ExpressionUtils.getHelpAsHtmlTable(keyWordsList, addHeader, addCaption, addFigure, StringInvariant.EMPTY, caption, cssClass); } /** * Returns (as HTML table) detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @param addHeader Indicator whether to add a header. * @param addCaption Indicator whether to add caption. * @param addFigure Indicator whether to add a FIGURE tag. * @param caption If a non-standard caption is to be added, use any string other than "". * @param cssClass If CSS class is to be added, use any string other than "". * * @return One string value containing all the help. String in HTML table format. */ public String getHelpAsHtmlTable(String query, boolean addHeader, boolean addCaption, boolean addFigure, String caption, String cssClass) { initParserKeyWords(); return ExpressionUtils.getHelpAsHtmlTable(keyWordsList, addHeader, addCaption, addFigure, query, caption, cssClass); } /** * Returns (as Markdown table) detailed user help on the syntax of mathematical expressions. * * @return One string value containing all the help. String in Markdown table format. */ public String getHelpAsMarkdownTable() { initParserKeyWords(); return ExpressionUtils.getHelpAsMarkdownTable(keyWordsList, StringInvariant.EMPTY); } /** * Returns (as Markdown table) detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * @return One string value containing all the help. String in Markdown table format. */ public String getHelpAsMarkdownTable(String query) { initParserKeyWords(); return ExpressionUtils.getHelpAsMarkdownTable(keyWordsList, query); } /** * Returns (as Markdown table) detailed user help on the syntax of mathematical expressions. * * @param addHeader Indicator whether to add a header. * @param addCaption Indicator whether to add caption. * @param caption If a non-standard caption is to be added, use any string other than "". * * @return One string value containing all the help. String in Markdown table format. */ public String getHelpAsMarkdownTable(boolean addHeader, boolean addCaption, String caption) { initParserKeyWords(); return ExpressionUtils.getHelpAsMarkdownTable(keyWordsList, addHeader, addCaption, StringInvariant.EMPTY, caption); } /** * Returns (as Markdown table) detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @param addHeader Indicator whether to add a header. * @param addCaption Indicator whether to add caption. * @param caption If a non-standard caption is to be added, use any string other than "". * * @return One string value containing all the help. String in Markdown table format. */ public String getHelpAsMarkdownTable(String query, boolean addHeader, boolean addCaption, String caption) { initParserKeyWords(); return ExpressionUtils.getHelpAsMarkdownTable(keyWordsList, addHeader, addCaption, query, caption); } /** * Returns (as Json) detailed user help on the syntax of mathematical expressions. * * @return One string value containing all the help. String in Json format. */ public String getHelpAsJson() { return getHelpAsJson(StringInvariant.EMPTY); } /** * Returns (as Json) detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @return One string value containing all the help. String in Json format. */ public String getHelpAsJson(String query) { return getHelpAsJson(query, true, StringInvariant.EMPTY); } /** * Returns (as Json) detailed user help on the syntax of mathematical expressions. * * @param addCaption Indicator whether to add caption. * @param caption If a non-standard caption is to be added, use any string other than "". * * @return One string value containing all the help. String in Json format. */ public String getHelpAsJson(boolean addCaption, String caption) { initParserKeyWords(); return ExpressionUtils.getHelpAsJson(keyWordsList, addCaption, StringInvariant.EMPTY, caption); } /** * Returns (as Json) detailed user help on the syntax of mathematical expressions. * Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @param addCaption Indicator whether to add caption. * @param caption If a non-standard caption is to be added, use any string other than "". * * @return One string value containing all the help. String in Json format. */ public String getHelpAsJson(String query, boolean addCaption, String caption) { initParserKeyWords(); return ExpressionUtils.getHelpAsJson(keyWordsList, addCaption, query, caption); } /** * Returns list of keywords known to the parser * * @return List of keywords known to the parser. * * @see KeyWord * @see KeyWord#wordTypeId * @see Expression#getHelp() */ public List getKeyWords() { return getKeyWords(StringInvariant.EMPTY); } /** * Returns list of keywords known to the parser. Allows simple and advanced searches. * * @param query For a simple search, simply enter a word (e.g.: "sine"). * Advanced search is also possible, please use one of the tags below: * "key=" - keyword (e.g.: "key=sin"), "desc=" - description (e.g.: "desc=trigonometric"), * "syn=" - syntax (e.g.: "syn=sin"), "type=" - type (e.g.: "type=unit"), * "since=" - since (e.g.: "since=4.1"), "typeid=" - please refer to parser tokens * (e.g.: "typeid=3"), "keyid=" - please refer to parser tokens (e.g.: "keyid=1004"). * Only one tag can be used per search. * * @return List of keywords known to the parser filter against query string. * * @see KeyWord * @see KeyWord#wordTypeId * @see Expression#getHelp(String) */ public List getKeyWords(String query) { initParserKeyWords(); return ExpressionUtils.getKeyWords(query, keyWordsList); } /* * shows tokens */ void showTokens() { showTokens(tokensList); } /* * show tokens */ static void showTokens(List tokensList) { ExpressionUtils.showTokens(tokensList); } /** * shows initial tokens */ void showInitialTokens() { showTokens(initialTokens); } /* * show arguments */ private void showArguments() { for (Argument a : argumentsList) { boolean vMode = a.getVerboseMode(); a.setSilentMode(); printSystemInfo(a.getArgumentName() + StringInvariant.SPACE_EQUAL_SPACE + a.getArgumentValue() + StringInvariant.NEW_LINE, WITH_EXP_STR); if (vMode) a.setVerboseMode(); } } /** * * @param info * @param withExpressionString */ private void printSystemInfo(String info, boolean withExpressionString) { if (withExpressionString) mXparser.consolePrint(StringUtils.surroundSquareBrackets(description) + StringUtils.surroundSquareBracketsAddSpace(expressionString) + info); else mXparser.consolePrint(info); } /** * Expression cloning. */ @Override protected Expression clone() { Expression newExp = new Expression(this, false, null); if (initialTokens != null && initialTokens.size() > 0) { newExp.initialTokens = createInitialTokens(0, initialTokens.size() - 1, initialTokens); newExp.initialCompilationDetails = initialCompilationDetails; } return newExp; } Expression cloneForThreadSafeInternal(CloneCache cloneCache) { Expression expressionClone = cloneCache.getExpressionClone(this); if (expressionClone == null) { cloneCache.cacheCloneInProgress(this); expressionClone = new Expression(this, true, cloneCache); if (initialTokens != null && initialTokens.size() > 0) { expressionClone.initialTokens = createInitialTokens(0, initialTokens.size() - 1, initialTokens); expressionClone.initialCompilationDetails = initialCompilationDetails; } cloneCache.clearCloneInProgress(this); cloneCache.cacheExpressionClone(this, expressionClone); } return expressionClone; } /** * Creates a completely independent 1-1 clone that can be safely used * by a separate thread. If the cloned element contains references * to other elements (e.g. arguments, functions, constants), * then they will also be cloned and the newly created element will * contain references to the corresponding clones. * Important - the API allows you to extract all these clones. * * @return Cloned object. */ public Expression cloneForThreadSafe() { CloneCache cloneCache = new CloneCache(); Expression expressionClone = cloneForThreadSafeInternal(cloneCache); cloneCache.addAllAtTheEndElements(); cloneCache.clearCache(); return expressionClone; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy