@Nonnull
  private static List<MathType> getMathTypesByPriority() {
    if (mathTypesByPriority == null) {
      final List<MathType> result = Arrays.asList(MathType.values());

      java.util.Collections.sort(
          result,
          new Comparator<MathType>() {
            @Override
            public int compare(MathType l, MathType r) {
              return l.priority.compareTo(r.priority);
            }
          });

      mathTypesByPriority = result;
    }

    return mathTypesByPriority;
  }
  @NotNull
  @Override
  public Result process(@NotNull String text) throws CalculatorParseException {
    final String result;

    int maxNumberOfOpenGroupSymbols = 0;
    int numberOfOpenGroupSymbols = 0;

    final StringBuilder text1 = new StringBuilder();

    int resultOffset = 0;

    final AbstractNumberBuilder numberBuilder;
    if (!formatNumber) {
      numberBuilder = new LiteNumberBuilder(CalculatorEngine.instance.getEngine());
    } else {
      numberBuilder = new NumberBuilder(CalculatorEngine.instance.getEngine());
    }
    for (int i = 0; i < text.length(); i++) {
      MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode());

      if (numberBuilder instanceof NumberBuilder) {
        final MutableObject<Integer> numberOffset = new MutableObject<Integer>(0);
        ((NumberBuilder) numberBuilder).process(text1, mathType, numberOffset);
        resultOffset += numberOffset.getObject();
      } else {
        ((LiteNumberBuilder) numberBuilder).process(mathType);
      }

      final String match = mathType.getMatch();
      switch (mathType.getMathType()) {
        case open_group_symbol:
          numberOfOpenGroupSymbols++;
          maxNumberOfOpenGroupSymbols =
              Math.max(maxNumberOfOpenGroupSymbols, numberOfOpenGroupSymbols);
          text1.append(text.charAt(i));
          break;
        case close_group_symbol:
          numberOfOpenGroupSymbols--;
          text1.append(text.charAt(i));
          break;
        case operator:
          text1.append(match);
          if (match.length() > 1) {
            i += match.length() - 1;
          }
          break;
        case function:
          i = processHighlightedText(text1, i, match, "i", null);
          break;
        case constant:
          i = processHighlightedText(text1, i, match, "b", null);
          break;
        case numeral_base:
          i = processHighlightedText(text1, i, match, "b", null);
          break;
        default:
          if (mathType.getMathType() == MathType.text || match.length() <= 1) {
            text1.append(text.charAt(i));
          } else {
            text1.append(match);
            i += match.length() - 1;
          }
      }
    }

    if (numberBuilder instanceof NumberBuilder) {
      final MutableObject<Integer> numberOffset = new MutableObject<Integer>(0);
      ((NumberBuilder) numberBuilder).processNumber(text1, numberOffset);
      resultOffset += numberOffset.getObject();
    }

    if (maxNumberOfOpenGroupSymbols > 0) {

      final StringBuilder text2 = new StringBuilder();

      String s = text1.toString();
      int i = processBracketGroup(text2, s, 0, 0, maxNumberOfOpenGroupSymbols);
      for (; i < s.length(); i++) {
        text2.append(s.charAt(i));
      }

      // Log.d(CalculatorEditor.class.getName(), text2.toString());

      result = text2.toString();
    } else {
      result = text1.toString();
    }

    return new Result(result, resultOffset);
  }
 public int processToJscl(@Nonnull StringBuilder result, int i) throws ParseException {
   return type.processToJscl(result, i, match);
 }
 public boolean isNeedMultiplicationSignBefore(@Nonnull MathType mathTypeBefore) {
   return needMultiplicationSignBefore && mathTypeBefore.isNeedMultiplicationSignAfter();
 }
  @Nonnull
  public static Result getType(
      @Nonnull String text,
      int i,
      boolean hexMode,
      @Nonnull Result result,
      @Nonnull Engine engine) {
    if (i < 0) {
      throw new IllegalArgumentException("I must be more or equals to 0.");
    } else if (i >= text.length() && i != 0) {
      throw new IllegalArgumentException("I must be less than size of text.");
    } else if (i == 0 && text.length() == 0) {
      return result.set(MathType.text, text);
    }
    final List<MathType> mathTypes = getMathTypesByPriority();
    for (int j = 0; j < mathTypes.size(); j++) {
      final MathType mathType = mathTypes.get(j);
      final String s = App.find(mathType.getTokens(engine), text, i);
      if (s == null) {
        continue;
      }

      if (s.length() > 1) {
        if (mathType == function) {
          final int nextToken = i + s.length();
          if (nextToken < text.length()) {
            // function must have an open group symbol after its name
            if (MathType.open_group_symbol
                .getTokens()
                .contains(text.substring(nextToken, nextToken + 1))) {
              return result.set(function, s);
            }
          } else if (nextToken == text.length()) {
            // or its name should finish the expression
            return result.set(function, s);
          }
          continue;
        }
        return result.set(mathType, s);
      }

      if (hexMode || JsclMathEngine.getInstance().getNumeralBase() == NumeralBase.hex) {
        final Character ch = s.charAt(0);
        if (NumeralBase.hex.getAcceptableCharacters().contains(ch)) {
          return result.set(MathType.digit, s);
        }
      }

      if (mathType == MathType.grouping_separator) {
        if (i + 1 < text.length()
            && MathType.digit.getTokens().contains(text.substring(i + 1, i + 2))
            && i - 1 >= 0
            && MathType.digit.getTokens().contains(text.substring(i - 1, i))) {
          return result.set(mathType, s);
        }
        continue;
      }

      return result.set(mathType, s);
    }

    return result.set(MathType.text, text.substring(i));
  }