File indexing completed on 2024-05-19 05:51:35

0001 /*
0002  * SPDX-FileCopyrightText: 2023 Michael Lang <criticaltemp@protonmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "qalculateengine.h"
0008 #include "kalkconfig.h"
0009 
0010 #include <libqalculate/Calculator.h>
0011 
0012 #include <QLocale>
0013 
0014 #include <KLocalizedString>
0015 
0016 constexpr std::array<QStringView, 6> ANGLE_UNITS = {u"Radians", u"Degrees", u"Gradians", u"Arcminute", u"Arcsecond", u"Turn"};
0017 constexpr std::array<ParsingMode, 5> PARSING_MODES = {PARSING_MODE_ADAPTIVE,
0018                                                       PARSING_MODE_CONVENTIONAL,
0019                                                       PARSING_MODE_IMPLICIT_MULTIPLICATION_FIRST,
0020                                                       PARSING_MODE_CHAIN,
0021                                                       PARSING_MODE_RPN};
0022 
0023 QalculateEngine::QalculateEngine()
0024 {
0025     new Calculator();
0026     CALCULATOR->terminateThreads();
0027     CALCULATOR->loadGlobalDefinitions();
0028     CALCULATOR->loadLocalDefinitions();
0029     CALCULATOR->loadGlobalCurrencies();
0030     CALCULATOR->loadExchangeRates();
0031     CALCULATOR->loadGlobalPrefixes();
0032     CALCULATOR->loadGlobalUnits();
0033     CALCULATOR->loadGlobalDataSets();
0034 }
0035 
0036 QalculateEngine *QalculateEngine::inst()
0037 {
0038     static QalculateEngine singleton;
0039     return &singleton;
0040 }
0041 
0042 QString QalculateEngine::evaluate(QString &expression, bool *isApproximate, const int baseEval, const int basePrint, bool exact, const int minExp)
0043 {
0044     CALCULATOR->abort();
0045 
0046     if (QStringLiteral("+−-×÷/^").contains(expression.right(1))) {
0047         expression.truncate(expression.size() - 1);
0048     }
0049 
0050     // do not need to evalulate if the expression is just a number or if expression is incomplete
0051     if (baseEval == 10 && expression.toDouble() != 0 && !expression.contains(QStringLiteral("E"), Qt::CaseInsensitive)) {
0052         m_result = QString();
0053         return m_result;
0054     } else if (QStringLiteral("(√∛").contains(expression.right(1))) {
0055         m_result = QString();
0056         return m_result;
0057     }
0058 
0059     expression.replace(HAIR_SPACE.toString(), QStringLiteral(" + "));
0060     expression.replace(QStringLiteral("%%"), QStringLiteral("percentpercent"));
0061 
0062     const bool showAprox = expression.contains(QStringLiteral("∫")) || expression.contains(QStringLiteral("integra"), Qt::CaseInsensitive);
0063 
0064     EvaluationOptions eo;
0065     eo.auto_post_conversion = POST_CONVERSION_BEST;
0066     eo.keep_zero_units = false;
0067     eo.structuring = STRUCTURING_SIMPLIFY;
0068     eo.mixed_units_conversion = MIXED_UNITS_CONVERSION_NONE;
0069     eo.approximation = exact ? APPROXIMATION_EXACT : APPROXIMATION_TRY_EXACT;
0070     eo.parse_options.base = baseEval;
0071     eo.parse_options.parsing_mode = PARSING_MODES.at(KalkConfig::self()->parsingMode());
0072     eo.parse_options.angle_unit = ANGLE_UNIT_CUSTOM;
0073     eo.parse_options.limit_implicit_multiplication = false;
0074     eo.parse_options.unknowns_enabled = false;
0075 
0076     PrintOptions po;
0077     po.base = basePrint;
0078     po.number_fraction_format = exact ? FRACTION_COMBINED : FRACTION_DECIMAL;
0079     po.indicate_infinite_series = false;
0080     po.base_display = BASE_DISPLAY_NORMAL;
0081     po.interval_display = expression.contains(QStringLiteral("+/-")) ? INTERVAL_DISPLAY_PLUSMINUS : INTERVAL_DISPLAY_SIGNIFICANT_DIGITS;
0082     po.is_approximate = isApproximate;
0083     po.use_unicode_signs = true;
0084     po.binary_bits = baseEval == 2 ? std::max(std::ceil(expression.size() / 4.0) * 4.0, 4.0) : 0;
0085     po.preserve_precision = false;
0086     po.min_exp = minExp;
0087     po.multiplication_sign = MULTIPLICATION_SIGN_X;
0088     po.division_sign = DIVISION_SIGN_DIVISION_SLASH;
0089     po.improve_division_multipliers = showAprox;
0090     po.preserve_format = false;
0091     po.restrict_to_parent_precision = true;
0092 
0093     CALCULATOR->setCustomAngleUnit(CALCULATOR->getActiveUnit(ANGLE_UNITS.at(KalkConfig::self()->angleUnit()).toUtf8().constData()));
0094     CALCULATOR->setPrecision(KalkConfig::self()->precision());
0095 
0096     std::string input = CALCULATOR->unlocalizeExpression(expression.toStdString(), eo.parse_options);
0097     std::string parsed;
0098     std::string result;
0099     AutomaticFractionFormat fracFormat = exact ? AUTOMATIC_FRACTION_OFF : AUTOMATIC_FRACTION_SINGLE;
0100     AutomaticApproximation autoAprox = showAprox ? AUTOMATIC_APPROXIMATION_AUTO : AUTOMATIC_APPROXIMATION_SINGLE;
0101     bool is_comparison = false;
0102     result = CALCULATOR->calculateAndPrint(input, 2000, eo, po, fracFormat, autoAprox, &parsed, -1, &is_comparison, false);
0103     m_result = QString::fromStdString(result);
0104     qDebug() << QString::fromStdString(parsed) << (*isApproximate ? "≈" : "=") << m_result;
0105 
0106     while (CALCULATOR->message()) {
0107         MessageType mtype = CALCULATOR->message()->type();
0108         if (mtype != MESSAGE_INFORMATION) {
0109             QStringView type = mtype == MESSAGE_WARNING ? i18n("warning") : i18n("error");
0110             qDebug() << type + QStringLiteral(": ") + QString::fromStdString(CALCULATOR->message()->message());
0111         }
0112         CALCULATOR->nextMessage();
0113     }
0114 
0115     if (exact && m_result.contains(QStringLiteral(" ∕ "))) {
0116         m_result.replace(QStringLiteral(" ∕ "), FRACTION_SLASH.toString());
0117         m_result.replace(QStringLiteral(" + "), HAIR_SPACE.toString());
0118     }
0119 
0120     m_result.replace(QLatin1Char('.'), QLocale().decimalPoint(), Qt::CaseInsensitive);
0121 
0122     return m_result;
0123 }
0124 
0125 #include "moc_qalculateengine.cpp"