diff --git a/java_console/.idea/libraries/commons_math3.xml b/java_console/.idea/libraries/commons_math3.xml
new file mode 100644
index 0000000000..0e5aa44f78
--- /dev/null
+++ b/java_console/.idea/libraries/commons_math3.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java_console/lib/commons-math3-3.3.jar b/java_console/lib/commons-math3-3.3.jar
new file mode 100644
index 0000000000..25102f2f93
Binary files /dev/null and b/java_console/lib/commons-math3-3.3.jar differ
diff --git a/java_console/models/models.iml b/java_console/models/models.iml
index 7e4d7d85e1..d60f872522 100644
--- a/java_console/models/models.iml
+++ b/java_console/models/models.iml
@@ -20,6 +20,7 @@
+
diff --git a/java_console/models/src/com/autsia/bracer/BracerParser.java b/java_console/models/src/com/autsia/bracer/BracerParser.java
new file mode 100644
index 0000000000..2a10fcdc75
--- /dev/null
+++ b/java_console/models/src/com/autsia/bracer/BracerParser.java
@@ -0,0 +1,458 @@
+/**
+ * TODO: for rusEfi purposes, we can remove Complex and thus not depend on
+ * apache-commons-math
+ *
+ * Copyright 2014 Dmytro Titov
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.autsia.bracer;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.StringTokenizer;
+
+import org.apache.commons.math3.complex.Complex;
+import org.apache.commons.math3.complex.ComplexFormat;
+
+/**
+ * Class for parsing and evaluating math expressions
+ *
+ * @author Dmytro Titov
+ * @version 7.0
+ * @since 1.0
+ */
+public class BracerParser {
+ /* list of available functions */
+ private final String[] FUNCTIONS = {"abs", "acos", "arg", "asin", "atan",
+ "conj", "cos", "cosh", "exp", "imag", "log", "neg", "pow", "real",
+ "sin", "sinh", "sqrt", "tan", "tanh", "not"};
+ /* list of available operators */
+ private final String OPERATORS = "+-*/&|!";
+ /* separator of arguments */
+ private final String SEPARATOR = ",";
+ /* imaginary symbol */
+ private final String IMAGINARY = "I";
+ /* variable token */
+ private final String VARIABLE = "var";
+ /* settings for complex formatting */
+ private ComplexFormat complexFormat = new ComplexFormat(IMAGINARY);
+ /* settings for numbers formatting */
+ private NumberFormat numberFormat = NumberFormat.getInstance(Locale.US);
+ /* temporary stack that holds operators, functions and brackets */
+ private Stack stackOperations = new Stack<>();
+ /* stack for holding expression converted to reversed polish notation */
+ private Stack stackRPN = new Stack<>();
+ /* stack for holding the calculations result */
+ private Stack stackAnswer = new Stack<>();
+
+ /**
+ * Class ctor for setting up the complex format of the parser
+ *
+ * @param precision Number of digits after the dot
+ * @since 2.0
+ */
+ public BracerParser(int precision) {
+ setPrecision(precision);
+ }
+
+ /**
+ * Set the precision of the real and imaginary parts of numbers
+ *
+ * @param precision Number of digits after the dot
+ * @since 2.0
+ */
+ public void setPrecision(int precision) {
+ numberFormat.setMinimumFractionDigits(precision);
+ numberFormat.setMaximumFractionDigits(precision);
+ complexFormat = new ComplexFormat(IMAGINARY, numberFormat, numberFormat);
+ }
+
+ /**
+ * Get the precision of the real and imaginary parts of numbers
+ *
+ * @return Precision
+ * @since 2.0
+ */
+ public int getPrecision() {
+ return numberFormat.getMinimumFractionDigits();
+ }
+
+ /**
+ * Parses the math expression (complicated formula) and stores the result
+ *
+ * @param expression String
input expression (math formula)
+ * @throws ParseException
if the input expression is not
+ * correct
+ * @since 3.0
+ */
+ public void parse(String expression) throws ParseException {
+ /* cleaning stacks */
+ stackOperations.clear();
+ stackRPN.clear();
+
+ /*
+ * make some preparations: remove spaces; handle unary + and -, handle
+ * degree character
+ */
+ expression = expression.replace(" ", "")
+ .replace("°", "*" + Double.toString(Math.PI) + "/180")
+ .replace("(-", "(0-").replace(",-", ",0-").replace("(+", "(0+")
+ .replace(",+", ",0+").replace("true", "1").replace("false", "0")
+ .replace("or", "|").replace("and", "&");
+ if (expression.charAt(0) == '-' || expression.charAt(0) == '+') {
+ expression = "0" + expression;
+ }
+ /* splitting input string into tokens */
+ StringTokenizer stringTokenizer = new StringTokenizer(expression,
+ OPERATORS + SEPARATOR + "()", true);
+
+ /* loop for handling each token - shunting-yard algorithm */
+ while (stringTokenizer.hasMoreTokens()) {
+ String token = stringTokenizer.nextToken();
+ if (isSeparator(token)) {
+ while (!stackOperations.empty()
+ && !isOpenBracket(stackOperations.lastElement())) {
+ stackRPN.push(stackOperations.pop());
+ }
+ } else if (isOpenBracket(token)) {
+ stackOperations.push(token);
+ } else if (isCloseBracket(token)) {
+ while (!stackOperations.empty()
+ && !isOpenBracket(stackOperations.lastElement())) {
+ stackRPN.push(stackOperations.pop());
+ }
+ stackOperations.pop();
+ if (!stackOperations.empty()
+ && isFunction(stackOperations.lastElement())) {
+ stackRPN.push(stackOperations.pop());
+ }
+ } else if (isNumber(token)) {
+ if (token.equals(IMAGINARY)) {
+ stackRPN.push(complexFormat.format(new Complex(0, 1)));
+ } else if (token.contains(IMAGINARY)) {
+ stackRPN.push(complexFormat.format(complexFormat.parse("0+"
+ + token)));
+ } else {
+ stackRPN.push(token);
+ }
+ } else if (isOperator(token)) {
+ while (!stackOperations.empty()
+ && isOperator(stackOperations.lastElement())
+ && getPrecedence(token) <= getPrecedence(stackOperations
+ .lastElement())) {
+ stackRPN.push(stackOperations.pop());
+ }
+ stackOperations.push(token);
+ } else if (isFunction(token)) {
+ stackOperations.push(token);
+ } else {
+ throw new ParseException("Unrecognized token: " + token, 0);
+ }
+ }
+ while (!stackOperations.empty()) {
+ stackRPN.push(stackOperations.pop());
+ }
+
+ /* reverse stack */
+ Collections.reverse(stackRPN);
+ }
+
+ /**
+ * Evaluates once parsed math expression with no variable included
+ *
+ * @return String
representation of the result
+ * @throws ParseException
if the input expression is not
+ * correct
+ * @since 1.0
+ */
+ public String evaluate() throws ParseException {
+ if (!stackRPN.contains("var")) {
+ return evaluate(0);
+ }
+ throw new ParseException("Unrecognized token: var", 0);
+ }
+
+ /**
+ * Evaluates once parsed math expression with "var" variable included
+ *
+ * @param variableValue User-specified Double
value
+ * @return String
representation of the result
+ * @throws ParseException
if the input expression is not
+ * correct
+ * @since 3.0
+ */
+ public String evaluate(double variableValue) throws ParseException {
+ /* check if is there something to evaluate */
+ if (stackRPN.empty()) {
+ return "";
+ }
+
+ /* clean answer stack */
+ stackAnswer.clear();
+
+ /* get the clone of the RPN stack for further evaluating */
+ @SuppressWarnings("unchecked")
+ Stack stackRPN = (Stack) this.stackRPN.clone();
+
+ /* enroll the variable value into expression */
+ Collections.replaceAll(stackRPN, VARIABLE,
+ Double.toString(variableValue));
+
+ /* evaluating the RPN expression */
+ while (!stackRPN.empty()) {
+ String token = stackRPN.pop();
+ if (isNumber(token)) {
+ stackAnswer.push(token);
+ } else if (isOperator(token)) {
+ Complex a = complexFormat.parse(stackAnswer.pop());
+ Complex b = complexFormat.parse(stackAnswer.pop());
+ boolean aBoolean = a.getReal() == 1.0;
+ boolean bBoolean = b.getReal() == 1.0;
+ switch (token) {
+ case "+":
+ stackAnswer.push(complexFormat.format(b.add(a)));
+ break;
+ case "-":
+ stackAnswer.push(complexFormat.format(b.subtract(a)));
+ break;
+ case "*":
+ stackAnswer.push(complexFormat.format(b.multiply(a)));
+ break;
+ case "/":
+ stackAnswer.push(complexFormat.format(b.divide(a)));
+ break;
+ case "|":
+ stackAnswer.push(String.valueOf(aBoolean || bBoolean ? "1" : "0"));
+ break;
+ case "&":
+ stackAnswer.push(String.valueOf(aBoolean && bBoolean ? "1" : "0"));
+ break;
+ }
+ } else if (isFunction(token)) {
+ Complex a = complexFormat.parse(stackAnswer.pop());
+ boolean aBoolean = a.getReal() == 1.0;
+ switch (token) {
+ case "abs":
+ stackAnswer.push(complexFormat.format(a.abs()));
+ break;
+ case "acos":
+ stackAnswer.push(complexFormat.format(a.acos()));
+ break;
+ case "arg":
+ stackAnswer.push(complexFormat.format(a.getArgument()));
+ break;
+ case "asin":
+ stackAnswer.push(complexFormat.format(a.asin()));
+ break;
+ case "atan":
+ stackAnswer.push(complexFormat.format(a.atan()));
+ break;
+ case "conj":
+ stackAnswer.push(complexFormat.format(a.conjugate()));
+ break;
+ case "cos":
+ stackAnswer.push(complexFormat.format(a.cos()));
+ break;
+ case "cosh":
+ stackAnswer.push(complexFormat.format(a.cosh()));
+ break;
+ case "exp":
+ stackAnswer.push(complexFormat.format(a.exp()));
+ break;
+ case "imag":
+ stackAnswer.push(complexFormat.format(a.getImaginary()));
+ break;
+ case "log":
+ stackAnswer.push(complexFormat.format(a.log()));
+ break;
+ case "neg":
+ stackAnswer.push(complexFormat.format(a.negate()));
+ break;
+ case "real":
+ stackAnswer.push(complexFormat.format(a.getReal()));
+ break;
+ case "sin":
+ stackAnswer.push(complexFormat.format(a.sin()));
+ break;
+ case "sinh":
+ stackAnswer.push(complexFormat.format(a.sinh()));
+ break;
+ case "sqrt":
+ stackAnswer.push(complexFormat.format(a.sqrt()));
+ break;
+ case "tan":
+ stackAnswer.push(complexFormat.format(a.tan()));
+ break;
+ case "tanh":
+ stackAnswer.push(complexFormat.format(a.tanh()));
+ break;
+ case "pow":
+ Complex b = complexFormat.parse(stackAnswer.pop());
+ stackAnswer.push(complexFormat.format(b.pow(a)));
+ break;
+ case "not":
+ stackAnswer.push(String.valueOf(!aBoolean ? "1" : "0"));
+ break;
+ }
+ }
+ }
+
+ if (stackAnswer.size() > 1) {
+ throw new ParseException("Some operator is missing", 0);
+ }
+
+ return stackAnswer.pop();
+ }
+
+ /**
+ * Evaluates non-variable expression and returns it's value as a Complex
+ * object
+ *
+ * @return Complex
representation of complex number
+ * @throws ParseException
if the input expression is not
+ * correct
+ * @since 4.0
+ */
+ public Complex evaluateComplex() throws ParseException {
+ return complexFormat.parse(evaluate());
+ }
+
+ /**
+ * Evaluates variable expression and returns it's value as a Complex object
+ *
+ * @param variableValue User-specified Double
value
+ * @return Complex
representation of complex number
+ * @throws ParseException
if the input expression is not
+ * correct
+ * @since 4.0
+ */
+ public Complex evaluateComplex(double variableValue) throws ParseException {
+ return complexFormat.parse(evaluate(variableValue));
+ }
+
+ /**
+ * Converts Complex
object to it's String
+ * representation
+ *
+ * @param number Input Complex
number to convert
+ * @return String
representation of complex number
+ * @since 5.0
+ */
+ public String format(Complex number) {
+ return complexFormat.format(number);
+ }
+
+ /**
+ * Get back an unmodifiable copy of the stack
+ */
+ public Collection getStackRPN() {
+ return Collections.unmodifiableCollection(stackRPN);
+ }
+
+ /**
+ * Check if the token is number (0-9, IMAGINARY
or
+ * VARIABLE
)
+ *
+ * @param token Input String
token
+ * @return boolean
output
+ * @since 1.0
+ */
+ private boolean isNumber(String token) {
+ try {
+ Double.parseDouble(token);
+ } catch (Exception e) {
+ return token.contains(IMAGINARY) || token.equals(VARIABLE);
+ }
+ return true;
+ }
+
+ /**
+ * Check if the token is function (e.g. "sin")
+ *
+ * @param token Input String
token
+ * @return boolean
output
+ * @since 1.0
+ */
+ private boolean isFunction(String token) {
+ for (String item : FUNCTIONS) {
+ if (item.equals(token)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if the token is SEPARATOR
+ *
+ * @param token Input String
token
+ * @return boolean
output
+ * @since 1.0
+ */
+ private boolean isSeparator(String token) {
+ return token.equals(SEPARATOR);
+ }
+
+ /**
+ * Check if the token is opening bracket
+ *
+ * @param token Input String
token
+ * @return boolean
output
+ * @since 1.0
+ */
+ private boolean isOpenBracket(String token) {
+ return token.equals("(");
+ }
+
+ /**
+ * Check if the token is closing bracket
+ *
+ * @param token Input String
token
+ * @return boolean
output
+ * @since 1.0
+ */
+ private boolean isCloseBracket(String token) {
+ return token.equals(")");
+ }
+
+ /**
+ * Check if the token is operator (e.g. "+")
+ *
+ * @param token Input String
token
+ * @return boolean
output
+ * @since 1.0
+ */
+ private boolean isOperator(String token) {
+ return OPERATORS.contains(token);
+ }
+
+ /**
+ * Gets the precedence of the operator
+ *
+ * @param token Input String
token
+ * @return byte
precedence
+ * @since 1.0
+ */
+ private byte getPrecedence(String token) {
+ if (token.equals("+") || token.equals("-")) {
+ return 1;
+ }
+ return 2;
+ }
+}