diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 01002e407..04d3ef1f5 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -6,6 +6,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 + with: + submodules: true - run: make -j2 PLATFORM=simulator TARGET=android - uses: actions/upload-artifact@master with: @@ -17,6 +19,8 @@ jobs: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - uses: numworks/setup-arm-toolchain@v1 - uses: actions/checkout@v1 + with: + submodules: true - run: make -j2 MODEL=n0100 epsilon.dfu - run: make -j2 MODEL=n0100 epsilon.onboarding.dfu - run: make -j2 MODEL=n0100 epsilon.onboarding.update.dfu @@ -34,6 +38,8 @@ jobs: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - uses: numworks/setup-arm-toolchain@v1 - uses: actions/checkout@v1 + with: + submodules: true - run: make -j2 epsilon.dfu - run: make -j2 epsilon.onboarding.dfu - run: make -j2 epsilon.onboarding.update.dfu @@ -47,19 +53,6 @@ jobs: name: epsilon-device-n0110.dfu path: output/release/device/n0110/epsilon.dfu - run: make -j2 test.elf - build-simulator-windows: - runs-on: windows-latest - steps: - - uses: numworks/setup-msys2@v1 - - uses: actions/checkout@v1 - - run: msys2do pacman -S --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-freetype mingw-w64-x86_64-pkg-config make mingw-w64-x86_64-python3 mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-libpng - - run: msys2do make -j2 PLATFORM=simulator - - uses: actions/upload-artifact@master - with: - name: epsilon-simulator-windows.exe - path: output/release/simulator/windows/epsilon.exe - - run: msys2do make -j2 PLATFORM=simulator test.headless.exe - - run: output\release\simulator\windows\test.headless.exe build-simulator-web: runs-on: ubuntu-latest steps: @@ -67,44 +60,24 @@ jobs: with: sdk: latest-fastcomp - uses: actions/checkout@v1 + with: + submodules: true - run: make -j2 PLATFORM=simulator TARGET=web - uses: actions/upload-artifact@master with: name: epsilon-simulator-web.zip path: output/release/simulator/web/simulator.zip - run: make -j2 PLATFORM=simulator TARGET=web test.headless.js - - run: node output/release/simulator/web/test.headless.js build-simulator-linux: runs-on: ubuntu-latest steps: - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - uses: actions/checkout@v1 + with: + submodules: true - run: make -j2 PLATFORM=simulator - uses: actions/upload-artifact@master with: name: epsilon-simulator-linux.bin path: output/release/simulator/linux/epsilon.bin - run: make -j2 PLATFORM=simulator test.headless.bin - - run: output/release/simulator/linux/test.headless.bin - build-simulator-macos: - runs-on: macOS-latest - steps: - - run: brew install numworks/tap/epsilon-sdk - - uses: actions/checkout@v1 - - run: make -j2 PLATFORM=simulator - - uses: actions/upload-artifact@master - with: - name: epsilon-simulator-macos.zip - path: output/release/simulator/macos/app/Payload - - run: make -j2 PLATFORM=simulator ARCH=x86_64 test.headless.bin - - run: output/release/simulator/macos/x86_64/test.headless.bin - build-simulator-ios: - runs-on: macOS-latest - steps: - - run: brew install numworks/tap/epsilon-sdk - - uses: actions/checkout@v1 - - run: make -j2 PLATFORM=simulator TARGET=ios EPSILON_TELEMETRY=0 - - uses: actions/upload-artifact@master - with: - name: epsilon-simulator-ios.ipa - path: output/release/simulator/ios/app/epsilon.ipa diff --git a/.gitignore b/.gitignore index 5019cb014..7212d2758 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ /output/ +/build/artifacts/ build/device/**/*.pyc epsilon.elf .vscode -.DS_Store \ No newline at end of file +.DS_Store +.gradle diff --git a/Makefile b/Makefile index 82040ffdf..c260884d4 100644 --- a/Makefile +++ b/Makefile @@ -126,8 +126,7 @@ include build/struct_layout/Makefile include build/scenario/Makefile include quiz/Makefile # Quiz needs to be included at the end -all_src = $(apps_all_src) $(escher_src) $(ion_all_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(epsilon_src) $(runner_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(ion_target_device_bench_src) $(tests_src) - +all_src = $(apps_all_src) $(escher_src) $(ion_all_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(poincare_src) $(python_src) $(runner_src) $(ion_target_device_flasher_light_src) $(ion_target_device_flasher_verbose_src) $(ion_target_device_bench_src) $(tests_src) # Make palette.h a dep for every source-file. # This ensures that the theming engine works correctly. $(call object_for,$(all_app_src)): $(BUILD_DIR)/escher/palette.h diff --git a/apps/Makefile b/apps/Makefile index 15d4774da..b6d6ae189 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -94,7 +94,7 @@ $(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp $(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png)) -all_app_src = $(app_src) $(epsilon_src) $(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(apps_official) $(apps_non_official) $(tests_src) +all_app_src = $(app_src)(apps_launch_on_boarding_src) $(apps_launch_default_src) $(apps_prompt_none_src) $(apps_prompt_update_src) $(apps_prompt_beta_src) $(apps_official) $(apps_non_official) $(tests_src) $(call object_for,$(all_app_src)): $(BUILD_DIR)/apps/i18n.h $(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h diff --git a/apps/calculation/additional_outputs/complex_graph_cell.cpp b/apps/calculation/additional_outputs/complex_graph_cell.cpp index aabe9b630..e4249e8f2 100644 --- a/apps/calculation/additional_outputs/complex_graph_cell.cpp +++ b/apps/calculation/additional_outputs/complex_graph_cell.cpp @@ -6,7 +6,7 @@ using namespace Poincare; namespace Calculation { ComplexGraphView::ComplexGraphView(ComplexModel * complexModel) : - CurveView(complexModel), + LabeledCurveView(complexModel), m_complex(complexModel) { } diff --git a/apps/calculation/additional_outputs/complex_graph_cell.h b/apps/calculation/additional_outputs/complex_graph_cell.h index bf440202f..7777c9991 100644 --- a/apps/calculation/additional_outputs/complex_graph_cell.h +++ b/apps/calculation/additional_outputs/complex_graph_cell.h @@ -1,24 +1,19 @@ #ifndef CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_GRAPH_CELL_H #define CALCULATION_ADDITIONAL_OUTPUTS_COMPLEX_GRAPH_CELL_H -#include "../../shared/curve_view.h" +#include "../../shared/labeled_curve_view.h" #include "complex_model.h" #include "illustration_cell.h" namespace Calculation { -class ComplexGraphView : public Shared::CurveView { +class ComplexGraphView : public Shared::LabeledCurveView { public: ComplexGraphView(ComplexModel * complexModel); void drawRect(KDContext * ctx, KDRect rect) const override; private: - char * label(Axis axis, int index) const override { - return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]); - } // '-' + significant digits + ".E-" + 2 digits (the represented dot is a float, so it is bounded by 1E38 and 1E-38 size_t labelMaxGlyphLengthSize() const override { return 1 + Poincare::Preferences::VeryShortNumberOfSignificantDigits + 3 + 2; } - char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; - char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize]; ComplexModel * m_complex; }; diff --git a/apps/calculation/additional_outputs/trigonometry_graph_cell.h b/apps/calculation/additional_outputs/trigonometry_graph_cell.h index 1bf126707..aea81926a 100644 --- a/apps/calculation/additional_outputs/trigonometry_graph_cell.h +++ b/apps/calculation/additional_outputs/trigonometry_graph_cell.h @@ -12,7 +12,6 @@ public: TrigonometryGraphView(TrigonometryModel * model); void drawRect(KDContext * ctx, KDRect rect) const override; private: - char * label(Axis axis, int index) const override { return nullptr; } TrigonometryModel * m_model; }; diff --git a/apps/calculation/base.de.i18n b/apps/calculation/base.de.i18n index e71cdc958..ebb1bd928 100644 --- a/apps/calculation/base.de.i18n +++ b/apps/calculation/base.de.i18n @@ -1,9 +1,9 @@ CalculApp = "Berechnung" CalculAppCapital = "BERECHNUNG" -AdditionalResults = "????" -DecimalBase = "????" -HexadecimalBase = "????" -BinaryBase = "????" -PrimeFactors = "????" -MixedFraction = "????" -EuclideanDivision = "????" +AdditionalResults = "Weitere Ergebnisse" +DecimalBase = "Dezimal" +HexadecimalBase = "Hexadezimal" +BinaryBase = "Binär" +PrimeFactors = "Primfaktor" +MixedFraction = "Gemischte Fraktion" +EuclideanDivision = "Euklidische Division" diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index ce6360029..ff41c7b3e 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -186,7 +186,7 @@ void HistoryViewCell::layoutSubviews(bool force) { force); KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); int singleLine = outputSize.width() + inputSize.width() < bounds().width() - 6; - int outputHeight = (singleLine) ? (maxCoordinate(0, inputSize.height() - outputSize.height()) / 2) + maxCoordinate(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height(); + int outputHeight = (singleLine && Poincare::Preferences::sharedPreferences()->resultDisplay() == Poincare::Preferences::ResultDisplay::Compact) ? (maxCoordinate(0, inputSize.height() - outputSize.height()) / 2) + maxCoordinate(0, (inputSize.height() - outputSize.height()) / 2) : inputSize.height(); m_scrollableOutputView.setFrame(KDRect( maxCoordinate(0, maxFrameWidth - outputSize.width()), outputHeight, diff --git a/apps/code/app.cpp b/apps/code/app.cpp index 665a09ce4..10d372183 100644 --- a/apps/code/app.cpp +++ b/apps/code/app.cpp @@ -49,8 +49,10 @@ bool App::Snapshot::lockOnConsole() const { } void App::Snapshot::setOpt(const char * name, const char * value) { - if (strcmp(name, "script") == 0) { + if (strcmp(name, "wipe") == 0) { m_scriptStore.deleteAllScripts(); + } + if (strcmp(name, "script") == 0) { char * separator = const_cast(UTF8Helper::CodePointSearch(value, ':')); if (*separator == 0) { return; diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index c833b7633..f8a651596 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -12,104 +12,104 @@ PythonSingleQuote = "Einfaches Anführungszeichen" PythonAbs = "Absolute/r Wert/Größe" PythonAcos = "Arkuskosinus" PythonAcosh = "Hyperbelkosinus" -PythonAppend = "Add x to the end of the list" +PythonAppend = "Hängt x an das Ende der Liste" PythonAsin = "Arkussinus" PythonAsinh = "Hyperbelsinus" PythonAtan = "Arkustangens" PythonAtan2 = "Gib atan(y/x)" PythonAtanh = "Hyperbeltangens" -PythonBin = "Ganzzahl nach binär konvertieren" +PythonBin = "Ganzzahl nach binär" PythonCeil = "Aufrundung" PythonChoice = "Zufallszahl aus der Liste" -PythonClear = "Empty the list" +PythonClear = "Leere die Liste" PythonCmathFunction = "cmath-Modul-Funktionspräfix" -PythonColor = "Definiere eine RGB-Farbe" +PythonColor = "Definiert eine RGB-Farbe" PythonComplex = "a+ib zurückgeben" -PythonCopySign = "Return x with the sign of y" +PythonCopySign = "x mit dem Vorzeichen von y" PythonCos = "Kosinus" PythonCosh = "Hyperbolic cosine" -PythonCount = "Count the occurrences of x" -PythonDegrees = "Convert x from radians to degrees" -PythonDivMod = "Quotient and remainder" -PythonDrawString = "Display a text from pixel (x,y)" -PythonErf = "Error function" +PythonCount = "Zählt wie oft x vorkommt" +PythonDegrees = "x von Radian zu Grad umwandeln" +PythonDivMod = "Quotient und Rest" +PythonDrawString = "Schreibt Text bei (x,y)" +PythonErf = "Fehlerfunktion" PythonErfc = "Complementary error function" PythonEval = "Return the evaluated expression" -PythonExp = "Exponential function" -PythonExpm1 = "Compute exp(x)-1" -PythonFabs = "Absolute value" -PythonFillRect = "Fill a rectangle at pixel (x,y)" -PythonFloat = "Convert x to a float" +PythonExp = "Exponentialfunktion" +PythonExpm1 = "Berechne exp(x)-1" +PythonFabs = "Absoluter Wert" +PythonFillRect = "Malt ein Rechteck bei Pixel (x,y)" +PythonFloat = "Wandelt x zu float um" PythonFloor = "Floor" PythonFmod = "a modulo b" -PythonFrExp = "Mantissa and exponent of x" -PythonGamma = "Gamma function" -PythonGetPixel = "Return pixel (x,y) color" -PythonGetrandbits = "Integer with k random bits" -PythonHex = "Convert integer to hexadecimal" -PythonImportCmath = "Import cmath module" -PythonImportIon = "Import ion module" -PythonImportKandinsky = "Import kandinsky module" -PythonImportRandom = "Import random module" -PythonImportMath = "Import math module" -PythonImportTime = "Import time module" -PythonImportTurtle = "Import turtle module" -PythonIndex = "Index of the first x occurrence" -PythonInput = "Prompt a value" -PythonInsert = "Insert x at index i in the list" -PythonInt = "Convert x to an integer" +PythonFrExp = "Rest und Exponent von x" +PythonGamma = "Gammafunktion" +PythonGetPixel = "Farbe von Pixel (x,y)" +PythonGetrandbits = "Ganzzahl mit k zufälligen Bits" +PythonHex = "Ganzzahl zu Hexadecimal" +PythonImportCmath = "cmath Modul importieren" +PythonImportIon = "ion Modul importieren" +PythonImportKandinsky = "kandinsky Modul importieren" +PythonImportRandom = "random Modul importieren" +PythonImportMath = "math Modul importieren" +PythonImportTime = "time Modul importieren" +PythonImportTurtle = "turtle Modul importieren" +PythonIndex = "Index, bei dem x zuerst vorkommt" +PythonInput = "Eingabeaufforderung" +PythonInsert = "x bei index i in der Liste einsetzen" +PythonInt = "x zu Ganzzahl" PythonIonFunction = "ion module function prefix" -PythonIsFinite = "Check if x is finite" -PythonIsInfinite = "Check if x is infinity" -PythonIsNaN = "Check if x is a NaN" -PythonIsKeyDown = "Return True if the k key is down" +PythonIsFinite = "Prüft ob x endlich ist" +PythonIsInfinite = "Prüft ob x unendlich ist" +PythonIsNaN = "Prüft ob x NaN ist" +PythonIsKeyDown = "true wenn k gedrückt ist" PythonKandinskyFunction = "kandinsky module function prefix" -PythonKeyLeft = "LEFT ARROW key" -PythonKeyUp = "UP ARROW key" -PythonKeyDown = "DOWN ARROW key" -PythonKeyRight = "RIGHT ARROW key" -PythonKeyOk = "OK key" -PythonKeyBack = "BACK key" -PythonKeyHome = "HOME key" -PythonKeyOnOff = "ON/OFF key" -PythonKeyShift = "SHIFT key" -PythonKeyAlpha = "ALPHA key" -PythonKeyXnt = "X,N,T key" -PythonKeyVar = "VAR key" -PythonKeyToolbox = "TOOLBOX key" -PythonKeyBackspace = "BACKSPACE key" -PythonKeyExp = "EXPONENTIAL key" -PythonKeyLn = "NATURAL LOGARITHM key" -PythonKeyLog = "DECIMAL LOGARITHM key" -PythonKeyImaginary = "IMAGINARY I key" -PythonKeyComma = "COMMA key" -PythonKeyPower = "POWER key" -PythonKeySine = "SINE key" -PythonKeyCosine = "COSINE key" -PythonKeyTangent = "TANGENT key" -PythonKeyPi = "PI key" -PythonKeySqrt = "SQUARE ROOT key" -PythonKeySquare = "SQUARE key" -PythonKeySeven = "7 key" -PythonKeyEight = "8 key" -PythonKeyNine = "9 key" +PythonKeyLeft = "Linke Pfeiltaste" +PythonKeyUp = "Pfeiltaste nach oben" +PythonKeyDown = "Pfeiltaste nach unten" +PythonKeyRight = "Rechte Pfeiltaste" +PythonKeyOk = "OK Taste" +PythonKeyBack = "ZURÜCK Taste" +PythonKeyHome = "HOME Taste" +PythonKeyOnOff = "AN/AUS Taste" +PythonKeyShift = "SHIFT Taste" +PythonKeyAlpha = "ALPHA Taste" +PythonKeyXnt = "X,N,T Taste" +PythonKeyVar = "VAR Taste" +PythonKeyToolbox = "WERKZEUGBOX Taste" +PythonKeyBackspace = "LÖSCHEN Taste" +PythonKeyExp = "EXPONENTIAL Taste" +PythonKeyLn = "NATURAL LOGARITHM Taste" +PythonKeyLog = "DECIMAL LOGARITHM Taste" +PythonKeyImaginary = "IMAGINÄRES I Taste" +PythonKeyComma = "KOMMA Taste" +PythonKeyPower = "HOCH Taste" +PythonKeySine = "SINUS Taste" +PythonKeyCosine = "COSINUS Taste" +PythonKeyTangent = "TANGENZ Taste" +PythonKeyPi = "PI Taste" +PythonKeySqrt = "WURZEL Taste" +PythonKeySquare = "QUADRAT Taste" +PythonKeySeven = "7 Taste" +PythonKeyEight = "8 Taste" +PythonKeyNine = "9 Taste" PythonKeyLeftParenthesis = "LEFT PARENTHESIS key" PythonKeyRightParenthesis = "RIGHT PARENTHESIS key" -PythonKeyFour = "4 key" -PythonKeyFive = "5 key" -PythonKeySix = "6 key" -PythonKeyMultiplication = "MULTIPLICATION key" -PythonKeyDivision = "DIVISION key" -PythonKeyOne = "1 key" -PythonKeyTwo = "2 key" -PythonKeyThree = "3 key" -PythonKeyPlus = "PLUS key" -PythonKeyMinus = "MINUS key" -PythonKeyZero = "0 key" -PythonKeyDot = "DOT key" -PythonKeyEe = "10 POWER X key" -PythonKeyAns = "ANS key" -PythonKeyExe = "EXE key" +PythonKeyFour = "4 Taste" +PythonKeyFive = "5 Taste" +PythonKeySix = "6 Taste" +PythonKeyMultiplication = "MULTIPLIKATIONSTASTE" +PythonKeyDivision = "DIVISIONSTASTE" +PythonKeyOne = "1 Taste" +PythonKeyTwo = "2 Taste" +PythonKeyThree = "3 Taste" +PythonKeyPlus = "PLUS Taste" +PythonKeyMinus = "MINUS Taste" +PythonKeyZero = "0 Taste" +PythonKeyDot = "PUNKT Taste" +PythonKeyEe = "10 HOCH X Taste" +PythonKeyAns = "ANS Taste" +PythonKeyExe = "EXE Taste" PythonLdexp = "Return x*(2**i), inverse of frexp" PythonLength = "Length of an object" PythonLgamma = "Log-gamma function" @@ -140,27 +140,27 @@ PythonReverse = "Reverse the elements of the list" PythonRound = "Round to n digits" PythonSeed = "Initialize random number generator" PythonSetPixel = "Color pixel (x,y)" -PythonSin = "Sine" +PythonSin = "Sinus" PythonSinh = "Hyperbolic sine" PythonSleep = "Suspend the execution for t seconds" PythonSort = "Sort the list" -PythonSqrt = "Square root" +PythonSqrt = "Wurzel" PythonSum = "Sum the items of a list" -PythonTan = "Tangent" +PythonTan = "Tangens" PythonTanh = "Hyperbolic tangent" PythonTimeFunction = "time module function prefix" PythonTrunc = "x truncated to an integer" PythonTurtleBackward = "Move backward by x pixels" -PythonTurtleBlack = "Black color" -PythonTurtleBlue = "Blue color" -PythonTurtleBrown = "Brown color" +PythonTurtleBlack = "Schwarze Farbe" +PythonTurtleBlue = "Blaue Farbe" +PythonTurtleBrown = "Braune Farbe" PythonTurtleCircle = "Circle of radius r pixels" -PythonTurtleColor = "Set the pen color" +PythonTurtleColor = "Stiftfarbe setzen" PythonTurtleForward = "Move forward by x pixels" PythonTurtleFunction = "turtle module function prefix" PythonTurtleGoto = "Move to (x,y) coordinates" -PythonTurtleGreen = "Green color" -PythonTurtleGrey = "Grey color" +PythonTurtleGreen = "Grüne Farbe" +PythonTurtleGrey = "Graue Farbe" PythonTurtleHeading = "Return the current heading" PythonTurtleHideturtle = "Hide the turtle" PythonTurtleIsdown = "Return True if the pen is down" @@ -169,19 +169,19 @@ PythonTurtleOrange = "Orange color" PythonTurtlePendown = "Pull the pen down" PythonTurtlePensize = "Set the line thickness to x pixels" PythonTurtlePenup = "Pull the pen up" -PythonTurtlePink = "Pink color" +PythonTurtlePink = "Pinke Farbe" PythonTurtlePosition = "Return the current (x,y) location" PythonTurtlePurple = "Purple color" -PythonTurtleRed = "Red color" +PythonTurtleRed = "Rote Farbe" PythonTurtleReset = "Reset the drawing" PythonTurtleRight = "Turn right by a degrees" PythonTurtleSetheading = "Set the orientation to a degrees" -PythonTurtleSetposition = "Positionne la tortue" -PythonTurtleShowturtle = "Show the turtle" -PythonTurtleSpeed = "Drawing speed between 0 and 10" -PythonTurtleWhite = "White color" -PythonTurtleYellow = "Yellow color" -PythonUniform = "Floating point number in [a,b]" +PythonTurtleSetposition = "Position des turtles" +PythonTurtleShowturtle = "Die turtle anzeigen" +PythonTurtleSpeed = "Zeichengeschwindigkeit zwischen 0 und 10" +PythonTurtleWhite = "Weiße Farbe" +PythonTurtleYellow = "Gelbe Farbe" +PythonUniform = "Fließkommazahl in [a,b]" PythonTimeFromImport = "Import time module" PythonTimeImport = "Import time module" PythonTimePrefix = "time module function prefix" diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index 4e916904e..6b3c9766f 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -400,6 +400,9 @@ void ConsoleController::resetSandbox() { } void ConsoleController::refreshPrintOutput() { + if (sandboxIsDisplayed()) { + return; + } m_selectableTableView.reloadData(); m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines()); if (m_preventEdition) { @@ -431,11 +434,26 @@ void ConsoleController::printText(const char * text, size_t length) { flushOutputAccumulationBufferToStore(); micropython_port_vm_hook_refresh_print(); } +#if __EMSCRIPTEN__ + /* If we called micropython_port_interrupt_if_needed here, we would need to + * put in the WHITELIST all the methods that call + * ConsoleController::printText, which means all the MicroPython methods that + * call print... This is a lot of work + might reduce the performance as + * emterpreted code is slower. + * + * We thus do not allow print interruption on the web simulator. It would be + * better to allow it, but the biggest problem was on the device anyways + * -> It is much quicker to interrupt Python on the web simulator than on the + * device. + * + * TODO: Allow print interrpution on emscripten -> maybe by using WASM=1 ? */ +#else /* micropython_port_vm_hook_loop is not enough to detect user interruptions, * because it calls micropython_port_interrupt_if_needed every 20000 * operations, and a print operation is quite long. We thus explicitely call * micropython_port_interrupt_if_needed here. */ micropython_port_interrupt_if_needed(); +#endif } void ConsoleController::autoImportScript(Script script, bool force) { diff --git a/apps/code/console_edit_cell.cpp b/apps/code/console_edit_cell.cpp index 30e0472fb..f938f52dc 100644 --- a/apps/code/console_edit_cell.cpp +++ b/apps/code/console_edit_cell.cpp @@ -7,6 +7,8 @@ namespace Code { +static inline int minInt(int x, int y) { return x < y ? x : y; } + ConsoleEditCell::ConsoleEditCell(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, TextFieldDelegate * delegate) : HighlightCell(), Responder(parentResponder), @@ -65,9 +67,12 @@ void ConsoleEditCell::clearAndReduceSize() { const char * ConsoleEditCell::shiftCurrentTextAndClear() { size_t previousBufferSize = m_textField.draftTextBufferSize(); m_textField.setDraftTextBufferSize(previousBufferSize + 1); - char * textFieldBuffer = m_textField.draftTextBuffer(); + char * textFieldBuffer = const_cast(m_textField.text()); char * newTextPosition = textFieldBuffer + 1; - strlcpy(newTextPosition, textFieldBuffer, previousBufferSize); + assert(previousBufferSize > 0); + size_t copyLength = minInt(previousBufferSize - 1, strlen(textFieldBuffer)); + memmove(newTextPosition, textFieldBuffer, copyLength); + newTextPosition[copyLength] = 0; textFieldBuffer[0] = 0; return newTextPosition; } diff --git a/apps/graph/app.cpp b/apps/graph/app.cpp index 52b94f4be..264e699da 100644 --- a/apps/graph/app.cpp +++ b/apps/graph/app.cpp @@ -58,7 +58,7 @@ App::App(Snapshot * snapshot) : m_listFooter(&m_listHeader, &m_listController, &m_listController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey), m_listHeader(&m_listStackViewController, &m_listFooter, &m_listController), m_listStackViewController(&m_tabViewController, &m_listHeader), - m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader), + m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader), m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController), m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController), m_graphStackViewController(&m_tabViewController, &m_graphHeader), diff --git a/apps/graph/graph/graph_controller.cpp b/apps/graph/graph/graph_controller.cpp index 9f45f3eff..74cc400dc 100644 --- a/apps/graph/graph/graph_controller.cpp +++ b/apps/graph/graph/graph_controller.cpp @@ -11,8 +11,8 @@ static inline float maxFloat(float x, float y) { return x > y ? x : y; } static inline double minDouble(double x, double y) { return x < y ? x : y; } static inline double maxDouble(double x, double y) { return x > y ? x : y; } -GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) : - FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion), +GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) : + FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, rangeVersion, angleUnitVersion), m_bannerView(this, inputEventHandlerDelegate, this), m_view(curveViewRange, m_cursor, &m_bannerView, &m_cursorView), m_graphRange(curveViewRange), diff --git a/apps/graph/graph/graph_controller.h b/apps/graph/graph/graph_controller.h index 8860dd245..84240fcd3 100644 --- a/apps/graph/graph/graph_controller.h +++ b/apps/graph/graph/graph_controller.h @@ -15,7 +15,7 @@ namespace Graph { class GraphController : public Shared::FunctionGraphController, public GraphControllerHelper { public: - GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header); + GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, Shared::InteractiveCurveViewRange * curveViewRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header); I18n::Message emptyMessage() override; void viewWillAppear() override; bool displayDerivativeInBanner() const { return m_displayDerivativeInBanner; } diff --git a/apps/home/base.de.i18n b/apps/home/base.de.i18n index 352d41520..99f705c4b 100644 --- a/apps/home/base.de.i18n +++ b/apps/home/base.de.i18n @@ -1,4 +1,4 @@ Apps = "Anwendungen" AppsCapital = "OMEGA" -ForbidenAppInExamMode1 = "This application is" -ForbidenAppInExamMode2 = "forbidden in exam mode" +ForbidenAppInExamMode1 = "Diese Anwendung ist im" +ForbidenAppInExamMode2 = "Prüfungsmodus verboten" diff --git a/apps/probability/distribution/distribution.cpp b/apps/probability/distribution/distribution.cpp index ac0c8f517..aae3631d0 100644 --- a/apps/probability/distribution/distribution.cpp +++ b/apps/probability/distribution/distribution.cpp @@ -25,9 +25,12 @@ double Distribution::rightIntegralFromAbscissa(double x) const { } double Distribution::finiteIntegralBetweenAbscissas(double a, double b) const { - if (b <= a) { + if (b < a) { return 0.0; } + if (a == b) { + return evaluateAtDiscreteAbscissa(a); + } if (isContinuous()) { return cumulativeDistributiveFunctionAtAbscissa(b) - cumulativeDistributiveFunctionAtAbscissa(a); } @@ -100,6 +103,7 @@ double Distribution::rightIntegralInverseForProbability(double * probability) { } double Distribution::evaluateAtDiscreteAbscissa(int k) const { + assert(isContinuous()); // Discrete distributions override this method return 0.0; } diff --git a/apps/probability/test/distributions.cpp b/apps/probability/test/distributions.cpp index 4cb4b5c84..20bd9d9f9 100644 --- a/apps/probability/test/distributions.cpp +++ b/apps/probability/test/distributions.cpp @@ -19,7 +19,13 @@ void assert_cumulative_distributive_function_direct_and_inverse_is(Probability:: quiz_assert(!std::isnan(r)); quiz_assert(!std::isinf(r)); quiz_assert(std::fabs(r-x) < FLT_EPSILON || std::fabs(r-x)/x < FLT_EPSILON); +} +void assert_finite_integral_between_abscissas_is(Probability::Distribution * distribution, double a, double b, double result) { + double r = distribution->finiteIntegralBetweenAbscissas(a, b); + quiz_assert(!std::isnan(r)); + quiz_assert(!std::isinf(r)); + quiz_assert(std::fabs(r-result) < FLT_EPSILON || std::fabs(r-result)/result < FLT_EPSILON); } //TODO other distributions @@ -37,6 +43,13 @@ QUIZ_CASE(binomial_distribution) { distribution.setParameterAtIndex(0.1, 1); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 0.0, 0.166771816996665822596668249389040283858776092529296875); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.0, 0.4817852491014791294077213024138472974300384521484375); + + // B(21, 0.2) + distribution.setParameterAtIndex(21.0, 0); + distribution.setParameterAtIndex(0.2, 1); + assert_finite_integral_between_abscissas_is(&distribution, 4.0, 4.0, 0.21563235015849934848); + assert_finite_integral_between_abscissas_is(&distribution, 5.0, 4.0, 0.0); + assert_finite_integral_between_abscissas_is(&distribution, 4.0, 5.0, 0.398919847793223794688); } QUIZ_CASE(chi_squared_distribution) { @@ -58,6 +71,12 @@ QUIZ_CASE(chi_squared_distribution) { assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.3, 0.047059684573231390369851823152202996425330638885498046875); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 2.9874567, 0.250530060451470470983537097708904184401035308837890625); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 4.987, 0.53051693435084168459781039928202517330646514892578125); + + // Chi Squared distribution with 6 degrees of freedom + distribution.setParameterAtIndex(6.0, 0); + assert_finite_integral_between_abscissas_is(&distribution, 4.0, 4.0, 0.0); + assert_finite_integral_between_abscissas_is(&distribution, 5.0, 4.0, 0.0); + assert_finite_integral_between_abscissas_is(&distribution, 4.0, 5.0, 0.1328633002997339414718); } QUIZ_CASE(student_distribution) { @@ -79,6 +98,12 @@ QUIZ_CASE(student_distribution) { assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, -4.987, 0.00167496657737900025118837898929768925881944596767425537109375); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.3, 0.876837383157582639370275501278229057788848876953125); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 2.9874567, 0.98612148076325445433809591122553683817386627197265625); + + // Student distribution with 6 degrees of freedom + distribution.setParameterAtIndex(6.0, 0); + assert_finite_integral_between_abscissas_is(&distribution, 4.0, 4.0, 0.0); + assert_finite_integral_between_abscissas_is(&distribution, 5.0, 4.0, 0.0); + assert_finite_integral_between_abscissas_is(&distribution, 4.0, 5.0, 0.002333318101494775250); } QUIZ_CASE(geometric_distribution) { @@ -92,6 +117,12 @@ QUIZ_CASE(geometric_distribution) { distribution.setParameterAtIndex(0.2, 0); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 7.0, 0.8322278399999998299563230830244719982147216796875); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 3.0, 0.5904); + + // Geometric distribution with probability of success 0.4 + distribution.setParameterAtIndex(0.4, 0); + assert_finite_integral_between_abscissas_is(&distribution, 1.0, 1.0, 0.24); + assert_finite_integral_between_abscissas_is(&distribution, 2.0, 1.0, 0.0); + assert_finite_integral_between_abscissas_is(&distribution, 1.0, 2.0, 0.384); } QUIZ_CASE(fisher_distribution) { @@ -115,4 +146,10 @@ QUIZ_CASE(fisher_distribution) { assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.4, 0.94560850441205857); assert_cumulative_distributive_function_direct_and_inverse_is(&distribution, 1.425, 0.95425004959692871775); + // Fisher distribution with d1 = 4 and d2 = 2 + distribution.setParameterAtIndex(4.0, 0); + distribution.setParameterAtIndex(2.0, 1); + assert_finite_integral_between_abscissas_is(&distribution, 1.0, 1.0, 0.0); + assert_finite_integral_between_abscissas_is(&distribution, 2.0, 1.0, 0.0); + assert_finite_integral_between_abscissas_is(&distribution, 1.0, 2.0, 0.19555555555555555); } diff --git a/apps/regression/app.cpp b/apps/regression/app.cpp index 4760b7841..53ecf1481 100644 --- a/apps/regression/app.cpp +++ b/apps/regression/app.cpp @@ -38,7 +38,7 @@ App * App::Snapshot::unpack(Container * container) { } void App::Snapshot::reset() { - m_store.deleteAllPairs(); + m_store.reset(); m_modelVersion = 0; m_rangeVersion = 0; setActiveTab(0); @@ -59,7 +59,7 @@ App::App(Snapshot * snapshot, Poincare::Context * parentContext) : m_calculationController(&m_calculationAlternateEmptyViewController, &m_calculationHeader, snapshot->store()), m_calculationAlternateEmptyViewController(&m_calculationHeader, &m_calculationController, &m_calculationController), m_calculationHeader(&m_tabViewController, &m_calculationAlternateEmptyViewController, &m_calculationController), - m_graphController(&m_graphAlternateEmptyViewController, this, &m_graphHeader, snapshot->store(), snapshot->cursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->graphSelectedDotIndex(), snapshot->selectedSeriesIndex()), + m_graphController(&m_graphAlternateEmptyViewController, this, &m_graphHeader, snapshot->store(), snapshot->cursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->graphSelectedDotIndex(), snapshot->selectedSeriesIndex()), m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController), m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController), m_graphStackViewController(&m_tabViewController, &m_graphHeader), diff --git a/apps/regression/app.h b/apps/regression/app.h index 0b0ec5d1d..b53a16868 100644 --- a/apps/regression/app.h +++ b/apps/regression/app.h @@ -31,6 +31,7 @@ public: int * graphSelectedDotIndex() { return &m_graphSelectedDotIndex; } int * selectedSeriesIndex() { return &m_selectedSeriesIndex; } uint32_t * modelVersion() { return &m_modelVersion; } + uint32_t * previousModelsVersions() { return m_store.seriesChecksum(); } uint32_t * rangeVersion() { return &m_rangeVersion; } private: void tidy() override; diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 78f8ea350..b6b54aaf1 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -14,8 +14,8 @@ static inline int maxInt(int x, int y) { return x > y ? x : y; } namespace Regression { -GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex) : - InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, store, &m_view, cursor, modelVersion, rangeVersion), +GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex) : + InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, store, &m_view, cursor, modelVersion, previousModelsVersions, rangeVersion), m_crossCursorView(), m_roundCursorView(), m_bannerView(this, inputEventHandlerDelegate, this), @@ -84,18 +84,7 @@ void GraphController::viewWillAppear() { /* Since *m_selectedDotIndex is altered by initCursorParameters(), * the following must absolutely come at the end. */ - if (*m_selectedDotIndex >= 0) { - setRoundCrossCursorView(false); - } else { - setRoundCrossCursorView(true); - m_roundCursorView.setColor(Palette::DataColor[*m_selectedSeriesIndex]); - } -} - -void GraphController::selectRegressionCurve() { - *m_selectedDotIndex = -1; - setRoundCrossCursorView(true); - m_roundCursorView.setColor(Palette::DataColor[*m_selectedSeriesIndex]); + setRoundCrossCursorView(); } // Private @@ -324,19 +313,22 @@ bool GraphController::moveCursorVertically(int direction) { assert(!validDot || !validRegression); + /* The model should be up to date before setting the cursor view. */ + if (validRegression) { // Select the regression *m_selectedSeriesIndex = closestRegressionSeries; - selectRegressionCurve(); + *m_selectedDotIndex = -1; + setRoundCrossCursorView(); m_cursor->moveTo(x, x, yValue(*m_selectedSeriesIndex, x, context)); return true; } if (validDot) { // Select the dot - setRoundCrossCursorView(false); *m_selectedSeriesIndex = closestDotSeries; *m_selectedDotIndex = dotSelected; + setRoundCrossCursorView(); if (dotSelected == m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex)) { // Select the mean dot double x = m_store->meanOfColumn(*m_selectedSeriesIndex, 0); @@ -359,6 +351,11 @@ uint32_t GraphController::modelVersion() { return m_store->storeChecksum(); } +uint32_t GraphController::modelVersionAtIndex(size_t i) { + assert(i < numberOfMemoizedVersions()); + return *(m_store->seriesChecksum() + i); +} + uint32_t GraphController::rangeVersion() { return m_store->rangeChecksum(); } @@ -404,8 +401,16 @@ InteractiveCurveViewRangeDelegate::Range GraphController::computeYRange(Interact return range; } -void GraphController::setRoundCrossCursorView(bool round) { +void GraphController::setRoundCrossCursorView() { + /* At this point, the model (selected series and dot indices) should be up + * to date. */ + bool round = *m_selectedDotIndex < 0; + if (round) { + // Set the color although the cursor view stays round + m_roundCursorView.setColor(Palette::DataColor[*m_selectedSeriesIndex]); + } CursorView * nextCursorView = round ? static_cast(&m_roundCursorView) : static_cast(&m_crossCursorView); + // Escape if the cursor view stays the same if (m_view.cursorView() == nextCursorView) { return; } diff --git a/apps/regression/graph_controller.h b/apps/regression/graph_controller.h index 0c35ad9ab..30e722552 100644 --- a/apps/regression/graph_controller.h +++ b/apps/regression/graph_controller.h @@ -16,12 +16,12 @@ namespace Regression { class GraphController : public Shared::InteractiveCurveViewController { public: - GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex); + GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, Store * store, Shared::CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, int * selectedDotIndex, int * selectedSeriesIndex); ViewController * initialisationParameterController() override; bool isEmpty() const override; I18n::Message emptyMessage() override; void viewWillAppear() override; - void selectRegressionCurve(); + void selectRegressionCurve() { *m_selectedDotIndex = -1; } int selectedSeriesIndex() const { return *m_selectedSeriesIndex; } // moveCursorHorizontally and Vertically are public to be used in tests @@ -43,7 +43,9 @@ private: // InteractiveCurveViewController void initCursorParameters() override; uint32_t modelVersion() override; + uint32_t modelVersionAtIndex(size_t i) override; uint32_t rangeVersion() override; + size_t numberOfMemoizedVersions() const override { return Store::k_numberOfSeries; } int selectedCurveIndex() const override { return *m_selectedSeriesIndex; } bool closestCurveIndexIsSuitable(int newIndex, int currentIndex) const override; Poincare::Coordinate2D xyValues(int curveIndex, double x, Poincare::Context * context) const override; @@ -55,7 +57,7 @@ private: // InteractiveCurveViewRangeDelegate Shared::InteractiveCurveViewRangeDelegate::Range computeYRange(Shared::InteractiveCurveViewRange * interactiveCurveViewRange) override; - void setRoundCrossCursorView(bool round); + void setRoundCrossCursorView(); Shared::CursorView m_crossCursorView; Shared::RoundCursorView m_roundCursorView; BannerView m_bannerView; diff --git a/apps/regression/graph_view.cpp b/apps/regression/graph_view.cpp index f5883cb16..6a651bc22 100644 --- a/apps/regression/graph_view.cpp +++ b/apps/regression/graph_view.cpp @@ -9,10 +9,8 @@ using namespace Shared; namespace Regression { GraphView::GraphView(Store * store, CurveViewCursor * cursor, BannerView * bannerView, Shared::CursorView * cursorView) : - CurveView(store, cursor, bannerView, cursorView), - m_store(store), - m_xLabels{}, - m_yLabels{} + LabeledCurveView(store, cursor, bannerView, cursorView), + m_store(store) { } @@ -41,13 +39,4 @@ void GraphView::drawRect(KDContext * ctx, KDRect rect) const { } } -char * GraphView::label(Axis axis, int index) const { - if (axis == Axis::Vertical) { - assert(index < k_maxNumberOfXLabels); - return (char *)m_yLabels[index]; - } - assert(index < k_maxNumberOfYLabels); - return (char *)m_xLabels[index]; -} - } diff --git a/apps/regression/graph_view.h b/apps/regression/graph_view.h index 17269ad1c..22db0b635 100644 --- a/apps/regression/graph_view.h +++ b/apps/regression/graph_view.h @@ -3,19 +3,16 @@ #include "store.h" #include "../constant.h" -#include "../shared/curve_view.h" +#include "../shared/labeled_curve_view.h" namespace Regression { -class GraphView : public Shared::CurveView { +class GraphView : public Shared::LabeledCurveView { public: GraphView(Store * store, Shared::CurveViewCursor * cursor, Shared::BannerView * bannerView, Shared::CursorView * cursorView); void drawRect(KDContext * ctx, KDRect rect) const override; private: - char * label(Axis axis, int index) const override; Store * m_store; - char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; - char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize]; }; } diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 0afd7941a..98f703bd5 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -19,13 +19,14 @@ static_assert(Store::k_numberOfSeries == 3, "Number of series changed, Regressio Store::Store() : InteractiveCurveViewRange(), DoublePairStore(), - m_seriesChecksum{0, 0, 0}, m_angleUnit(Poincare::Preferences::AngleUnit::Degree) { - for (int i = 0; i < k_numberOfSeries; i++) { - m_regressionTypes[i] = Model::Type::Linear; - m_regressionChanged[i] = false; - } + resetMemoization(); +} + +void Store::reset() { + deleteAllPairs(); + resetMemoization(); } void Store::tidy() { @@ -188,6 +189,13 @@ double Store::doubleCastedNumberOfPairsOfSeries(int series) const { return DoublePairStore::numberOfPairsOfSeries(series); } +void Store::resetMemoization() { + assert(((int)Model::Type::Linear) == 0); + memset(m_seriesChecksum, 0, sizeof(m_seriesChecksum)); + memset(m_regressionTypes, 0, sizeof(m_regressionTypes)); + memset(m_regressionChanged, 0, sizeof(m_regressionChanged)); +} + float Store::maxValueOfColumn(int series, int i) const { float maxColumn = -FLT_MAX; for (int k = 0; k < numberOfPairsOfSeries(series); k++) { diff --git a/apps/regression/store.h b/apps/regression/store.h index dfdc682c1..2cc6f600f 100644 --- a/apps/regression/store.h +++ b/apps/regression/store.h @@ -22,6 +22,7 @@ class Store : public Shared::InteractiveCurveViewRange, public Shared::DoublePai public: Store(); + void reset(); // Clean pool void tidy(); @@ -35,6 +36,7 @@ public: assert((int)m_regressionTypes[series] >= 0 && (int)m_regressionTypes[series] < Model::k_numberOfModels); return regressionModel((int)m_regressionTypes[series]); } + uint32_t * seriesChecksum() { return m_seriesChecksum; } // Dots /* Return the closest dot to abscissa x above the regression curve if @@ -70,6 +72,7 @@ public: double squaredCorrelationCoefficient(int series) const; private: constexpr static float k_displayHorizontalMarginRatio = 0.05f; + void resetMemoization(); float maxValueOfColumn(int series, int i) const; //TODO LEA why float ? float minValueOfColumn(int series, int i) const; //TODO LEA why float ? Model * regressionModel(int index); diff --git a/apps/sequence/app.cpp b/apps/sequence/app.cpp index a98e3daac..fc68fad5d 100644 --- a/apps/sequence/app.cpp +++ b/apps/sequence/app.cpp @@ -55,7 +55,7 @@ App::App(Snapshot * snapshot) : m_listFooter(&m_listHeader, &m_listController, &m_listController, ButtonRowController::Position::Bottom, ButtonRowController::Style::EmbossedGrey), m_listHeader(nullptr, &m_listFooter, &m_listController), m_listStackViewController(&m_tabViewController, &m_listHeader), - m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->functionStore(), snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader), + m_graphController(&m_graphAlternateEmptyViewController, this, snapshot->functionStore(), snapshot->graphRange(), snapshot->cursor(), snapshot->indexFunctionSelectedByCursor(), snapshot->modelVersion(), snapshot->previousModelsVersions(), snapshot->rangeVersion(), snapshot->angleUnitVersion(), &m_graphHeader), m_graphAlternateEmptyViewController(&m_graphHeader, &m_graphController, &m_graphController), m_graphHeader(&m_graphStackViewController, &m_graphAlternateEmptyViewController, &m_graphController), m_graphStackViewController(&m_tabViewController, &m_graphHeader), diff --git a/apps/sequence/graph/graph_controller.cpp b/apps/sequence/graph/graph_controller.cpp index c2c4b8fea..8e14c64c8 100644 --- a/apps/sequence/graph/graph_controller.cpp +++ b/apps/sequence/graph/graph_controller.cpp @@ -11,8 +11,8 @@ namespace Sequence { static inline int minInt(int x, int y) { return (x < y ? x : y); } static inline int maxInt(int x, int y) { return (x > y ? x : y); } -GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) : - FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, graphRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion), +GraphController::GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) : + FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, graphRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, previousModelsVersions, rangeVersion, angleUnitVersion), m_bannerView(this, inputEventHandlerDelegate, this), m_view(sequenceStore, graphRange, m_cursor, &m_bannerView, &m_cursorView), m_graphRange(graphRange), diff --git a/apps/sequence/graph/graph_controller.h b/apps/sequence/graph/graph_controller.h index da0e64006..2177bc230 100644 --- a/apps/sequence/graph/graph_controller.h +++ b/apps/sequence/graph/graph_controller.h @@ -14,7 +14,7 @@ namespace Sequence { class GraphController final : public Shared::FunctionGraphController { public: - GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header); + GraphController(Responder * parentResponder, ::InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header); I18n::Message emptyMessage() override; void viewWillAppear() override; TermSumController * termSumController() { return &m_termSumController; } diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index e97f8d390..64643ace1 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -28,7 +28,6 @@ void Sequence::tidy() { m_firstInitialCondition.tidyName(); m_secondInitialCondition.tidy(); m_secondInitialCondition.tidyName(); - m_nameLayout = Layout(); } Sequence::Type Sequence::type() const { @@ -81,13 +80,10 @@ void Sequence::setInitialRank(int rank) { } Poincare::Layout Sequence::nameLayout() { - if (m_nameLayout.isUninitialized()) { - m_nameLayout = HorizontalLayout::Builder( - CodePointLayout::Builder(fullName()[0], KDFont::SmallFont), - VerticalOffsetLayout::Builder(CodePointLayout::Builder(symbol(), KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Subscript) - ); - } - return m_nameLayout; + return HorizontalLayout::Builder( + CodePointLayout::Builder(fullName()[0], KDFont::SmallFont), + VerticalOffsetLayout::Builder(CodePointLayout::Builder(symbol(), KDFont::SmallFont), VerticalOffsetLayoutNode::Position::Subscript) + ); } bool Sequence::isDefined() { diff --git a/apps/sequence/sequence.h b/apps/sequence/sequence.h index e7901de68..d8e2a5f6d 100644 --- a/apps/sequence/sequence.h +++ b/apps/sequence/sequence.h @@ -24,8 +24,9 @@ public: DoubleRecurrence = 2 }; Sequence(Ion::Storage::Record record = Record()) : - Function(record), - m_nameLayout() {} + Function(record) + { + } I18n::Message parameterMessageName() const override; CodePoint symbol() const override { return 'n'; } void tidy() override; @@ -111,7 +112,7 @@ private: public: SequenceModel() : Shared::ExpressionModel(), m_name() {} void tidyName() { m_name = Poincare::Layout(); } - virtual Poincare::Layout name(Sequence * sequence); + Poincare::Layout name(Sequence * sequence); protected: virtual void buildName(Sequence * sequence) = 0; Poincare::Layout m_name; @@ -153,7 +154,6 @@ private: DefinitionModel m_definition; FirstInitialConditionModel m_firstInitialCondition; SecondInitialConditionModel m_secondInitialCondition; - Poincare::Layout m_nameLayout; }; } diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index 7be14ce0e..bc10bd120 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -7,7 +7,7 @@ EditionLinear = "Linear " Edition2D = "Natürlich " ComplexFormat = "Komplex" ExamMode = "Testmodus" -ExamModeActive = "Wieder starten Testmodus" +ExamModeActive = "Testmodus neustarten" ToDeactivateExamMode1 = "Um den Testmodus auszuschalten," ToDeactivateExamMode2 = "schließen Sie den Rechner an einen" ToDeactivateExamMode3 = "Computer oder eine Steckdose an." @@ -36,28 +36,15 @@ SoftwareVersion = "Epsilon version" CustomSoftwareVersion = "Omega version" Username = "Name" MicroPythonVersion = "µPythonversion" -ResultDisplay = "Resultaatweergave" -DefaultResult = "Standaard " +ResultDisplay = "Ergebniswiedergabe" +DefaultResult = "Standard " CompactResult = "Compact " FontSizes = "Python Schriftgröße" -LargeFont = "Große " -SmallFont = "Kleine " +LargeFont = "Groß " +SmallFont = "Klein " SerialNumber = "Seriennummer" UpdatePopUp = "Erinnerung: Update" BetaPopUp = "Beta pop-up" -LEDColor = "LEDs farbe" -ExamModeMode = "Modus" -ExamModeModeStandard = "Standard " -ExamModeModeNoSym = "Ohne symbolisch " -ExamModeModeNoSymNoText = "No sym no text " -ExamModeModeDutch = "Niederländisch " -ColorRed = "Rot " -ColorWhite = "Weiss " -ColorBlue = "Blau " -ColorGreen = "Grün " -ColorYellow = "Gelb " -ColorPurple = "Lila " -ColorOrange = "Orange " Contributors = "Beiträger" Accessibility = "Barrierefreiheit" AccessibilityInvertColors = "Farbumkehrung" @@ -66,13 +53,13 @@ AccessibilityGamma = "Gammakorrektur" AccessibilityGammaRed = "Rotes Gamma" AccessibilityGammaGreen = "Grünes Gamma" AccessibilityGammaBlue = "Blaues Gamma" -MathOptions = "Mathe-optionen" +MathOptions = "Berechnungseinstellungen" SymbolMultiplication = "Multiplikation" SymbolMultiplicationCross = "Kreuz " SymbolMultiplicationMiddleDot = "Mittelpunkt " SymbolMultiplicationStar = "Stern " SymbolMultiplicationAutoSymbol = "automatisch " -PythonFont = "Python schriftart" +PythonFont = "Python Schriftart" Large = "Groß " Small = "Klein " MemUse = "Speicher" diff --git a/apps/settings/base.en.i18n b/apps/settings/base.en.i18n index 9fb8c3617..e09b048fa 100644 --- a/apps/settings/base.en.i18n +++ b/apps/settings/base.en.i18n @@ -45,19 +45,6 @@ SmallFont = "Small " SerialNumber = "Serial number" UpdatePopUp = "Update pop-up" BetaPopUp = "Beta pop-up" -LEDColor = "LED color" -ExamModeMode = "Mode" -ExamModeModeStandard = "Standard " -ExamModeModeNoSym = "No sym " -ExamModeModeNoSymNoText = "No sym no text " -ExamModeModeDutch = "Dutch " -ColorRed = "Red " -ColorWhite = "White " -ColorBlue = "Blue " -ColorGreen = "Green " -ColorYellow = "Yellow " -ColorPurple = "Purple " -ColorOrange = "Orange " Contributors = "Contributors" Accessibility = "Accessibility" AccessibilityInvertColors = "Invert colors" diff --git a/apps/settings/base.es.i18n b/apps/settings/base.es.i18n index b476d7378..c8d4a88a1 100644 --- a/apps/settings/base.es.i18n +++ b/apps/settings/base.es.i18n @@ -45,19 +45,6 @@ SmallFont = "Pequeño " SerialNumber = "Número serie" UpdatePopUp = "Pop-up de actualización" BetaPopUp = "Beta pop-up" -LEDColor = "Color del LED" -ExamModeMode = "Modo" -ExamModeModeStandard = "Estándar " -ExamModeModeNoSym = "Sin simbólico " -ExamModeModeNoSymNoText = "sin simbolismo sin texto " -ExamModeModeDutch = "Holandés " -ColorRed = "Rojo " -ColorWhite = "Blanco " -ColorBlue = "Azul " -ColorGreen = "Verde " -ColorYellow = "Amarillo " -ColorPurple = "Púrpura " -ColorOrange = "Naranja " Contributors = "Contribuyentes" Accessibility = "Accesibilidad" AccessibilityInvertColors = "Colores invertidos" diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index 4e413d057..a23f8a0b9 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -45,19 +45,6 @@ SmallFont = "Petit " SerialNumber = "Numéro série" UpdatePopUp = "Rappel mise à jour" BetaPopUp = "Rappel version bêta" -LEDColor = "Couleur LED" -ExamModeMode = "Mode" -ExamModeModeStandard = "Standard " -ExamModeModeNoSym = "Sans symbolique " -ExamModeModeNoSymNoText = "Sans symbolique ni texte " -ExamModeModeDutch = "Néerlandais " -ColorRed = "Rouge " -ColorWhite = "Blanc " -ColorBlue = "Bleu " -ColorGreen = "Vert " -ColorYellow = "Jaune " -ColorPurple = "Mauve " -ColorOrange = "Orange " Contributors = "Contributeurs" Accessibility = "Accessibilité" AccessibilityInvertColors = "Inverser couleurs" diff --git a/apps/settings/base.hu.i18n b/apps/settings/base.hu.i18n index 6361dbec8..b3828759e 100644 --- a/apps/settings/base.hu.i18n +++ b/apps/settings/base.hu.i18n @@ -45,19 +45,6 @@ SmallFont = "Kicsi " SerialNumber = "Sorozatszám" UpdatePopUp = "Elöugró ablak frissítése" BetaPopUp = "Béta pop-up" -LEDColor = "LED szín" -ExamModeMode = "Üzemmód" -ExamModeModeStandard = "Normál" -ExamModeModeNoSym = "Nincs sym" -ExamModeModeNoSymNoText = "Nincs szimbolikus, nincs szöveg " -ExamModeModeDutch = "Holland " -ColorRed = "Piros " -ColorWhite = "Fehér " -ColorBlue = "Kék " -ColorGreen = "Zöld " -ColorYellow = "Sárga " -ColorPurple = "Lila " -ColorOrange = "Narancssárga " Contributors = "Közremüködök" Accessibility = "Hozzáférhetöség" AccessibilityInvertColors = "Invertált színek" diff --git a/apps/settings/base.pt.i18n b/apps/settings/base.pt.i18n index 0e5218695..c3fb57e31 100644 --- a/apps/settings/base.pt.i18n +++ b/apps/settings/base.pt.i18n @@ -45,19 +45,6 @@ SmallFont = "Pequeno " SerialNumber = "Número serie" UpdatePopUp = "Alertas de atualização" BetaPopUp = "Beta pop-up" -LEDColor = "Cor LED" -ExamModeMode = "Modo" -ExamModeModeStandard = "Padrão " -ExamModeModeNoSym = "Sem simbólico " -ExamModeModeNoSymNoText = "Sem simbólico sem texto " -ExamModeModeDutch = "Holandês " -ColorRed = "Vermelho " -ColorWhite = "Branco " -ColorBlue = "Azul " -ColorGreen = "Verde " -ColorYellow = "Amarelo " -ColorPurple = "Roxo " -ColorOrange = "Caranja " Contributors = "Contribuidores" Accessibility = "Acessibilidade" AccessibilityInvertColors = "Cores invertidas" diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 12a529e80..353bbb86f 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -82,3 +82,16 @@ XStart = "X Startwert" Zoom = "Zoom" Developers = "Entwickler" BetaTesters = "Beta-Tester" +LEDColor = "LED Farbe" +ExamModeMode = "Modus" +ExamModeModeStandard = "Standard " +ExamModeModeNoSym = "Ohne Symbole " +ExamModeModeNoSymNoText = "Ohne Symbole & Text " +ExamModeModeDutch = "Niederländisch " +ColorRed = "Rot " +ColorWhite = "Weiss " +ColorBlue = "Blau " +ColorGreen = "Grün " +ColorYellow = "Gelb " +ColorPurple = "Lila " +ColorOrange = "Orange " diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index 5e62daa44..bbef03c83 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -82,3 +82,16 @@ XStart = "X start" Zoom = "Zoom" Developers = "Developers" BetaTesters = "Beta testers" +LEDColor = "LED color" +ExamModeMode = "Mode" +ExamModeModeStandard = "Standard " +ExamModeModeNoSym = "No sym " +ExamModeModeNoSymNoText = "No sym no text " +ExamModeModeDutch = "Dutch " +ColorRed = "Red " +ColorWhite = "White " +ColorBlue = "Blue " +ColorGreen = "Green " +ColorYellow = "Yellow " +ColorPurple = "Purple " +ColorOrange = "Orange " diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index ff7a855bd..2ac9ff491 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -82,3 +82,16 @@ XStart = "X inicio" Zoom = "Zoom" Developers = "Desarrolladores" BetaTesters = "Probadores beta" +LEDColor = "Color del LED" +ExamModeMode = "Modo" +ExamModeModeStandard = "Estándar " +ExamModeModeNoSym = "Sin simbólico " +ExamModeModeNoSymNoText = "sin simbolismo sin texto " +ExamModeModeDutch = "Holandés " +ColorRed = "Rojo " +ColorWhite = "Blanco " +ColorBlue = "Azul " +ColorGreen = "Verde " +ColorYellow = "Amarillo " +ColorPurple = "Púrpura " +ColorOrange = "Naranja " diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index 821b9c26c..f711cbbec 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -82,3 +82,16 @@ XStart = "X début" Zoom = "Zoom" Developers = "Développeurs" BetaTesters = "Beta testeurs" +LEDColor = "Couleur LED" +ExamModeMode = "Mode" +ExamModeModeStandard = "Standard " +ExamModeModeNoSym = "Sans symbolique " +ExamModeModeNoSymNoText = "Sans symbolique ni texte " +ExamModeModeDutch = "Néerlandais " +ColorRed = "Rouge " +ColorWhite = "Blanc " +ColorBlue = "Bleu " +ColorGreen = "Vert " +ColorYellow = "Jaune " +ColorPurple = "Mauve " +ColorOrange = "Orange " diff --git a/apps/shared.hu.i18n b/apps/shared.hu.i18n index b44acba08..4862d64f7 100644 --- a/apps/shared.hu.i18n +++ b/apps/shared.hu.i18n @@ -82,3 +82,16 @@ XStart = "X kezdete" Zoom = "Zoom" Developers = "Fejlesztök" BetaTesters = "Béta tesztelök" +LEDColor = "LED szín" +ExamModeMode = "Üzemmód" +ExamModeModeStandard = "Normál" +ExamModeModeNoSym = "Nincs sym" +ExamModeModeNoSymNoText = "Nincs szimbolikus, nincs szöveg " +ExamModeModeDutch = "Holland " +ColorRed = "Piros " +ColorWhite = "Fehér " +ColorBlue = "Kék " +ColorGreen = "Zöld " +ColorYellow = "Sárga " +ColorPurple = "Lila " +ColorOrange = "Narancssárga " diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index 2dc5f6e5f..7ffa4a8b0 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -82,3 +82,16 @@ XStart = "X inicio" Zoom = "Zoom" Developers = "Desenvolvedores" BetaTesters = "Testadores beta" +LEDColor = "Cor LED" +ExamModeMode = "Modo" +ExamModeModeStandard = "Padrão " +ExamModeModeNoSym = "Sem simbólico " +ExamModeModeNoSymNoText = "Sem simbólico sem texto " +ExamModeModeDutch = "Holandês " +ColorRed = "Vermelho " +ColorWhite = "Branco " +ColorBlue = "Azul " +ColorGreen = "Verde " +ColorYellow = "Amarelo " +ColorPurple = "Roxo " +ColorOrange = "Caranja " diff --git a/apps/shared/Makefile b/apps/shared/Makefile index e97b68ed0..98e4a8fe6 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -45,6 +45,7 @@ app_shared_src = $(addprefix apps/shared/,\ interactive_curve_view_controller.cpp \ interval.cpp \ interval_parameter_controller.cpp \ + labeled_curve_view.cpp \ language_controller.cpp \ layout_field_delegate.cpp \ list_parameter_controller.cpp \ diff --git a/apps/shared/cursor_view.h b/apps/shared/cursor_view.h index 4590950b2..830473c24 100644 --- a/apps/shared/cursor_view.h +++ b/apps/shared/cursor_view.h @@ -1,11 +1,12 @@ #ifndef SHARED_CURSOR_VIEW_H #define SHARED_CURSOR_VIEW_H -#include +#include +#include namespace Shared { -class CursorView : public View { +class CursorView : public TransparentView { public: virtual void setCursorFrame(KDRect frame, bool force) { View::setFrame(frame, force); } void drawRect(KDContext * ctx, KDRect rect) const override; diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index 3a549ae06..aedbd140a 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -106,11 +106,11 @@ void CurveView::setOkView(View * okView) { * m_frame.height() - 1 yMin() */ -const float CurveView::pixelWidth() const { +float CurveView::pixelWidth() const { return (m_curveViewRange->xMax() - m_curveViewRange->xMin()) / (m_frame.width() - 1); } -const float CurveView::pixelHeight() const { +float CurveView::pixelHeight() const { return (m_curveViewRange->yMax() - m_curveViewRange->yMin()) / (m_frame.height() - 1); } diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index 433fce06f..a9ea741ab 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -38,8 +38,8 @@ public: void setBannerView(View * bannerView); void setOkView(View * okView); void setForceOkDisplay(bool force) { m_forceOkDisplay = force; } - const float pixelWidth() const; - const float pixelHeight() const; + float pixelWidth() const; + float pixelHeight() const; protected: CurveViewRange * curveViewRange() const { return m_curveViewRange; } void setCurveViewRange(CurveViewRange * curveViewRange); @@ -100,7 +100,7 @@ private: float min(Axis axis) const; float max(Axis axis) const; float gridUnit(Axis axis) const; - virtual char * label(Axis axis, int index) const = 0; + virtual char * label(Axis axis, int index) const { return nullptr; } virtual size_t labelMaxGlyphLengthSize() const { return k_labelBufferMaxGlyphLength; } int numberOfLabels(Axis axis) const; /* Recursively join two dots (dichotomy). The method stops when the diff --git a/apps/shared/curve_view_range.h b/apps/shared/curve_view_range.h index 8cda5fdfe..caa1d9ab0 100644 --- a/apps/shared/curve_view_range.h +++ b/apps/shared/curve_view_range.h @@ -17,8 +17,8 @@ public: virtual float xMax() const = 0; virtual float yMin() const = 0; virtual float yMax() const = 0; - const float xCenter() const { return (xMin() + xMax()) / 2; } - const float yCenter() const { return (yMin() + yMax()) / 2; } + float xCenter() const { return (xMin() + xMax()) / 2; } + float yCenter() const { return (yMin() + yMax()) / 2; } virtual float xGridUnit() const { return computeGridUnit(k_minNumberOfXGridUnits, k_maxNumberOfXGridUnits, xMax() - xMin()); } diff --git a/apps/shared/function_app.cpp b/apps/shared/function_app.cpp index 7cede0ee8..0b7a95454 100644 --- a/apps/shared/function_app.cpp +++ b/apps/shared/function_app.cpp @@ -11,11 +11,14 @@ FunctionApp::Snapshot::Snapshot() : m_rangeVersion(0), m_angleUnitVersion(Preferences::AngleUnit::Radian) { + assert(m_previousModelsVersions[0] == 0); } void FunctionApp::Snapshot::reset() { m_indexFunctionSelectedByCursor = 0; m_modelVersion = 0; + assert(sizeof(m_previousModelsVersions) == sizeof(uint32_t) * FunctionGraphController::sNumberOfMemoizedModelVersions); + memset(m_previousModelsVersions, 0, sizeof(m_previousModelsVersions)); m_rangeVersion = 0; setActiveTab(0); } diff --git a/apps/shared/function_app.h b/apps/shared/function_app.h index ffec00c31..d15e4c5e8 100644 --- a/apps/shared/function_app.h +++ b/apps/shared/function_app.h @@ -2,6 +2,7 @@ #define SHARED_FUNCTION_APP_H #include "expression_field_delegate_app.h" +#include "function_graph_controller.h" #include "function_store.h" #include "curve_view_cursor.h" #include "values_controller.h" @@ -15,6 +16,7 @@ public: Snapshot(); CurveViewCursor * cursor() { return &m_cursor; } uint32_t * modelVersion() { return &m_modelVersion; } + uint32_t * previousModelsVersions() { return m_previousModelsVersions; } uint32_t * rangeVersion() { return &m_rangeVersion; } Poincare::Preferences::AngleUnit * angleUnitVersion() { return &m_angleUnitVersion; } virtual FunctionStore * functionStore() = 0; @@ -26,6 +28,7 @@ public: private: int m_indexFunctionSelectedByCursor; uint32_t m_modelVersion; + uint32_t m_previousModelsVersions[FunctionGraphController::sNumberOfMemoizedModelVersions]; uint32_t m_rangeVersion; Poincare::Preferences::AngleUnit m_angleUnitVersion; }; diff --git a/apps/shared/function_graph_controller.cpp b/apps/shared/function_graph_controller.cpp index cf70f677d..f851f65ef 100644 --- a/apps/shared/function_graph_controller.cpp +++ b/apps/shared/function_graph_controller.cpp @@ -15,8 +15,8 @@ static inline float maxFloat(float x, float y) { return x > y ? x : y; } static inline double minDouble(double x, double y) { return x < y ? x : y; } static inline double maxDouble(double x, double y) { return x > y ? x : y; } -FunctionGraphController::FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion) : - InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, interactiveRange, curveView, cursor, modelVersion, rangeVersion), +FunctionGraphController::FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion) : + InteractiveCurveViewController(parentResponder, inputEventHandlerDelegate, header, interactiveRange, curveView, cursor, modelVersion, previousModelsVersions, rangeVersion), m_initialisationParameterController(this, interactiveRange), m_angleUnitVersion(angleUnitVersion), m_indexFunctionSelectedByCursor(indexFunctionSelectedByCursor) @@ -183,6 +183,10 @@ uint32_t FunctionGraphController::modelVersion() { return functionStore()->storeChecksum(); } +uint32_t FunctionGraphController::modelVersionAtIndex(size_t i) { + return functionStore()->storeChecksumAtIndex(i); +} + uint32_t FunctionGraphController::rangeVersion() { return interactiveCurveViewRange()->rangeChecksum(); } diff --git a/apps/shared/function_graph_controller.h b/apps/shared/function_graph_controller.h index e111c4234..fcb15ae57 100644 --- a/apps/shared/function_graph_controller.h +++ b/apps/shared/function_graph_controller.h @@ -13,7 +13,8 @@ namespace Shared { class FunctionGraphController : public InteractiveCurveViewController, public FunctionBannerDelegate { public: - FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion); + static constexpr size_t sNumberOfMemoizedModelVersions = 5; + FunctionGraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion); bool isEmpty() const override; ViewController * initialisationParameterController() override; void didBecomeFirstResponder() override; @@ -49,7 +50,9 @@ private: // InteractiveCurveViewController bool moveCursorVertically(int direction) override; uint32_t modelVersion() override; + uint32_t modelVersionAtIndex(size_t i) override; uint32_t rangeVersion() override; + size_t numberOfMemoizedVersions() const override { return sNumberOfMemoizedModelVersions; } InitialisationParameterController m_initialisationParameterController; Poincare::Preferences::AngleUnit * m_angleUnitVersion; diff --git a/apps/shared/function_graph_view.cpp b/apps/shared/function_graph_view.cpp index 552cf1be1..36e435a3a 100644 --- a/apps/shared/function_graph_view.cpp +++ b/apps/shared/function_graph_view.cpp @@ -8,13 +8,11 @@ namespace Shared { FunctionGraphView::FunctionGraphView(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, BannerView * bannerView, CursorView * cursorView) : - CurveView(graphRange, cursor, bannerView, cursorView), + LabeledCurveView(graphRange, cursor, bannerView, cursorView), m_selectedRecord(), m_highlightedStart(NAN), m_highlightedEnd(NAN), m_shouldColorHighlighted(false), - m_xLabels{}, - m_yLabels{}, m_context(nullptr) { } @@ -67,10 +65,6 @@ void FunctionGraphView::setAreaHighlightColor(bool highlightColor) { } } -char * FunctionGraphView::label(Axis axis, int index) const { - return (axis == Axis::Horizontal ? (char *)m_xLabels[index] : (char *)m_yLabels[index]); -} - void FunctionGraphView::reloadBetweenBounds(float start, float end) { if (start == end) { return; diff --git a/apps/shared/function_graph_view.h b/apps/shared/function_graph_view.h index 0ec2f11cf..4e3f0a55a 100644 --- a/apps/shared/function_graph_view.h +++ b/apps/shared/function_graph_view.h @@ -2,14 +2,14 @@ #define SHARED_FUNCTION_GRAPH_VIEW_H #include -#include "curve_view.h" +#include "labeled_curve_view.h" #include "function.h" #include "../constant.h" #include "interactive_curve_view_range.h" namespace Shared { -class FunctionGraphView : public CurveView { +class FunctionGraphView : public LabeledCurveView { public: FunctionGraphView(InteractiveCurveViewRange * graphRange, CurveViewCursor * cursor, BannerView * bannerView, CursorView * cursorView); @@ -26,9 +26,6 @@ protected: float m_highlightedEnd; bool m_shouldColorHighlighted; private: - char * label(Axis axis, int index) const override; - char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; - char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize]; Poincare::Context * m_context; }; diff --git a/apps/shared/function_store.cpp b/apps/shared/function_store.cpp index 77ed25132..7a05715a2 100644 --- a/apps/shared/function_store.cpp +++ b/apps/shared/function_store.cpp @@ -6,4 +6,11 @@ uint32_t FunctionStore::storeChecksum() { return Ion::Storage::sharedStorage()->checksum(); } +uint32_t FunctionStore::storeChecksumAtIndex(size_t i) { + if (numberOfActiveFunctions() <= i) { + return 0; + } + return activeRecordAtIndex(i).checksum(); +} + } diff --git a/apps/shared/function_store.h b/apps/shared/function_store.h index 2790f6703..03e57ef58 100644 --- a/apps/shared/function_store.h +++ b/apps/shared/function_store.h @@ -13,6 +13,7 @@ class FunctionStore : public ExpressionModelStore { public: FunctionStore() : ExpressionModelStore() {} uint32_t storeChecksum(); + uint32_t storeChecksumAtIndex(size_t i); int numberOfActiveFunctions() const { return numberOfModelsSatisfyingTest(&isFunctionActive, nullptr); } diff --git a/apps/shared/interactive_curve_view_controller.cpp b/apps/shared/interactive_curve_view_controller.cpp index f1bc67391..997149852 100644 --- a/apps/shared/interactive_curve_view_controller.cpp +++ b/apps/shared/interactive_curve_view_controller.cpp @@ -7,10 +7,11 @@ using namespace Poincare; namespace Shared { -InteractiveCurveViewController::InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion) : +InteractiveCurveViewController::InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion) : SimpleInteractiveCurveViewController(parentResponder, cursor), ButtonRowDelegate(header, nullptr), m_modelVersion(modelVersion), + m_previousModelsVersions(previousModelsVersions), m_rangeVersion(rangeVersion), m_rangeParameterController(this, inputEventHandlerDelegate, interactiveRange), m_zoomParameterController(this, interactiveRange, curveView), @@ -132,11 +133,43 @@ Responder * InteractiveCurveViewController::defaultController() { return tabController(); } +bool InteractiveCurveViewController::previousModelsWereAllDeleted() { + bool result = true; + const int modelsCount = numberOfCurves(); + const int memoizationCount = numberOfMemoizedVersions(); + + // Look for a current model that is the same as in the previous version + for (int i = 0; i < modelsCount; i++) { + uint32_t currentVersion = modelVersionAtIndex(i); + for (int j = 0; j < memoizationCount; j++) { + uint32_t * previousVersion = m_previousModelsVersions + j; + if (currentVersion == *previousVersion) { + result = false; + break; + } + } + if (!result) { + break; + } + } + + // Update the memoization + for (int i = 0; i < memoizationCount; i++) { + uint32_t * previousVersion = m_previousModelsVersions + i; + uint32_t newVersion = modelVersionAtIndex(i); + if (*previousVersion != newVersion) { + *previousVersion = newVersion; + } + } + return result; +} + void InteractiveCurveViewController::viewWillAppear() { SimpleInteractiveCurveViewController::viewWillAppear(); uint32_t newModelVersion = modelVersion(); if (*m_modelVersion != newModelVersion) { - if (*m_modelVersion == 0 || numberOfCurves() == 1 || shouldSetDefaultOnModelChange()) { + // Put previousModelsWereAllDeleted first to update the model versions + if (previousModelsWereAllDeleted() || *m_modelVersion == 0 || numberOfCurves() == 1 || shouldSetDefaultOnModelChange()) { interactiveCurveViewRange()->setDefault(); } *m_modelVersion = newModelVersion; diff --git a/apps/shared/interactive_curve_view_controller.h b/apps/shared/interactive_curve_view_controller.h index 58a72a792..9ef048f18 100644 --- a/apps/shared/interactive_curve_view_controller.h +++ b/apps/shared/interactive_curve_view_controller.h @@ -12,7 +12,7 @@ namespace Shared { class InteractiveCurveViewController : public SimpleInteractiveCurveViewController, public InteractiveCurveViewRangeDelegate, public ButtonRowDelegate, public AlternateEmptyViewDefaultDelegate { public: - InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * rangeVersion); + InteractiveCurveViewController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, ButtonRowController * header, InteractiveCurveViewRange * interactiveRange, CurveView * curveView, CurveViewCursor * cursor, uint32_t * modelVersion, uint32_t * previousModelsVersions, uint32_t * rangeVersion); const char * title() override; bool handleEvent(Ion::Events::Event event) override; @@ -28,6 +28,8 @@ public: Responder * defaultController() override; + bool previousModelsWereAllDeleted(); + void viewWillAppear() override; void viewDidDisappear() override; void willExitResponderChain(Responder * nextFirstResponder) override; @@ -39,6 +41,7 @@ protected: virtual void initCursorParameters() = 0; virtual bool moveCursorVertically(int direction) = 0; virtual uint32_t modelVersion() = 0; + virtual uint32_t modelVersionAtIndex(size_t i) = 0; virtual uint32_t rangeVersion() = 0; bool isCursorVisible(); @@ -66,7 +69,9 @@ private: float addMargin(float x, float range, bool isVertical, bool isMin) override; virtual bool shouldSetDefaultOnModelChange() const { return false; } + virtual size_t numberOfMemoizedVersions() const = 0; uint32_t * m_modelVersion; + uint32_t * m_previousModelsVersions; uint32_t * m_rangeVersion; RangeParameterController m_rangeParameterController; ZoomParameterController m_zoomParameterController; diff --git a/apps/shared/labeled_curve_view.cpp b/apps/shared/labeled_curve_view.cpp new file mode 100644 index 000000000..2a10c6655 --- /dev/null +++ b/apps/shared/labeled_curve_view.cpp @@ -0,0 +1,31 @@ +#include "labeled_curve_view.h" + +namespace Shared { + +char * HorizontallyLabeledCurveView::label(Axis axis, int index) const { + if (axis == Axis::Horizontal) { + assert(index < k_maxNumberOfXLabels); + return m_xLabels[index]; + } + return nullptr; +} + +char * VerticallyLabeledCurveView::label(Axis axis, int index) const { + if (axis == Axis::Vertical) { + assert(index < k_maxNumberOfYLabels); + return m_yLabels[index]; + } + return nullptr; +} + +char * LabeledCurveView::label(Axis axis, int index) const { + if (axis == Axis::Horizontal) { + assert(index < k_maxNumberOfXLabels); + return m_xLabels[index]; + } else { + assert(index < k_maxNumberOfYLabels); + return m_yLabels[index]; + } +} + +} diff --git a/apps/shared/labeled_curve_view.h b/apps/shared/labeled_curve_view.h new file mode 100644 index 000000000..cc094702b --- /dev/null +++ b/apps/shared/labeled_curve_view.h @@ -0,0 +1,37 @@ +#ifndef SHARED_LABELED_CURVE_VIEW_H +#define SHARED_LABELED_CURVE_VIEW_H + +#include "curve_view.h" + +/* This CurveView subclass provides label storage for common use cases */ + +namespace Shared { + +class HorizontallyLabeledCurveView : public CurveView { +public: + using CurveView::CurveView; +private: + char * label(Axis axis, int index) const override; + mutable char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; +}; + +class VerticallyLabeledCurveView : public CurveView { +public: + using CurveView::CurveView; +private: + char * label(Axis axis, int index) const override; + mutable char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize]; +}; + +class LabeledCurveView : public CurveView { +public: + using CurveView::CurveView; +private: + char * label(Axis axis, int index) const override; + mutable char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; + mutable char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize]; +}; + +} + +#endif diff --git a/apps/shared/round_cursor_view.cpp b/apps/shared/round_cursor_view.cpp index cf9bbc739..0eb160022 100644 --- a/apps/shared/round_cursor_view.cpp +++ b/apps/shared/round_cursor_view.cpp @@ -49,6 +49,14 @@ void RoundCursorView::setCursorFrame(KDRect f, bool force) { CursorView::setCursorFrame(f, force); } +void RoundCursorView::markRectAsDirty(KDRect rect) { + /* The CursorView class inherits from TransparentView, so does + * RoundCursorView. The method markRectAsDirty is thus overriden to avoid + * marking as dirty the background of the RoundCursorView in its superview. + */ + View::markRectAsDirty(rect); +} + #ifdef GRAPH_CURSOR_SPEEDUP bool RoundCursorView::eraseCursorIfPossible() { if (!m_underneathPixelBufferLoaded) { diff --git a/apps/shared/round_cursor_view.h b/apps/shared/round_cursor_view.h index 332467bd1..930cea460 100644 --- a/apps/shared/round_cursor_view.h +++ b/apps/shared/round_cursor_view.h @@ -24,6 +24,7 @@ public: void resetMemoization() const { m_underneathPixelBufferLoaded = false; } #endif private: + void markRectAsDirty(KDRect rect) override; #ifdef GRAPH_CURSOR_SPEEDUP bool eraseCursorIfPossible(); #endif diff --git a/apps/shared/sum_graph_controller.cpp b/apps/shared/sum_graph_controller.cpp index 6e3003dd3..cd597088e 100644 --- a/apps/shared/sum_graph_controller.cpp +++ b/apps/shared/sum_graph_controller.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "poincare_helpers.h" #include @@ -150,14 +149,13 @@ void SumGraphController::reloadBannerView() { m_legendView.setEditableZone(m_cursor->x()); result = NAN; } - m_legendView.setSumSymbol(m_step, m_startSum, endSum, result, functionLayout); + m_legendView.setSumLayout(m_step, m_startSum, endSum, result, functionLayout); } /* Legend View */ SumGraphController::LegendView::LegendView(SumGraphController * controller, InputEventHandlerDelegate * inputEventHandlerDelegate, CodePoint sumSymbol) : m_sum(0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground), - m_sumLayout(), m_legend(k_font, I18n::Message::Default, 0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground), m_editableZone(controller, m_textBuffer, k_editableZoneBufferSize, TextField::maxBufferSize(), inputEventHandlerDelegate, controller, k_font, 0.0f, 0.5f, Palette::PrimaryText, Palette::SubMenuBackground), m_sumSymbol(sumSymbol) @@ -179,54 +177,41 @@ void SumGraphController::LegendView::setLegendMessage(I18n::Message message, Ste } void SumGraphController::LegendView::setEditableZone(double d) { - constexpr int precision = Preferences::MediumNumberOfSignificantDigits; - constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision); - char buffer[bufferSize]; - PoincareHelpers::ConvertFloatToTextWithDisplayMode(d, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal); + char buffer[k_valuesBufferSize]; + PoincareHelpers::ConvertFloatToTextWithDisplayMode(d, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal); m_editableZone.setText(buffer); } -void SumGraphController::LegendView::setSumSymbol(Step step, double start, double end, double result, Layout functionLayout) { +void SumGraphController::LegendView::setSumLayout(Step step, double start, double end, double result, Layout functionLayout) { assert(step == Step::Result || functionLayout.isUninitialized()); constexpr int sigmaLength = 2; const CodePoint sigma[sigmaLength] = {' ', m_sumSymbol}; - if (step == Step::FirstParameter) { - m_sumLayout = LayoutHelper::CodePointString(sigma, sigmaLength); - } else if (step == Step::SecondParameter) { - constexpr int precision = Preferences::MediumNumberOfSignificantDigits; - constexpr int bufferSize = PrintFloat::charSizeForFloatsWithPrecision(precision); - char buffer[bufferSize]; - PoincareHelpers::ConvertFloatToTextWithDisplayMode(start, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal); - m_sumLayout = CondensedSumLayout::Builder( - LayoutHelper::CodePointString(sigma, sigmaLength), + Poincare::Layout sumLayout = LayoutHelper::CodePointString(sigma, sigmaLength); + if (step != Step::FirstParameter) { + char buffer[k_valuesBufferSize]; + Layout endLayout; + if (step == Step::SecondParameter) { + endLayout = EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, k_font, false); + } else { + PoincareHelpers::ConvertFloatToTextWithDisplayMode(end, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal); + endLayout = LayoutHelper::String(buffer, strlen(buffer), k_font); + } + PoincareHelpers::ConvertFloatToTextWithDisplayMode(start, buffer, k_valuesBufferSize, k_valuesPrecision, Preferences::PrintFloatMode::Decimal); + sumLayout = CondensedSumLayout::Builder( + sumLayout, LayoutHelper::String(buffer, strlen(buffer), k_font), - EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, k_font, false)); - } else { - constexpr int precision = Preferences::LargeNumberOfSignificantDigits; - constexpr int sizeForPrecision = PrintFloat::charSizeForFloatsWithPrecision(precision); - constexpr int bufferSize = 2 + sizeForPrecision; - char buffer[bufferSize]; - PoincareHelpers::ConvertFloatToTextWithDisplayMode(start, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal); - Layout start = LayoutHelper::String(buffer, strlen(buffer), k_font); - PoincareHelpers::ConvertFloatToTextWithDisplayMode(end, buffer, bufferSize, precision, Preferences::PrintFloatMode::Decimal); - Layout end = LayoutHelper::String(buffer, strlen(buffer), k_font); - m_sumLayout = CondensedSumLayout::Builder( - LayoutHelper::CodePointString(sigma, sigmaLength), - start, - end); - strlcpy(buffer, "= ", 3); - PoincareHelpers::ConvertFloatToText(result, buffer+2, bufferSize-2, precision); - m_sumLayout = HorizontalLayout::Builder( - m_sumLayout, - functionLayout, - LayoutHelper::String(buffer, strlen(buffer), k_font)); - } - m_sum.setLayout(m_sumLayout); - if (step == Step::Result) { - m_sum.setAlignment(0.5f, 0.5f); - } else { - m_sum.setAlignment(0.0f, 0.5f); + endLayout); + if (step == Step::Result) { + PoincareHelpers::ConvertFloatToText(result, buffer, k_valuesBufferSize, k_valuesPrecision); + sumLayout = HorizontalLayout::Builder( + sumLayout, + functionLayout, + LayoutHelper::String("= ", 2, k_font), + LayoutHelper::String(buffer, strlen(buffer), k_font)); + } } + m_sum.setLayout(sumLayout); + m_sum.setAlignment(0.5f * (step == Step::Result), 0.5f); layoutSubviews(step, false); } diff --git a/apps/shared/sum_graph_controller.h b/apps/shared/sum_graph_controller.h index 8042117b2..e876f23c8 100644 --- a/apps/shared/sum_graph_controller.h +++ b/apps/shared/sum_graph_controller.h @@ -55,9 +55,11 @@ private: void drawRect(KDContext * ctx, KDRect rect) const override; void setLegendMessage(I18n::Message message, Step step); void setEditableZone(double d); - void setSumSymbol(Step step, double start, double end, double result, Poincare::Layout functionLayout); + void setSumLayout(Step step, double start, double end, double result, Poincare::Layout functionLayout); private: - constexpr static KDCoordinate k_editableZoneBufferSize = Poincare::PrintFloat::k_maxFloatCharSize; + constexpr static size_t k_editableZoneBufferSize = Poincare::PrintFloat::k_maxFloatCharSize; + constexpr static int k_valuesPrecision = Poincare::Preferences::MediumNumberOfSignificantDigits; + constexpr static int k_valuesBufferSize = Poincare::PrintFloat::charSizeForFloatsWithPrecision(k_valuesPrecision); constexpr static KDCoordinate k_legendHeight = 35; constexpr static const KDFont * k_font = KDFont::SmallFont; static KDCoordinate editableZoneWidth() { return 12*k_font->glyphSize().width(); } @@ -69,7 +71,6 @@ private: void layoutSubviews(bool force = false) override; void layoutSubviews(Step step, bool force); ExpressionView m_sum; - Poincare::Layout m_sumLayout; MessageTextView m_legend; TextField m_editableZone; char m_textBuffer[k_editableZoneBufferSize]; diff --git a/apps/statistics/box_axis_view.cpp b/apps/statistics/box_axis_view.cpp index 5057fcdd5..c36ab4175 100644 --- a/apps/statistics/box_axis_view.cpp +++ b/apps/statistics/box_axis_view.cpp @@ -12,8 +12,4 @@ void BoxAxisView::drawRect(KDContext * ctx, KDRect rect) const { drawLabelsAndGraduations(ctx, rect, Axis::Horizontal, false, false, true, k_axisMargin); } -char * BoxAxisView::label(Axis axis, int index) const { - return axis == Axis::Vertical ? nullptr : (char *)m_labels[index]; -} - } diff --git a/apps/statistics/box_axis_view.h b/apps/statistics/box_axis_view.h index 2fbfe0df8..b3caef60d 100644 --- a/apps/statistics/box_axis_view.h +++ b/apps/statistics/box_axis_view.h @@ -3,24 +3,21 @@ #include "box_range.h" #include "store.h" -#include "../shared/curve_view.h" +#include "../shared/labeled_curve_view.h" #include "../constant.h" #include namespace Statistics { -class BoxAxisView : public Shared::CurveView { +class BoxAxisView : public Shared::HorizontallyLabeledCurveView { public: BoxAxisView(Store * store) : - CurveView(&m_boxRange), - m_labels{}, + HorizontallyLabeledCurveView(&m_boxRange), m_boxRange(BoxRange(store)) {} void drawRect(KDContext * ctx, KDRect rect) const override; private: constexpr static KDCoordinate k_axisMargin = 3; - char * label(Axis axis, int index) const override; - char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; BoxRange m_boxRange; }; diff --git a/apps/statistics/box_view.h b/apps/statistics/box_view.h index 6ce1a9275..b95cbbc29 100644 --- a/apps/statistics/box_view.h +++ b/apps/statistics/box_view.h @@ -37,7 +37,6 @@ private: static constexpr KDCoordinate k_quantileBarWidth = 2; KDCoordinate boxLowerBoundPixel() const; KDCoordinate boxUpperBoundPixel() const; - char * label(Axis axis, int index) const override { return nullptr; } Store * m_store; BoxRange m_boxRange; int m_series; diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index e6bdc2a1b..66250b872 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -8,10 +8,9 @@ using namespace Shared; namespace Statistics { HistogramView::HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor, KDColor notSelectedHistogramColor, KDColor selectedBarColor) : - CurveView(store, nullptr, bannerView, nullptr), + HorizontallyLabeledCurveView(store, nullptr, bannerView, nullptr), m_controller(controller), m_store(store), - m_labels{}, m_highlightedBarStart(NAN), m_highlightedBarEnd(NAN), m_series(series), @@ -63,10 +62,6 @@ void HistogramView::setHighlight(float start, float end) { } } -char * HistogramView::label(Axis axis, int index) const { - return axis == Axis::Vertical ? nullptr : (char *)m_labels[index]; -} - float HistogramView::EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context) { Store * store = (Store *)model; float totalSize = ((float *)context)[0]; diff --git a/apps/statistics/histogram_view.h b/apps/statistics/histogram_view.h index 0737e7854..94bb8a9bf 100644 --- a/apps/statistics/histogram_view.h +++ b/apps/statistics/histogram_view.h @@ -5,13 +5,13 @@ #include #include "store.h" #include "../constant.h" -#include "../shared/curve_view.h" +#include "../shared/labeled_curve_view.h" namespace Statistics { class HistogramController; -class HistogramView : public Shared::CurveView { +class HistogramView : public Shared::HorizontallyLabeledCurveView { public: HistogramView(HistogramController * controller, Store * store, int series, Shared::BannerView * bannerView, KDColor selectedHistogramColor = Palette::StatisticsSelected, KDColor notSelectedHistogramColor = Palette::StatisticsNotSelected, KDColor selectedBarColor = Palette::StatisticsSelected); int series() const { return m_series; } @@ -21,10 +21,8 @@ public: void setHighlight(float start, float end); void setDisplayLabels(bool display) { m_displayLabels = display; } private: - char * label(Axis axis, int index) const override; HistogramController * m_controller; Store * m_store; - char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; static float EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context); float m_highlightedBarStart; float m_highlightedBarEnd; diff --git a/apps/toolbox.de.i18n b/apps/toolbox.de.i18n index 61999f5f7..c7acf774d 100644 --- a/apps/toolbox.de.i18n +++ b/apps/toolbox.de.i18n @@ -1,17 +1,17 @@ -Unit = "Units" -UnitTimeMenu = "Time" -UnitTimeSecondMenu = "Second" -UnitTimeSecond = "Second" -UnitTimeSecondMilli = "Millisecond" -UnitTimeSecondMicro = "Microsecond" -UnitTimeSecondNano = "Nanosecond" +Unit = "Einheiten" +UnitTimeMenu = "Zeit" +UnitTimeSecondMenu = "Sekunde" +UnitTimeSecond = "Sekunde" +UnitTimeSecondMilli = "Millisekunde" +UnitTimeSecondMicro = "Microsekunde" +UnitTimeSecondNano = "Nanosekunde" UnitTimeMinute = "Minute" -UnitTimeHour = "Hour" -UnitTimeDay = "Day" -UnitTimeWeek = "Week" -UnitTimeMonth = "Month" -UnitTimeYear = "Year" -UnitDistanceMenu = "Distance" +UnitTimeHour = "Stunde" +UnitTimeDay = "Tag" +UnitTimeWeek = "Woche" +UnitTimeMonth = "Monat" +UnitTimeYear = "Jahr" +UnitDistanceMenu = "Distanz" UnitDistanceMeterMenu = "Meter" UnitDistanceMeterKilo = "Kilometer" UnitDistanceMeter = "Meter" @@ -19,43 +19,43 @@ UnitDistanceMeterMilli = "Millimeter" UnitDistanceMeterMicro = "Micrometer" UnitDistanceMeterNano = "Nanometer" UnitDistanceMeterPico = "Picometer" -UnitDistanceAstronomicalUnit = "Astronomical unit" -UnitDistanceLightYear = "Light year" +UnitDistanceAstronomicalUnit = "Astronomische Einheit" +UnitDistanceLightYear = "Lichtjahr" UnitDistanceParsec = "Parsec" -UnitMassMenu = "Mass" -UnitMassGramKilo = "Kilogram" -UnitMassGram = "Gram" -UnitMassGramMilli = "Milligram" -UnitMassGramMicro = "Microgram" -UnitMassGramNano = "Nanogram" +UnitMassMenu = "Masse" +UnitMassGramKilo = "Kilogramm" +UnitMassGram = "Gramm" +UnitMassGramMilli = "Milligramm" +UnitMassGramMicro = "Microgramm" +UnitMassGramNano = "Nanogramm" UnitMassTonne = "Tonne" -UnitCurrentMenu = "Electric current" +UnitCurrentMenu = "Elektrischer Strom" UnitCurrentAmpere = "Ampere" UnitCurrentAmpereMilli = "Milliampere" UnitCurrentAmpereMicro = "Microampere" -UnitTemperatureMenu = "Temperature" +UnitTemperatureMenu = "Temperaturen" UnitTemperatureKelvin = "Kelvin" -UnitAmountMenu = "Amount of substance" -UnitAmountMole = "Mole" -UnitAmountMoleMilli = "Millimole" -UnitAmountMoleMicro = "Micromole" -UnitLuminousIntensityMenu = "Luminous intensity" +UnitAmountMenu = "Substanzmenge" +UnitAmountMole = "Mol" +UnitAmountMoleMilli = "Millimol" +UnitAmountMoleMicro = "Micromol" +UnitLuminousIntensityMenu = "Helligkeit" UnitLuminousIntensityCandela = "Candela" -UnitFrequencyMenu = "Frequency" +UnitFrequencyMenu = "Frequenz" UnitFrequencyHertzGiga = "Gigahertz" UnitFrequencyHertzMega = "Megahertz" UnitFrequencyHertzKilo = "Kilohertz" UnitFrequencyHertz = "Hertz" -UnitForceMenu = "Force" +UnitForceMenu = "Kraft" UnitForceNewtonKilo = "Kilonewton" UnitForceNewton = "Newton" UnitForceNewtonMilli = "Millinewton" -UnitPressureMenu = "Pressure" +UnitPressureMenu = "Druck" UnitPressurePascal = "Pascal" UnitPressurePascalHecto = "Hectopascal" UnitPressureBar = "Bar" UnitPressureAtm = "Atmosphere" -UnitEnergyMenu = "Energy" +UnitEnergyMenu = "Energie" UnitEnergyJouleMenu = "Joule" UnitEnergyJouleKilo = "Kilojoule" UnitEnergyJoule = "Joule" @@ -65,37 +65,37 @@ UnitEnergyElectronVoltMega = "Megaelectronvolt" UnitEnergyElectronVoltKilo = "Kiloelectronvolt" UnitEnergyElectronVolt = "Electronvolt" UnitEnergyElectronVoltMilli = "Millielectronvolt" -UnitPowerMenu = "Power" +UnitPowerMenu = "Leistung" UnitPowerWattGiga = "Gigawatt" UnitPowerWattMega = "Megawatt" UnitPowerWattKilo = "Kilowatt" UnitPowerWatt = "Watt" UnitPowerWattMilli = "Milliwatt" UnitPowerWattMicro = "Microwatt" -UnitElectricChargeMenu = "Electric charge" +UnitElectricChargeMenu = "Elektrische Ladung" UnitChargeCoulomb = "Coulomb" -UnitPotentialMenu = "Electric potential" +UnitPotentialMenu = "Elektrisches Potenzial" UnitPotentialVoltKilo = "Kilovolt" UnitPotentialVolt = "Volt" UnitPotentialVoltMilli = "Millivolt" UnitPotentialVoltMicro = "Microvolt" -UnitCapacitanceMenu = "Electrical capacitance" +UnitCapacitanceMenu = "Elektrische Kapazität" UnitCapacitanceFarad = "Farad" UnitCapacitanceFaradMilli = "Millifarad" UnitCapacitanceFaradMicro = "Microfarad" -UnitResistanceMenu = "Electrical resistance" +UnitResistanceMenu = "Elektrischer Widerstand" UnitResistanceOhmKilo = "Kiloohm" UnitResistanceOhm = "Ohm" -UnitConductanceMenu = "Electrical conductance" +UnitConductanceMenu = "Elektrische Leitfähigkeit" UnitConductanceSiemens = "Siemens" UnitConductanceSiemensMilli = "Millisiemens" -UnitMagneticFieldMenu = "Magnetic field" +UnitMagneticFieldMenu = "Magnetisches Feld" UnitMagneticFieldTesla = "Tesla" -InductanceMenu = "Electrical inductance" +InductanceMenu = "Elektrische Induktion" UnitInductanceHenry = "Henry" -UnitSurfaceMenu = "Area" -UnitSurfaceHectar = "Hectare" -UnitVolumeMenu = "Volume" +UnitSurfaceMenu = "Fläche" +UnitSurfaceHectar = "Hektar" +UnitVolumeMenu = "Volumen" UnitVolumeLiter = "Liter" UnitVolumeLiterDeci = "Deciliter" UnitVolumeLiterCenti = "Centiliter" @@ -409,9 +409,9 @@ NumberElementUue = "119 - Ununennium (Uue)" AlphaElementUue = "Uue - Ununennium (119)" NumberElementUbn = "120 - Unbinilium (Ubn)" AlphaElementUbn = "Ubn - Unbinilium (120)" -UnitOfMesurement = "Unit of mesurement" +UnitOfMesurement = "Messeinheit" SpeedOfLightTag = "Lichtgeschwindigkeit (m·s^-1)" -YearLightTag = "Ein Jahr Licht (km)" +YearLightTag = "Lichtjahr (km)" Thermodynamics = "Thermodynamik" BoltzmannTag = "Boltzmann Konstante (J·K^-1)" AvogadroTag = "Avogadro-Konstante (mol^-1)" @@ -427,4 +427,4 @@ NeutronMassTag = "Masse eines Neutrons (kg)" Gravitation = "Gravitation" ElementalChargeTag = "Elementarladung (C)" GAccelerationTag = "Beschleunigung (m·s^-2)" -GConstantTag = "Konstant (m^3·kg^-1·s^-2)" \ No newline at end of file +GConstantTag = "Konstant (m^3·kg^-1·s^-2)" diff --git a/build/scenario/Makefile b/build/scenario/Makefile index aafb398e8..64f65e8bf 100644 --- a/build/scenario/Makefile +++ b/build/scenario/Makefile @@ -1,11 +1,11 @@ .PHONY: scenario_logger scenario_logger: - $(Q) make -j8 PLATFORM=simulator clean && make -j8 DEBUG=1 ARCH=x86_64 PLATFORM=simulator epsilon.headless.bin + $(Q) $(MAKE) PLATFORM=simulator clean && $(MAKE) DEBUG=1 ARCH=x86_64 PLATFORM=simulator epsilon.headless.bin $(Q) cp output/debug/simulator/macos/x86_64/epsilon.headless.bin epsilon_scenario_logger.bin @echo "Run ./epsilon_scenario_logger.bin --logAfter 0 < scenario.esc to log a scenario" .PHONY: scenario_creator scenario_creator: - $(Q) make -j8 PLATFORM=simulator clean && make -j8 DEBUG=1 ESCHER_LOG_EVENTS_BINARY=1 PLATFORM=simulator + $(Q) $(MAKE) PLATFORM=simulator clean && $(MAKE) DEBUG=1 ESCHER_LOG_EVENTS_BINARY=1 PLATFORM=simulator $(Q) cp -R output/debug/simulator/macos/app/Payload/Epsilon.app epsilon_scenario_creator.app @echo "Run lldb epsilon_scenario_creator.app then process launch -o scenario.esc to create a scenario" diff --git a/build/targets.all.mak b/build/targets.all.mak new file mode 100644 index 000000000..e6d989add --- /dev/null +++ b/build/targets.all.mak @@ -0,0 +1,62 @@ +ANDROID_GRADLE_KEYSTORE ?= ~/.gradle/google-play-upload.keystore +ANDROID_GRADLE_PROPERTIES ?= ~/.gradle/gradle.properties +IOS_MOBILE_PROVISION ?= build/artifacts/NumWorks_Graphing_Calculator_Distribution.mobileprovision +EMCC ?= emcc + +define source_emsdk +source ~/emsdk/emsdk_env.sh > /dev/null +endef + +define file_check +@ if test ! -f $(1); \ + then \ + echo "Missing file: $(1)"; \ + exit 1; \ +fi +endef + +define command_check +@ if ! command -v $(1) > /dev/null; \ + then \ + echo "Missing command: $(1), did you forget to source?"; \ + exit 1; \ +fi +endef + +.PHONY: all_official +all_official: + $(call file_check,$(ANDROID_GRADLE_KEYSTORE)) + $(call file_check,$(ANDROID_GRADLE_PROPERTIES)) + $(call file_check,$(IOS_MOBILE_PROVISION)) + $(call command_check,$(EMCC)) + $(Q) rm -rf output/all_official + $(Q) mkdir -p output/all_official + $(Q) echo "BUILD_FIRMWARE DEVICE N0110" + $(Q) $(MAKE) clean + $(Q) $(MAKE) epsilon.official.onboarding.dfu + $(Q) cp output/release/device/n0110/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0110.dfu + $(Q) echo "BUILD_FIRMWARE DEVICE N0100" + $(Q) $(MAKE) MODEL=n0100 clean + $(Q) $(MAKE) MODEL=n0100 epsilon.official.onboarding.dfu + $(Q) cp output/release/device/n0100/epsilon.official.onboarding.dfu output/all_official/epsilon.device.n0100.dfu + $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB ZIP" + $(Q) $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web clean + $(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web output/release/simulator/web/simulator.official.zip + $(Q) cp output/release/simulator/web/simulator.official.zip output/all_official/simulator.web.zip + $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB JS" + $(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web epsilon.official.js + $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.js + $(Q) cp output/release/simulator/web/epsilon.official.js.mem output/all_official/epsilon.js.mem + $(Q) echo "BUILD_FIRMWARE SIMULATOR WEB PYTHON JS" + $(Q) $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web clean + $(Q) $(call source_emsdk); $(MAKE) DEBUG=0 PLATFORM=simulator TARGET=web EPSILON_GETOPT=1 EPSILON_APPS=code epsilon.official.js + $(Q) cp output/release/simulator/web/epsilon.official.js output/all_official/epsilon.python.js + $(Q) cp output/release/simulator/web/epsilon.official.js.mem output/all_official/epsilon.python.js.mem + $(Q) echo "BUILD_FIRMWARE SIMULATOR ANDROID" + $(Q) $(MAKE) PLATFORM=simulator TARGET=android clean + $(Q) $(MAKE) PLATFORM=simulator TARGET=android epsilon.official.apk + $(Q) cp output/release/simulator/android/app/outputs/apk/release/android-release-unsigned.apk output/all_official/epsilon.apk + $(Q) echo "BUILD_FIRMWARE SIMULATOR IOS" + $(Q) $(MAKE) PLATFORM=simulator TARGET=ios clean + $(Q) $(MAKE) PLATFORM=simulator TARGET=ios IOS_PROVISIONNING_PROFILE=$(IOS_MOBILE_PROVISION) output/release/simulator/ios/app/epsilon.official.ipa + $(Q) cp output/release/simulator/ios/app/epsilon.official.ipa output/all_official/epsilon.ipa diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index 8f56063df..456da622a 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -20,30 +20,3 @@ $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ex fi $(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^) -.PHONY: %.two_binaries -%.two_binaries: %.elf - @echo "Building an internal and an external binary for $<" - $(Q) $(OBJCOPY) -O binary -j .text.external -j .rodata.external -j .exam_mode_buffer $(BUILD_DIR)/$< $(BUILD_DIR)/$(basename $<).external.bin - $(Q) $(OBJCOPY) -O binary -R .text.external -R .rodata.external -R .exam_mode_buffer $(BUILD_DIR)/$< $(BUILD_DIR)/$(basename $<).internal.bin - @echo "Padding $(basename $<).external.bin and $(basename $<).internal.bin" - $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin - $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).internal.bin - -.PHONY: binpack -binpack: - rm -rf build/binpack - mkdir -p build/binpack - ${MAKE} clean - ${MAKE} $(BUILD_DIR)/flasher.light.bin - cp $(BUILD_DIR)/flasher.light.bin build/binpack - ${MAKE} clean - ${MAKE} $(BUILD_DIR)/bench.flash.bin - ${MAKE} $(BUILD_DIR)/bench.ram.bin - cp $(BUILD_DIR)/bench.ram.bin $(BUILD_DIR)/bench.flash.bin build/binpack - ${MAKE} clean - ${MAKE} epsilon.onboarding.update.two_binaries - cp $(BUILD_DIR)/epsilon.onboarding.update.internal.bin $(BUILD_DIR)/epsilon.onboarding.update.external.bin build/binpack - ${MAKE} clean - cd build && for binary in flasher.light.bin bench.flash.bin bench.ram.bin epsilon.onboarding.internal.bin epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done - cd build && tar cvfz binpack-`git rev-parse HEAD | head -c 7`.tgz binpack - rm -rf build/binpack diff --git a/build/targets.mak b/build/targets.mak index 68ce3de04..ca7b9d97c 100644 --- a/build/targets.mak +++ b/build/targets.mak @@ -3,21 +3,15 @@ base_src = $(liba_src) $(kandinsky_src) $(escher_src) $(libaxx_src) $(poincare_s epsilon_src = $(base_src) $(ion_default_src) $(apps_default_src) epsilon_official_src = $(base_src) $(ion_default_src) $(apps_official_default_src) -epsilon_onboarding_src = $(base_src) $(ion_default_src) $(apps_onboarding_src) -epsilon_official_onboarding_src = $(base_src) $(ion_default_src) $(apps_official_onboarding_src) -epsilon_onboarding_update_src = $(base_src) $(ion_default_src) $(apps_onboarding_update_src) -epsilon_official_onboarding_update_src = $(base_src) $(ion_default_src) $(apps_official_onboarding_update_src) -epsilon_onboarding_beta_src = $(base_src) $(ion_default_src) $(apps_onboarding_beta_src) -epsilon_official_onboarding_beta_src = $(base_src) $(ion_default_src) $(apps_official_onboarding_beta_src) $(BUILD_DIR)/epsilon.$(EXE): $(call object_for,$(epsilon_src)) $(BUILD_DIR)/epsilon.official.$(EXE): $(call object_for,$(epsilon_official_src)) -$(BUILD_DIR)/epsilon.onboarding.$(EXE): $(call object_for,$(epsilon_onboarding_src)) -$(BUILD_DIR)/epsilon.official.onboarding.$(EXE): $(call object_for,$(epsilon_official_onboarding_src)) -$(BUILD_DIR)/epsilon.onboarding.update.$(EXE): $(call object_for,$(epsilon_onboarding_update_src)) -$(BUILD_DIR)/epsilon.official.onboarding.update.$(EXE): $(call object_for,$(epsilon_official_onboarding_update_src)) -$(BUILD_DIR)/epsilon.onboarding.beta.$(EXE): $(call object_for,$(epsilon_onboarding_beta_src)) -$(BUILD_DIR)/epsilon.official.onboarding.beta.$(EXE): $(call object_for,$(epsilon_official_onboarding_beta_src)) +$(BUILD_DIR)/epsilon.onboarding.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_src)) +$(BUILD_DIR)/epsilon.official.onboarding.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_src)) +$(BUILD_DIR)/epsilon.onboarding.update.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_update_src)) +$(BUILD_DIR)/epsilon.official.onboarding.update.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_update_src)) +$(BUILD_DIR)/epsilon.onboarding.beta.$(EXE): $(call object_for, $(base_src) $(ion_default_src) $(apps_onboarding_beta_src)) +$(BUILD_DIR)/epsilon.official.onboarding.beta.$(EXE): $(call object_for,$(base_src) $(ion_default_src) $(apps_official_onboarding_beta_src)) test_base_src = $(base_src) $(apps_tests_src) $(runner_src) $(tests_src) @@ -41,3 +35,5 @@ endef -include build/targets.$(PLATFORM).mak $(foreach extension,$(HANDY_TARGETS_EXTENSIONS),$(foreach executable,$(HANDY_TARGETS),$(eval $(call handy_target_rule,$(executable),$(extension))))) + +include build/targets.all.mak diff --git a/build/targets.simulator.web.mak b/build/targets.simulator.web.mak index 65abe11f7..a1774a34a 100644 --- a/build/targets.simulator.web.mak +++ b/build/targets.simulator.web.mak @@ -1,17 +1,6 @@ -$(BUILD_DIR)/epsilon.js: EMSCRIPTEN_INIT_FILE = 1 +$(BUILD_DIR)/epsilon%packed.js: EMSCRIPTEN_INIT_FILE = 0 $(BUILD_DIR)/test.headless.js: EMSCRIPTEN_MODULARIZE = 0 $(BUILD_DIR)/epsilon.packed.js: $(call object_for,$(epsilon_src)) - -.PHONY: workshop_python_emulator -workshop_python_emulator: - $(MAKE) PLATFORM=simulator TARGET=web clean_for_apps_selection - $(MAKE) PLATFORM=simulator TARGET=web EPSILON_APPS=code - $(MAKE) PLATFORM=simulator TARGET=web clean_for_apps_selection - -.PHONY: clean_for_apps_selection -clean_for_apps_selection: - @echo "CLEAN BEFORE CHANGING EPSILON_APPS" - $(Q) rm -f $(BUILD_DIR)/apps/apps_container_storage.o - $(Q) rm -f $(BUILD_DIR)/apps/i18n.* +$(BUILD_DIR)/epsilon.official.packed.js: $(call object_for,$(epsilon_official_src)) diff --git a/build/toolchain.emscripten.mak b/build/toolchain.emscripten.mak index f8ec0beaa..fb6aafc39 100644 --- a/build/toolchain.emscripten.mak +++ b/build/toolchain.emscripten.mak @@ -132,7 +132,7 @@ EMFLAGS += -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 # Configure LDFLAGS EMSCRIPTEN_MODULARIZE ?= 1 LDFLAGS += -s MODULARIZE=$(EMSCRIPTEN_MODULARIZE) -s 'EXPORT_NAME="Epsilon"' -EMSCRIPTEN_INIT_FILE ?= 0 +EMSCRIPTEN_INIT_FILE ?= 1 LDFLAGS += --memory-init-file $(EMSCRIPTEN_INIT_FILE) SFLAGS += $(EMFLAGS) diff --git a/docs/build/index.md b/docs/build/index.md index f704da2d1..b34fd48ca 100644 --- a/docs/build/index.md +++ b/docs/build/index.md @@ -54,7 +54,7 @@ git clone https://github.com/numworks/epsilon.git ## Run Epsilon on your computer -Once the SDK has been installed, just open your terminal (Msys2, Terminal.app, xterm, etc...) and type the following commands: +Once the SDK has been installed, just open your terminal (Msys2, Terminal.app, xterm…) and type the following commands: ``` make PLATFORM=simulator clean diff --git a/docs/index.md b/docs/index.md index 509364255..edc128688 100644 --- a/docs/index.md +++ b/docs/index.md @@ -46,12 +46,11 @@ The choice of a programming language is a controversial topic. Not all of them c - It is a [system](https://en.wikipedia.org/wiki/System_programming_language) programming language, which is something we need since we have to write some low-level code. - It has excellent tooling: several extremly high-quality compilers -- It is used for several high-profile projects LLVM, WebKit, MySQL, Photoshop, etc... This ensures a strong ecosystem of tools, code and documentation. +- It is used for several high-profile projects LLVM, WebKit, MySQL, Photoshop… This ensures a strong ecosystem of tools, code and documentation. - It easily allows Object-Oriented Programming, which is a convenient abstraction. - Of course knowing a tool means knowing its limits. C++ isn't exempt of defaults: -- It *is* a complicated language. The C++ 11 specification is 1300 pages long. +- It *is* a complicated language. The C++ 11 specification is 1'300 pages long. - It allows for a lot of abstractions, which is a double-edged sword. It can allow for some very tricky code, and it's very easy to have complex operations being run without noticing. If you want to contribute to Epsilon, you'll need to learn some C++. @@ -66,7 +65,7 @@ The stack memory is possibly the most used area of memory. It contains all local #### Heap memory -Unfortunately, local variables can't answer all use cases, and sometimes one need to allocate memory that lives longer than a function call. This is traditionally done by using a pair of *malloc* / *free* functions. +Unfortunately, local variables can't answer all use cases, and sometimes one need to allocate memory that lives longer than a function call. This is traditionally done by using a pair of `malloc` / `free` functions. This raises a lot of potential problems that can trigger unpredictable dynamic behaviors: @@ -77,7 +76,7 @@ This raises a lot of potential problems that can trigger unpredictable dynamic b
Memory allocation has to be contiguous. So the allocation algorithm has to use a smart heuristic to ensure that it will not fragment its allocated space too much.
-Some automatic memory management solutions do exist (garbage collection, smart pointers), but they all come with a cost. We decided to manually manage dynamic memory, but to use it as sparingly as possible. +We decided to avoid `malloc` altogether and to use a mix of static allocation and a pool of relocatable garbage-collected nodes for manipulating mathematical expressions. ### Writing code that runs on the bare metal @@ -90,8 +89,8 @@ In practice, this means that the firmware will need to know in advance how the m - Where will we store read-only variables? - Where will the code live in memory? -The firmware will also need to take special care of the system initialization. There is no such thing as a "main" function on a firmware. Instead, on Cortex-M4 devices, after reset the CPU simply jumps to the address contained at address 0x00000000 (which happens to be the first bytes of flash memory). So if your firmware starts by 0x12345678, code execution will start at address 0x12345678. +The firmware will also need to take special care of the system initialization. There is no such thing as a "main" function on a firmware. Instead, on Cortex-M devices, after reset the CPU simply jumps to the address contained at address 0x00000000 (which happens to be the first bytes of flash memory). So if your firmware starts by 0x12345678, code execution will start at address 0x12345678. -Enforcing such a careful memory layout would be an impossible job without the proper tool. Fortunately, embedded linkers can be scripted and allow this kind of tailor-made configuration. You'll find Epsilon's linker script in "ion/src/device/boot/flash.ld" - it is heavily commented and should be self-explanatory. +Enforcing such a careful memory layout would be an impossible job without the proper tool. Fortunately, embedded linkers can be scripted and allow this kind of tailor-made configuration. You'll find Epsilon's linker script in [ion/src/device/n0110/flash.ld](https://github.com/numworks/epsilon/blob/master/ion/src/device/n0110/flash.ld) - it is heavily commented and should be self-explanatory. -That being said, there are additional things the OS usually takes care of which we need to do ourselves : for example, initialize global variables to zero. This is done in the "ion/src/device/boot/rt0.cpp" file, which is worth reading too. +That being said, there are additional things the OS usually takes care of which we need to do ourselves : for example, initialize global variables to zero. This is done in the [ion/src/device/shared/boot/rt0.cpp](https://github.com/numworks/epsilon/blob/master/ion/src/device/shared/boot/rt0.cpp) file, which is worth reading too. diff --git a/docs/ion/index.md b/docs/ion/index.md index 9d87c5458..c09205d95 100644 --- a/docs/ion/index.md +++ b/docs/ion/index.md @@ -11,25 +11,25 @@ By providing multiple implementations of the Ion functions, we therefore can get ## Device -This is the reference platform corresponding to the actual device. To really understand what the code is doing, you'll need to refer to our Electrical Engineering pages. Among other thing, Ion is responsible for handling the boot process and the memory layout of the code on the device: +This is the reference platform corresponding to the actual device. To really understand what the code is doing, you'll need to refer to our [Electrical Engineering](https://www.numworks.com/resources/engineering/hardware/electrical/) pages. Among other thing, Ion is responsible for handling the boot process and the memory layout of the code on the device: ### Boot -On boot, the Cortex core interprets the beginning of Flash memory as an array of addresses, each having a specific meaning. For example, the first value gives the address of the stack and the second one the address the processor jumps to right after reset. This list of addresses is called the ISR table. +On boot, the Cortex core interprets the beginning of Flash memory as an array of addresses, each having a specific meaning. For example, the first value gives the address of the stack and the second one the address the processor jumps to right after reset. This list of addresses is called the [ISR table](https://github.com/numworks/epsilon/blob/master/ion/src/device/shared/boot/isr.c). ### Memory layout -Like we saw in the previous paragraph, the MCU has a specific memory layout (for example, Flash starts at address 0x08000000) and expects certain values at certain addresses. To ensure the firmware is laid out in memory exactly how the processor expects it, we use a custom linker script. +Like we saw in the previous paragraph, the MCU has a specific memory layout (for example, Flash starts at address 0x08000000) and expects certain values at certain addresses. To ensure the firmware is laid out in memory exactly how the processor expects it, we use a custom [linker script](https://github.com/numworks/epsilon/blob/master/ion/src/device/n0110/flash.ld). ## Simulator -The simulator platform implements Ion calls using the FLTK library. The result is a native GUI program that can run under a variety of operating systems such as Windows, macOS or most Linux distributions. +The simulator platform implements Ion calls using the [SDL](https://www.libsdl.org/) library. The result is a native GUI program that can run under a variety of operating systems such as Windows, macOS or most Linux distributions. It's very practical to code using the simulator, but one has to pay attention to the differences from an actual device: it'll be significantly faster, and will have a lot more memory. So code written using the simulator should always be thoroughly tested on an actual device. ## Emscripten -The emscripten platform implements Ion calls for a Web browser. This lets us build a version of Epsilon that can run directly in a browser, as shown in our online simulator. The C++ code is transpiled to JavaScript using Emscripten, and then packaged in a webpage. +The emscripten platform implements Ion calls for a Web browser. This lets us build a version of Epsilon that can run directly in a browser, as shown in our [online simulator](https://www.numworks.com/simulator/). The C++ code is transpiled to JavaScript using Emscripten, and then packaged in a webpage. Building on Emscripten takes quite a lot of time so you will most likely not want to use it for development purposes. But obviously it's a very neat feature for end users who can give the calculator a spin straight from their browser. diff --git a/docs/poincare/expression_tree.svg b/docs/poincare/expression-tree.svg similarity index 100% rename from docs/poincare/expression_tree.svg rename to docs/poincare/expression-tree.svg diff --git a/docs/poincare/index.md b/docs/poincare/index.md index f1ada84eb..0e08b9cb2 100644 --- a/docs/poincare/index.md +++ b/docs/poincare/index.md @@ -1,5 +1,5 @@ --- -title: Poincare +title: Poincaré --- # Poincaré @@ -9,7 +9,7 @@ Poincare takes text input such as `1+2*3` and turns it into a tree structure, th Each node of a tree represents either an operator or a value. All nodes have a type (`Type::Addition`, `Type::Multiplication`...) and some also store a value (ie `Type::Rational`). -![A example expression tree of Epsilon software](<%= p "expression_tree.svg" %>){:class="img-right"} +![A example expression tree of Epsilon software](<%= p "expression-tree.svg" %>){:class="img-right"} According to their types, expressions are childless (`Type::Rational`) or store pointers to their children (we call those children operands). To ease tree traversal, each node also keeps a pointer to its parent: that information is somewhat redundant but makes dealing with the expression tree much easier. `Multiplication` and `Addition` are the only type that can hold an infinite number of operands. Other expressions have a fixed number of operands: for instance, an `AbsoluteValue` will only ever have one child. ## RTTI: Run-time type information @@ -43,7 +43,6 @@ To sort those operands, we defined an order on expressions with the following fe * The order relationship is depth-first recursive: if two expressions are equal in type and values, we compare their operands starting with the last. * To compare two expressions, we first sort their commutative children to ensure the unicity of expression representations. This guarantees that the order is total on expressions. - ![Order relationship on expressions](<%= p "order.svg" %>){:class="img-responsive"} In the example, both root nodes are r so we compare their last operands. Both are equal to $$\pi$$ so we compare the next operands. As 3 > 2, we can conclude on the order relation between the expressions. diff --git a/escher/include/escher/transparent_view.h b/escher/include/escher/transparent_view.h index 582e20eac..6f7865f54 100644 --- a/escher/include/escher/transparent_view.h +++ b/escher/include/escher/transparent_view.h @@ -4,7 +4,7 @@ #include class TransparentView : public View { -public: +protected: void markRectAsDirty(KDRect rect) override; }; diff --git a/escher/src/text_area.cpp b/escher/src/text_area.cpp index e549b9dc5..837e341b7 100644 --- a/escher/src/text_area.cpp +++ b/escher/src/text_area.cpp @@ -116,14 +116,18 @@ bool TextArea::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::ShiftUp || event == Ion::Events::ShiftDown) { selectUpDown(event == Ion::Events::ShiftUp); return true; - } else if (event == Ion::Events::ShiftLeft) { + } else if (event == Ion::Events::AlphaLeft) { contentView()->moveCursorGeo(-INT_MAX/2, 0); - } else if (event == Ion::Events::ShiftRight) { + TextInput::scrollToCursor(); + } else if (event == Ion::Events::AlphaRight) { contentView()->moveCursorGeo(INT_MAX/2, 0); - } else if (event == Ion::Events::ShiftUp) { + TextInput::scrollToCursor(); + } else if (event == Ion::Events::AlphaUp) { contentView()->moveCursorGeo(0, -INT_MAX/2); - } else if (event == Ion::Events::ShiftDown) { + TextInput::scrollToCursor(); + } else if (event == Ion::Events::AlphaDown) { contentView()->moveCursorGeo(0, INT_MAX/2); + TextInput::scrollToCursor(); } else if (event == Ion::Events::Left) { if (contentView()->resetSelection()) { return true; diff --git a/escher/src/transparent_view.cpp b/escher/src/transparent_view.cpp index 9f85ecd80..631910362 100644 --- a/escher/src/transparent_view.cpp +++ b/escher/src/transparent_view.cpp @@ -2,7 +2,7 @@ void TransparentView::markRectAsDirty(KDRect rect) { if (m_superview) { - m_superview->markRectAsDirty(KDRect(rect.translatedBy(m_frame.origin()))); + m_superview->markRectAsDirty(rect.translatedBy(m_frame.origin())); } View::markRectAsDirty(rect); } diff --git a/ion/include/ion/events.h b/ion/include/ion/events.h index 1d4cee1b8..d02e0e6c4 100644 --- a/ion/include/ion/events.h +++ b/ion/include/ion/events.h @@ -53,6 +53,7 @@ Event getEvent(int * timeout); ShiftAlphaStatus shiftAlphaStatus(); void setShiftAlphaStatus(ShiftAlphaStatus s); +void removeShift(); bool isShiftActive(); bool isAlphaActive(); bool isLockActive(); @@ -182,6 +183,11 @@ constexpr Event ShiftThree = Event::ShiftKey(Keyboard::Key::Three); // Alpha +constexpr Event AlphaLeft = Event::AlphaKey(Keyboard::Key::Left); +constexpr Event AlphaRight = Event::AlphaKey(Keyboard::Key::Right); +constexpr Event AlphaUp = Event::AlphaKey(Keyboard::Key::Up); +constexpr Event AlphaDown = Event::AlphaKey(Keyboard::Key::Down); + constexpr Event Colon = Event::AlphaKey(Keyboard::Key::XNT); constexpr Event SemiColon = Event::AlphaKey(Keyboard::Key::Var); constexpr Event DoubleQuotes = Event::AlphaKey(Keyboard::Key::Toolbox); diff --git a/ion/include/ion/keyboard/layout_B2/layout_events.h b/ion/include/ion/keyboard/layout_B2/layout_events.h index f6c773b79..b67988907 100644 --- a/ion/include/ion/keyboard/layout_B2/layout_events.h +++ b/ion/include/ion/keyboard/layout_B2/layout_events.h @@ -30,7 +30,7 @@ static constexpr EventData s_dataForEvent[4*Event::PageSize] = { TL(), TL(), TL(), TL(), TL(), U(), TL(), TL(), TL(), TL(), U(), U(), // Alpha - U(), U(), U(), U(), U(), U(), + TL(), TL(), TL(), TL(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), T(":"), T(";"), T("\""), T("%"), T("a"), T("b"), T("c"), T("d"), T("e"), T("f"), @@ -75,7 +75,7 @@ static constexpr const char * s_nameForEvent[255] = { nullptr, nullptr, nullptr, "BrightnessPlus", "BrightnessMinus", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, //Alpha, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + "AlphaLeft", "AlphaUp", "AlphaDown", "AlphaRight", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "Colon", "SemiColon", "DoubleQuotes", "Percent", "LowerA", "LowerB", "LowerC", "LowerD", "LowerE", "LowerF", diff --git a/ion/include/ion/keyboard/layout_B3/layout_events.h b/ion/include/ion/keyboard/layout_B3/layout_events.h index 447881bfc..8124c3e9d 100644 --- a/ion/include/ion/keyboard/layout_B3/layout_events.h +++ b/ion/include/ion/keyboard/layout_B3/layout_events.h @@ -30,7 +30,7 @@ static constexpr EventData s_dataForEvent[4*Event::PageSize] = { TL(), TL(), TL(), TL(), TL(), U(), TL(), TL(), TL(), TL(), U(), U(), // Alpha - U(), U(), U(), U(), U(), U(), + TL(), TL(), TL(), TL(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), T(":"), T(";"), T("\""), T("%"), T("a"), T("b"), T("c"), T("d"), T("e"), T("f"), @@ -75,7 +75,7 @@ static constexpr const char * s_nameForEvent[255] = { nullptr, nullptr, nullptr, "BrightnessPlus", "BrightnessMinus", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, //Alpha, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + "AlphaLeft", "AlphaUp", "AlphaDown", "AlphaRight", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "Colon", "SemiColon", "DoubleQuotes", "Percent", "LowerA", "LowerB", "LowerC", "LowerD", "LowerE", "LowerF", diff --git a/ion/src/device/shared/usb/stack/endpoint0.cpp b/ion/src/device/shared/usb/stack/endpoint0.cpp index 14478a086..4e1d4e980 100644 --- a/ion/src/device/shared/usb/stack/endpoint0.cpp +++ b/ion/src/device/shared/usb/stack/endpoint0.cpp @@ -259,7 +259,7 @@ void Endpoint0::clearForOutTransactions(uint16_t wLength) { setOutNAK(false); } -uint16_t Endpoint0::receiveSomeData() { +int Endpoint0::receiveSomeData() { // If it is the first chunk of data to be received, m_transferBufferLength is 0. uint16_t packetSize = MIN(k_maxPacketSize, m_request.wLength() - m_transferBufferLength); uint16_t sizeOfPacketRead = readPacket(m_largeBuffer + m_transferBufferLength, packetSize); diff --git a/ion/src/device/shared/usb/stack/endpoint0.h b/ion/src/device/shared/usb/stack/endpoint0.h index ca6809bfb..a0ef41686 100644 --- a/ion/src/device/shared/usb/stack/endpoint0.h +++ b/ion/src/device/shared/usb/stack/endpoint0.h @@ -57,7 +57,7 @@ public: void clearForOutTransactions(uint16_t wLength); private: - uint16_t receiveSomeData(); + int receiveSomeData(); uint16_t readPacket(void * buffer, uint16_t length); uint16_t writePacket(const void * buffer, uint16_t length); diff --git a/ion/src/shared/events_modifier.cpp b/ion/src/shared/events_modifier.cpp index 512c12540..54ba7d4c7 100644 --- a/ion/src/shared/events_modifier.cpp +++ b/ion/src/shared/events_modifier.cpp @@ -11,6 +11,16 @@ ShiftAlphaStatus shiftAlphaStatus() { return sShiftAlphaStatus; } +void removeShift() { + if (sShiftAlphaStatus == ShiftAlphaStatus::Shift) { + sShiftAlphaStatus = ShiftAlphaStatus::Default; + } else if (sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlpha ) { + sShiftAlphaStatus = ShiftAlphaStatus::Alpha; + } else if (sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlphaLock) { + sShiftAlphaStatus = ShiftAlphaStatus::AlphaLock; + } +} + bool isShiftActive() { return sShiftAlphaStatus == ShiftAlphaStatus::Shift || sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlpha || sShiftAlphaStatus == ShiftAlphaStatus::ShiftAlphaLock; } diff --git a/ion/src/simulator/android/.gradle/5.2.1/fileChanges/last-build.bin b/ion/src/simulator/android/.gradle/5.2.1/fileChanges/last-build.bin deleted file mode 100644 index f76dd238a..000000000 Binary files a/ion/src/simulator/android/.gradle/5.2.1/fileChanges/last-build.bin and /dev/null differ diff --git a/ion/src/simulator/android/.gradle/5.2.1/fileHashes/fileHashes.lock b/ion/src/simulator/android/.gradle/5.2.1/fileHashes/fileHashes.lock deleted file mode 100644 index bc5ad9eb0..000000000 Binary files a/ion/src/simulator/android/.gradle/5.2.1/fileHashes/fileHashes.lock and /dev/null differ diff --git a/ion/src/simulator/android/.gradle/5.2.1/gc.properties b/ion/src/simulator/android/.gradle/5.2.1/gc.properties deleted file mode 100644 index e69de29bb..000000000 diff --git a/ion/src/simulator/android/Makefile b/ion/src/simulator/android/Makefile index 3b56131b5..0e6e70184 100644 --- a/ion/src/simulator/android/Makefile +++ b/ion/src/simulator/android/Makefile @@ -34,10 +34,15 @@ $(BUILD_DIR)/app/res/%.xml: ion/src/simulator/android/src/res/%.xml | $$(@D)/. .PHONY: force_remake -$(BUILD_DIR)/app/libs/%/libepsilon.so: force_remake $$(@D)/. - $(Q) echo "MAKE NDK_ABI=$*" - $(Q) $(MAKE) NDK_ABI=$* epsilon.so - $(Q) cp $(BUILD_DIR)/$*/epsilon.so $@ +define rule_for_libepsilon +$$(BUILD_DIR)/app/libs/%/lib$(1): force_remake $$$$(@D)/. + $(Q) echo "MAKE NDK_ABI=$$*" + $(Q) $$(MAKE) NDK_ABI=$$* $(1) + $(Q) cp $$(BUILD_DIR)/$$*/$(1) $$@ +endef + +$(eval $(call rule_for_libepsilon,epsilon.so)) +$(eval $(call rule_for_libepsilon,epsilon.official.so)) # If NDK_ABI is not defined, we will re-trigger a build for each avaialble ABI. # This is used to build APKs, which needs to embbed a binary for each ABI. @@ -46,22 +51,28 @@ ifndef NDK_ABI NDK_ABIS = armeabi-v7a arm64-v8a x86 x86_64 -epsilon_apk_deps = $(patsubst %,$(BUILD_DIR)/app/libs/%/libepsilon.so,$(NDK_ABIS)) -epsilon_apk_deps += $(subst ion/src/simulator/android/src/res,$(BUILD_DIR)/app/res,$(wildcard ion/src/simulator/android/src/res/*/*)) +epsilon_apk_deps = $(subst ion/src/simulator/android/src/res,$(BUILD_DIR)/app/res,$(wildcard ion/src/simulator/android/src/res/*/*)) epsilon_apk_deps += $(addprefix $(BUILD_DIR)/app/res/,mipmap/ic_launcher.png mipmap-v26/ic_launcher_foreground.png) -.PHONY: gradle_% -gradle_%: $(epsilon_apk_deps) +define rule_for_gradle +.PHONY: gradle_$1_$2 +gradle_$1_$2: $$(epsilon_apk_deps) $$(patsubst %,$$(BUILD_DIR)/app/libs/%/libepsilon$2so,$(NDK_ABIS)) @echo "GRADLE ion/src/simulator/android/build.gradle" - $(Q) ANDROID_HOME=$(ANDROID_HOME) EPSILON_VERSION=$(EPSILON_VERSION) BUILD_DIR=$(BUILD_DIR) ion/src/simulator/android/gradlew -b ion/src/simulator/android/build.gradle $* + $(Q) ANDROID_HOME=$(ANDROID_HOME) EPSILON_VERSION=$(EPSILON_VERSION) BUILD_DIR=$(BUILD_DIR) ion/src/simulator/android/gradlew -b ion/src/simulator/android/build.gradle $1 +endef + +$(eval $(call rule_for_gradle,assembleCodesigned,.)) +$(eval $(call rule_for_gradle,assembleRelease,.)) +$(eval $(call rule_for_gradle,assembleCodesigned,.official.)) +$(eval $(call rule_for_gradle,assembleRelease,.official.)) DEFAULT = epsilon.apk -.PHONY: epsilon.apk +.PHONY: epsilon%apk ifdef ANDROID_SIGNING_STORE_FILE -epsilon.apk: gradle_assembleCodesigned +epsilon%apk: gradle_assembleCodesigned_% else -epsilon.apk: gradle_assembleRelease +epsilon%apk: gradle_assembleRelease_% $(warning Building without code signing. Define ANDROID_SIGNING_STORE_FILE, ANDROID_SIGNING_STORE_PASSWORD, ANDROID_SIGNING_KEY_ALIAS and ANDROID_SIGNING_KEY_PASSWORD for a signed build.) endif diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index 876963d03..c19966b28 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -35,7 +35,7 @@ SIMULATOR_ICONSET = $(SIMULATOR_ASSETS_PATH)/AppIcon.appiconset include ion/src/simulator/shared/apple/Makefile -$(call simulator_app_plist,Info.plist): ion/src/simulator/ios/Info.plist $(call simulator_app_resource,Assets.car) +$(call simulator_app_plist,%,Info.plist): ion/src/simulator/ios/Info.plist $(call simulator_app_resource,%,Assets.car) $(call rule_label,PLUTIL) $(Q) cp $< $@ $(Q) plutil -insert "BuildMachineOSBuild" -string "$(IOS_BUILD_MACHINE_OS_BUILD)" $@ @@ -54,22 +54,23 @@ $(call simulator_app_plist,Info.plist): ion/src/simulator/ios/Info.plist $(call $(Q) plutil -replace CFBundleIcons -json `plutil -extract CFBundleIcons json -o - $(BUILD_DIR)/app/assets/partial.plist` $@ $(Q) plutil -replace CFBundleIcons~ipad -json `plutil -extract CFBundleIcons~ipad json -o - $(BUILD_DIR)/app/assets/partial.plist` $@ -$(call simulator_app_resource,launch.storyboardc): ion/src/simulator/ios/launch.storyboard | $$(@D)/. +$(call simulator_app_resource,%,launch.storyboardc): ion/src/simulator/ios/launch.storyboard | $$(@D)/. $(call rule_label,IBTOOL) $(Q) $(IBTOOL) --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --compile $@ $^ ifdef IOS_PROVISIONNING_PROFILE -$(call simulator_app_resource,embedded.mobileprovision): $(IOS_PROVISIONNING_PROFILE) | $$(@D)/. +$(call simulator_app_resource,%,embedded.mobileprovision): $(IOS_PROVISIONNING_PROFILE) | $$(@D)/. $(call rule_label,COPY) $(Q) cp $^ $@ -$(BUILD_DIR)/app/entitlements.plist: $(call simulator_app_resource,embedded.mobileprovision) +$(BUILD_DIR)/app/entitlements.plist: $(IOS_PROVISIONNING_PROFILE) $(call rule_label,SCMS) $(Q) security cms -D -i $(IOS_PROVISIONNING_PROFILE) | plutil -extract Entitlements xml1 - -o $@ -SIMULATOR_APP_DEPS += $(BUILD_DIR)/app/entitlements.plist +simulator_app_deps += $(BUILD_DIR)/app/entitlements.plist +simulator_app_deps += $(call simulator_app_resource,$1,embedded.mobileprovision) else -$(call simulator_app_resource,embedded.mobileprovision): +$(call simulator_app_resource,%,embedded.mobileprovision): $(warning Building without a provisionning profile. Please define IOS_PROVISIONNING_PROFILE to point to the .mobileprovision file you want to use.) endif @@ -77,19 +78,19 @@ $(SIMULATOR_ICONSET)/Contents.json: ion/src/simulator/ios/icon_assets.json $(SIM $(call rule_label,COPY) $(Q) cp $< $@ -$(call simulator_app_resource,Assets.car): $(SIMULATOR_ICONSET)/Contents.json | $$(@D)/. +$(call simulator_app_resource,%,Assets.car): $(SIMULATOR_ICONSET)/Contents.json | $$(@D)/. $(call rule_label,ACTOOL) - $(Q) $(ACTOOL) --compile $(BUILD_DIR)/app/Payload/Epsilon.app --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --platform $(APPLE_SDK) --app-icon AppIcon --output-partial-info-plist $(BUILD_DIR)/app/assets/partial.plist $(SIMULATOR_ASSETS_PATH) > /dev/null + $(Q) $(ACTOOL) --compile $(BUILD_DIR)/app/Payload/$*.app --minimum-deployment-target $(APPLE_PLATFORM_MIN_VERSION) --platform $(APPLE_SDK) --app-icon AppIcon --output-partial-info-plist $(BUILD_DIR)/app/assets/partial.plist $(SIMULATOR_ASSETS_PATH) > /dev/null -SIMULATOR_APP_DEPS += $(call simulator_app_resource,\ +simulator_app_deps += $(call simulator_app_resource,$(1), \ Assets.car \ launch.storyboardc \ ) -$(BUILD_DIR)/app/epsilon.ipa: $(SIMULATOR_APP_DEPS) +$(BUILD_DIR)/app/epsilon%ipa: $(subst ..,.,$(call simulator_app_deps,Epsilon$*)) ifdef IOS_PROVISIONNING_PROFILE $(call rule_label,SIGN) - $(Q) codesign --force --entitlements $(BUILD_DIR)/app/entitlements.plist --sign "iPhone Distribution: NumWorks" $(BUILD_DIR)/app/Payload/Epsilon.app + $(Q) codesign --force --entitlements $(BUILD_DIR)/app/entitlements.plist --sign "Apple Distribution: NumWorks" $(BUILD_DIR)/app/Payload/Epsilon.app endif $(call rule_label,ZIP) $(Q) cd $(dir $@) ; zip -qr9 $(notdir $@) Payload @@ -99,7 +100,7 @@ DEFAULT := $(BUILD_DIR)/app/epsilon.ipa endif ifeq ($(APPLE_PLATFORM),ios-simulator) -.PHONY: epsilon_run -epsilon_run: $(SIMULATOR_APP_DEPS) - xcrun simctl install booted $(BUILD_DIR)/app/Payload/Epsilon.app +.PHONY: epsilon%run +epsilon%run: $(subst _.,.,$(call simulator_app_deps,Epsilon$*)) + xcrun simctl install booted $(BUILD_DIR)/app/Payload/Epsilon$(subst _,.,$*)app endif diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index ae25b03f3..0f27650df 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -22,7 +22,7 @@ SIMULATOR_ICONSET = $(BUILD_DIR)/app/assets/app.iconset include ion/src/simulator/shared/apple/Makefile -$(call simulator_app_plist,Info.plist): ion/src/simulator/macos/Info.plist +$(call simulator_app_plist,%,Info.plist): ion/src/simulator/macos/Info.plist $(call rule_label,PLUTIL) $(Q) cp $< $@ $(Q) plutil -insert "LSMinimumSystemVersion" -string "$(MACOS_MIN_VERSION)" $@ @@ -31,12 +31,21 @@ $(call simulator_app_plist,Info.plist): ion/src/simulator/macos/Info.plist # macOS uses icns files -$(call simulator_app_resource,app.icns): $(SIMULATOR_ICONS) | $$(@D)/. +.SECONDARY: $(SIMULATOR_ICONS) | $$(@D)/. + +$(call simulator_app_resource,%,app.icns): $(SIMULATOR_ICONS) | $$(@D)/. $(call rule_label,ICNUTIL) $(Q) iconutil --convert icns --output $@ $(SIMULATOR_ICONSET) -SIMULATOR_APP_DEPS += $(call simulator_app_resource,app.icns) +simulator_app_deps += $(call simulator_app_resource,$(1),app.icns) + +simulator_app_deps_unofficial = $(call simulator_app_deps,Epsilon) +simulator_app_deps_official = $(call simulator_app_deps,Epsilon.official) + +.PHONY: Epsilon.app Epsilon.official.app +Epsilon.app: $(simulator_app_deps_unofficial) +Epsilon.official.app: $(simulator_app_deps_official) ifndef ARCH -DEFAULT := $(SIMULATOR_APP_DEPS) +DEFAULT := Epsilon.app endif diff --git a/ion/src/simulator/shared/apple/Makefile b/ion/src/simulator/shared/apple/Makefile index f5b1c77fe..22faf47d7 100644 --- a/ion/src/simulator/shared/apple/Makefile +++ b/ion/src/simulator/shared/apple/Makefile @@ -2,27 +2,37 @@ # The only things that have to be customized per platform are the icons and the # Info.plist. -SIMULATOR_APP_PATH = $(BUILD_DIR)/app/Payload/Epsilon.app +SIMULATOR_APP_PATH = $(BUILD_DIR)/app/Payload -simulator_app_binary = $(addprefix $(SIMULATOR_APP_PATH)/$(SIMULATOR_APP_BINARY_PATH),$(1)) -simulator_app_resource = $(addprefix $(SIMULATOR_APP_PATH)/$(SIMULATOR_APP_RESOURCE_PATH),$(1)) -simulator_app_plist = $(addprefix $(SIMULATOR_APP_PATH)/$(SIMULATOR_APP_PLIST_PATH),$(1)) +simulator_app_binary = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_BINARY_PATH),$(2)) +simulator_app_resource = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_RESOURCE_PATH),$(2)) +simulator_app_plist = $(addprefix $(SIMULATOR_APP_PATH)/$(1).app/$(SIMULATOR_APP_PLIST_PATH),$(2)) # Epsilon binary .PHONY: force_remake -$(BUILD_DIR)/%/epsilon.bin: force_remake - $(Q) echo "MAKE ARCH=$*" - $(Q) $(MAKE) ARCH=$* +define rule_for_epsilon +$$(BUILD_DIR)/%/$(1): force_remake + $(Q) echo "MAKE ARCH=$$*" + $(Q) $$(MAKE) ARCH=$$* $(1) +endef -$(call simulator_app_binary,Epsilon): $(patsubst %,$(BUILD_DIR)/%/epsilon.bin,$(ARCHS)) | $$(@D)/. - $(call rule_label,LIPO) - $(Q) $(LIPO) -create $^ -output $@ +$(eval $(call rule_for_epsilon,epsilon.bin)) +$(eval $(call rule_for_epsilon,epsilon.official.bin)) + +define rule_for_lipo +$$(call simulator_app_binary,$1,Epsilon): $$(patsubst %,$(BUILD_DIR)/%/$2.bin,$$(ARCHS)) | $$$$(@D)/. + $$(call rule_label,LIPO) + $(Q) $$(LIPO) -create $$^ -output $$@ +endef + +$(eval $(call rule_for_lipo,Epsilon,epsilon)) +$(eval $(call rule_for_lipo,Epsilon.official,epsilon.official)) # Background image -$(call simulator_app_resource,background.jpg): ion/src/simulator/assets/background.jpg | $$(@D)/. +$(call simulator_app_resource,%,background.jpg): ion/src/simulator/assets/background.jpg | $$(@D)/. $(call rule_label,COPY) $(Q) cp $^ $@ @@ -44,6 +54,6 @@ $(addprefix $(SIMULATOR_ICONSET)/,icon_%.png): ion/src/simulator/assets/logo.svg # Export simulator app dependencies -SIMULATOR_APP_DEPS += $(call simulator_app_binary,Epsilon) -SIMULATOR_APP_DEPS += $(call simulator_app_plist,Info.plist) -SIMULATOR_APP_DEPS += $(call simulator_app_resource,background.jpg) +simulator_app_deps += $(call simulator_app_binary,$(1),Epsilon) +simulator_app_deps += $(call simulator_app_plist,$(1),Info.plist) +simulator_app_deps += $(call simulator_app_resource,$(1),background.jpg) diff --git a/ion/src/simulator/shared/events_keyboard.cpp b/ion/src/simulator/shared/events_keyboard.cpp index 02bd8b4a1..61fe0f972 100644 --- a/ion/src/simulator/shared/events_keyboard.cpp +++ b/ion/src/simulator/shared/events_keyboard.cpp @@ -64,10 +64,14 @@ namespace Ion { namespace Events { static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) { + /* If an event is detected, we want to remove the Shift modifier to mimic the + * device behaviour. If no event was detected, we restore the previous + * ShiftAlphaStatus. */ + Ion::Events::ShiftAlphaStatus previousShiftAlphaStatus = Ion::Events::shiftAlphaStatus(); + Ion::Events::removeShift(); + if (event.keysym.mod & KMOD_CTRL) { switch (event.keysym.sym) { - case SDLK_BACKSPACE: - return Clear; case SDLK_x: return Cut; case SDLK_c: @@ -88,14 +92,8 @@ static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) { } } switch (event.keysym.sym) { - case SDLK_ESCAPE: - return Home; - case SDLK_RETURN: - return OK; case SDLK_v: return Var; - case SDLK_BACKSPACE: - return Clear; case SDLK_x: return Exp; case SDLK_n: @@ -124,38 +122,12 @@ static Event eventFromSDLKeyboardEvent(SDL_KeyboardEvent event) { return Ans; } } - if (event.keysym.mod & KMOD_SHIFT) { - switch(event.keysym.sym) { - case SDLK_UP: - return ShiftUp; - case SDLK_DOWN: - return ShiftDown; - case SDLK_LEFT: - return ShiftLeft; - case SDLK_RIGHT: - return ShiftRight; - } - } switch(event.keysym.sym) { - case SDLK_UP: - return Up; - case SDLK_DOWN: - return Down; - case SDLK_LEFT: - return Left; - case SDLK_RIGHT: - return Right; - case SDLK_RETURN: - return EXE; - case SDLK_ESCAPE: - return Back; - case SDLK_TAB: - return Toolbox; - case SDLK_BACKSPACE: - return Backspace; case SDLK_AC_BACK: return Termination; } + // No event was detected, restore the previous ShiftAlphaStatus. + Ion::Events::setShiftAlphaStatus(previousShiftAlphaStatus); return None; } @@ -180,6 +152,12 @@ static Event eventFromSDLTextInputEvent(SDL_TextInputEvent event) { } char character = event.text[0]; if (character >= 32 && character < 127) { + /* We remove the shift, otherwise it might stay activated when it shouldn't. + * For instance on a French keyboard, to input "1", we first press "Shift" + * (which activates the Shift modifier on the calculator), then we press + * "&", transformed by eventFromSDLTextInputEvent into the text "1". If we + * do not remove the Shift here, it would still be pressed afterwards. */ + Ion::Events::removeShift(); return sEventForASCIICharAbove32[character-32]; } return None; @@ -193,6 +171,7 @@ Event getPlatformEvent() { } #endif SDL_Event event; + Event result = None; while (SDL_PollEvent(&event)) { // The while is important: it'll do a fast-pass over all useless SDL events if (event.type == SDL_WINDOWEVENT) { @@ -201,16 +180,35 @@ Event getPlatformEvent() { } } if (event.type == SDL_QUIT) { - return Termination; + result = Termination; + break; } if (event.type == SDL_KEYDOWN) { - return eventFromSDLKeyboardEvent(event.key); + if (IonSimulatorSDLKeyDetectedByScan(event.key.keysym.scancode)) { + continue; + } + result = eventFromSDLKeyboardEvent(event.key); + break; } if (event.type == SDL_TEXTINPUT) { - return eventFromSDLTextInputEvent(event.text); + result = eventFromSDLTextInputEvent(event.text); + break; } } - return None; + if (result != None) { + /* When events are not being processed - for instance when a Python script + * is being executed - SDL puts all ingoing events in a queue. + * When events processing goes back to normal, all the queued events are + * processed, which can result in weird behaviours (for instance, really + * fast navigation in the calculator, "instantanate" text input, ...). + * These behaviours are even more visible if the script contains an infinite + * loop. + * To prevent that, we flush all queued events after encountering a non-null + * event -> the first event from the queue will still be processed, but not + * the subsequent ones. */ + SDL_FlushEvents(0, UINT32_MAX); + } + return result; } } diff --git a/ion/src/simulator/shared/keyboard_sdl.cpp b/ion/src/simulator/shared/keyboard_sdl.cpp index 4940c2c98..a1b173719 100644 --- a/ion/src/simulator/shared/keyboard_sdl.cpp +++ b/ion/src/simulator/shared/keyboard_sdl.cpp @@ -22,6 +22,34 @@ void IonSimulatorKeyboardKeyUp(int keyNumber) { namespace Ion { namespace Keyboard { +class KeySDLKeyPair { +public: + constexpr KeySDLKeyPair(Ion::Keyboard::Key key, SDL_Scancode SDLKey) : + m_key(key), + m_SDLKey(SDLKey) + {} + Ion::Keyboard::Key key() const { return m_key; } + SDL_Scancode SDLKey() const { return m_SDLKey; } +private: + Ion::Keyboard::Key m_key; + SDL_Scancode m_SDLKey; +}; + +constexpr static KeySDLKeyPair sKeyPairs[] = { + KeySDLKeyPair(Ion::Keyboard::Key::Down, SDL_SCANCODE_DOWN), + KeySDLKeyPair(Ion::Keyboard::Key::Up, SDL_SCANCODE_UP), + KeySDLKeyPair(Ion::Keyboard::Key::Left, SDL_SCANCODE_LEFT), + KeySDLKeyPair(Ion::Keyboard::Key::Right, SDL_SCANCODE_RIGHT), + KeySDLKeyPair(Ion::Keyboard::Key::Shift, SDL_SCANCODE_LSHIFT), + KeySDLKeyPair(Ion::Keyboard::Key::Shift, SDL_SCANCODE_RSHIFT), + KeySDLKeyPair(Ion::Keyboard::Key::EXE, SDL_SCANCODE_RETURN), + KeySDLKeyPair(Ion::Keyboard::Key::Back, SDL_SCANCODE_ESCAPE), + KeySDLKeyPair(Ion::Keyboard::Key::Toolbox, SDL_SCANCODE_TAB), + KeySDLKeyPair(Ion::Keyboard::Key::Backspace, SDL_SCANCODE_BACKSPACE) +}; + +constexpr int sNumberOfKeyPairs = sizeof(sKeyPairs)/sizeof(KeySDLKeyPair); + State scan() { // We need to tell SDL to get new state from the host OS SDL_PumpEvents(); @@ -32,13 +60,12 @@ State scan() { // Grab this opportunity to refresh the display if needed Simulator::Main::refresh(); -#if EPSILON_SDL_SCREEN_ONLY - // In this case, keyboard states will be sent over another channel - return sKeyboardState; -#else // Start with a "clean" state State state; - +#if EPSILON_SDL_SCREEN_ONLY + // In this case, keyboard states are sent over another channel. + state = sKeyboardState; +#else // Register a key for the mouse, if any SDL_Point p; Uint32 mouseState = SDL_GetMouseState(&p.x, &p.y); @@ -46,10 +73,27 @@ State scan() { Key k = Simulator::Layout::keyAt(&p); state.setKey(k); } - - return state; #endif + + // Catch the physical keyboard events + const uint8_t * SDLstate = SDL_GetKeyboardState(NULL); + for (int i = 0; i < sNumberOfKeyPairs; i++) { + KeySDLKeyPair pair = sKeyPairs[i]; + if (SDLstate[pair.SDLKey()]) { + state.setKey(pair.key()); + } + } + return state; } } } + +bool IonSimulatorSDLKeyDetectedByScan(SDL_Scancode key) { + for (int i = 0; i < Ion::Keyboard::sNumberOfKeyPairs; i++) { + if (key == Ion::Keyboard::sKeyPairs[i].SDLKey()) { + return true; + } + } + return false; +} diff --git a/ion/src/simulator/shared/platform.h b/ion/src/simulator/shared/platform.h index 0dcb4e74c..494c83c23 100644 --- a/ion/src/simulator/shared/platform.h +++ b/ion/src/simulator/shared/platform.h @@ -2,6 +2,7 @@ #define ION_SIMULATOR_PLATFORM_H #include +#include #ifdef __cplusplus extern "C" { @@ -14,14 +15,12 @@ SDL_Texture * IonSimulatorLoadImage(SDL_Renderer * renderer, const char * identi char * IonSimulatorGetLanguageCode(); #if EPSILON_SDL_SCREEN_ONLY - void IonSimulatorKeyboardKeyDown(int keyNumber); void IonSimulatorKeyboardKeyUp(int keyNumber); - void IonSimulatorEventsPushEvent(int eventNumber); - #endif +bool IonSimulatorSDLKeyDetectedByScan(SDL_Scancode key); void IonSimulatorCallbackDidRefresh(); void IonSimulatorCallbackDidScanKeyboard(); diff --git a/ion/src/simulator/web/Makefile b/ion/src/simulator/web/Makefile index ada734ed7..b31be6f0b 100644 --- a/ion/src/simulator/web/Makefile +++ b/ion/src/simulator/web/Makefile @@ -26,7 +26,7 @@ endif DEFAULT = $(BUILD_DIR)/simulator.zip -$(BUILD_DIR)/simulator.zip: $(BUILD_DIR)/epsilon.packed.js +$(BUILD_DIR)/simulator%zip: $(BUILD_DIR)/epsilon%packed.js @rm -rf $(basename $@) @mkdir -p $(basename $@) @cp $^ $(basename $@)/epsilon.js diff --git a/liba/include/math.h b/liba/include/math.h index 7fef24005..3cb95bcb5 100644 --- a/liba/include/math.h +++ b/liba/include/math.h @@ -35,13 +35,13 @@ typedef double double_t; #define FP_SUBNORMAL 0x08 #define FP_ZERO 0x10 -#define fpclassify(x) __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x) -#define signbit(x) __builtin_signbit(x) #define finite(x) __builtin_finite(x) +#define fpclassify(x) __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x) #define isfinite(x) __builtin_isfinite(x) #define isnormal(x) __builtin_isnormal(x) #define isnan(x) __builtin_isnan(x) #define isinf(x) __builtin_isinf(x) +#define signbit(x) __builtin_signbit(x) float acosf(float x); float acoshf(float x); @@ -60,19 +60,19 @@ float fabsf(float x); float fmaxf(float x, float y); float floorf(float x); float fmodf(float x, float y); -float frexpf(float x, int *eptr); +float frexpf(float x, int *exp); float hypotf(float x, float y); -float ldexpf(float x, int n); +float ldexpf(float x, int exp); float lgammaf(float x); float lgammaf_r(float x, int *signgamp); float log1pf(float x); float log10f(float x); float logf(float x); -float modff(float value, float *iptr); +float modff(float x, float *iptr); float nearbyintf(float x); float powf(float x, float y); float roundf(float x); -float scalbnf(float x, int n); +float scalbnf(float x, int exp); float sinf(float x); float sinhf(float x); float sqrtf(float x); @@ -99,7 +99,7 @@ double fabs(double x); double fmax(double x, double y); double floor(double x); double fmod(double x, double y); -double frexp(double x, int *eptr); +double frexp(double x, int *exp); double hypot(double x, double y); double lgamma(double x); double lgamma_r(double x, int *signgamp); @@ -108,14 +108,13 @@ double log1p(double x); double log10(double x); double log2(double x); double logb(double x); -double modf(double value, double *iptr); +double modf(double x, double *iptr); double nearbyint(double x); double pow(double x, double y); -double nearbyint(double x); double rint(double x); double round(double x); -double scalb(double x, double fn); -double scalbn(double x, int n); +double scalb(double x, double exp); +double scalbn(double x, int exp); double sin(double x); double sinh(double x); double sqrt(double x); @@ -124,6 +123,8 @@ double tanh(double x); double tgamma(double x); double trunc(double x); +extern int signgam; + /* The C99 standard says that any libc function can be re-declared as a macro. * (See N1124 paragraph 7.1.4). This means that C files willing to actually * implement said functions should either re-define the prototype or #undef the @@ -145,18 +146,19 @@ double trunc(double x); #define fabsf(x) __builtin_fabsf(x) #define floorf(x) __builtin_floorf(x) #define fmodf(x, y) __builtin_fmodf(x, y) -#define frexpf(x, y) __builtin_frexpf(x, y) -#define ldexpf(x, n) __builtin_ldexpf(x, n) +#define frexpf(x, exp) __builtin_frexpf(x, exp) +#define ldexpf(x, exp) __builtin_ldexpf(x, exp) #define lgammaf(x) __builtin_lgammaf(x) #define lgammaf_r(x, signgamp) __builtin_lgammaf_r(x, signgamp) #define log1pf(x) __builtin_log1pf(x) #define log10f(x) __builtin_log10f(x) #define logf(x) __builtin_logf(x) -#define nanf(s) __builtin_nanf(s) +#define modff(x, iptr) __builtin_modff(x, iptr) +#define nanf(tagp) __builtin_nanf(tagp) #define nearbyintf(x) __builtin_nearbyintf(x) #define powf(x, y) __builtin_powf(x, y) #define roundf(x) __builtin_roundf(x) -#define scalbnf(x, n) __builtin_scalbnf(x, n) +#define scalbnf(x, exp) __builtin_scalbnf(x, exp) #define sinf(x) __builtin_sinf(x) #define sinhf(x) __builtin_sinhf(x) #define sqrtf(x) __builtin_sqrtf(x) @@ -182,7 +184,8 @@ double trunc(double x); #define fabs(x) __builtin_fabs(x) #define floor(x) __builtin_floor(x) #define fmod(x, y) __builtin_fmod(x, y) -#define ldexp(x, n) __builtin_scalbn(x, n) +#define frexp(x, exp) __builtin_frexp(x, exp) +#define ldexp(x, exp) __builtin_scalbn(x, exp) #define lgamma(x) __builtin_lgamma(x) #define lgamma_r(x, signgamp) __builtin_lgamma_r(x, signgamp) #define log(x) __builtin_log(x) @@ -190,11 +193,14 @@ double trunc(double x); #define log10(x) __builtin_log10(x) #define log2(x) __builtin_log2(x) #define logb(x) __builtin_logb(x) -#define nan(s) __builtin_nan(s) +#define modf(x, iptr) __builtin_modf(x, iptr) +#define nan(tagp) __builtin_nan(tagp) +#define nearbyint(x) __builtin_nearbyint(x) #define pow(x, y) __builtin_pow(x, y) #define rint(x) __builtin_rint(x) #define round(x) __builtin_round(x) -#define scalbn(x, n) __builtin_scalbn(x, n) +#define scalb(x, exp) __builtin_scalb(x, exp) +#define scalbn(x, exp) __builtin_scalbn(x, exp) #define sin(x) __builtin_sin(x) #define sinh(x) __builtin_sinh(x) #define sqrt(x) __builtin_sqrt(x) @@ -203,8 +209,6 @@ double trunc(double x); #define tgamma(x) __builtin_tgamma(x) #define trunc(x) __builtin_trunc(x) -extern int signgam; - LIBA_END_DECLS #endif diff --git a/liba/test/math.c b/liba/test/math.c index 64f874df8..c09fc30f2 100644 --- a/liba/test/math.c +++ b/liba/test/math.c @@ -1,210 +1,97 @@ // This file tests that each math fuction links. #include -int test_fpclassifyf(float x) { - return fpclassify(x); -} -int test_isfinitef(float x) { - return isfinite(x); -} -int test_isnormalf(float x) { - return isnormal(x); -} -int test_isnanf(float x) { - return isnan(x); -} -int test_isinff(float x) { - return isinf(x); -} +int test_fpclassifyf(float x) { return fpclassify(x); } +int test_signbitf(float x) { return signbit(x); } +int test_finitef(float x) { return finite(x); } +int test_isfinitef(float x) { return isfinite(x); } +int test_isnormalf(float x) { return isnormal(x); } +int test_isnanf(float x) { return isnan(x); } +int test_isinff(float x) { return isinf(x); } -float test_acosf(float x) { - return acosf(x); -} -float test_acoshf(float x) { - return acoshf(x); -} -float test_asinf(float x) { - return asinf(x); -} -float test_asinhf(float x) { - return asinhf(x); -} -float test_atanf(float x) { - return atanf(x); -} -float test_atan2f(float y, float x) { - return atan2f(y, x); -} -float test_atanhf(float x) { - return atanhf(x); -} -float test_ceilf(float x) { - return ceilf(x); -} -float test_copysignf(float x, float y) { - return copysignf(x, y); -} -float test_cosf(float x) { - return cosf(x); -} -float test_coshf(float x) { - return coshf(x); -} -float test_expf(float x) { - return expf(x); -} -float test_expm1f(float x) { - return expm1f(x); -} -float test_fabsf(float x) { - return fabsf(x); -} -float test_floorf(float x) { - return floorf(x); -} -float test_fmodf(float x, float y) { - return fmodf(x, y); -} -float test_lgammaf(float x) { - return lgammaf(x); -} -float test_lgammaf_r(float x, int *signgamp) { - return lgammaf_r(x, signgamp); -} -float test_log1pf(float x) { - return log1pf(x); -} -float test_log10f(float x) { - return log10f(x); -} -float test_logf(float x) { - return logf(x); -} -float test_nanf(const char *s) { - return nanf(s); -} -float test_nearbyintf(float x) { - return nearbyintf(x); -} -float test_powf(float x, float y) { - return powf(x, y); -} -float test_roundf(float x) { - return roundf(x); -} -float test_scalbnf(float x, int n) { - return scalbnf(x, n); -} -float test_sinf(float x) { - return sinf(x); -} -float test_sinhf(float x) { - return sinhf(x); -} -float test_sqrtf(float x) { - return sqrtf(x); -} -float test_tanf(float x) { - return tanf(x); -} -float test_tanhf(float x) { - return tanhf(x); -} +float test_acosf(float x) { return acosf(x); } +float test_acoshf(float x) { return acoshf(x); } +float test_asinf(float x) { return asinf(x); } +float test_asinhf(float x) { return asinhf(x); } +float test_atanf(float x) { return atanf(x); } +float test_atan2f(float y, float x) { return atan2f(y, x); } +float test_atanhf(float x) { return atanhf(x); } +float test_ceilf(float x) { return ceilf(x); } +float test_copysignf(float x, float y) { return copysignf(x, y); } +float test_cosf(float x) { return cosf(x); } +float test_coshf(float x) { return coshf(x); } +float test_expf(float x) { return expf(x); } +float test_expm1f(float x) { return expm1f(x); } +float test_fabsf(float x) { return fabsf(x); } +float test_floorf(float x) { return floorf(x); } +float test_fmodf(float x, float y) { return fmodf(x, y); } +float test_frexpf(float x, int *exp) { return frexpf(x, exp); } +float test_ldexpf(float x, int exp) { return ldexpf(x, exp); } +float test_lgammaf(float x) { return lgammaf(x); } +float test_lgammaf_r(float x, int *signgamp) { return lgammaf_r(x, signgamp); } +float test_log1pf(float x) { return log1pf(x); } +float test_log10f(float x) { return log10f(x); } +float test_logf(float x) { return logf(x); } +float test_modff(float x, float *iptr) { return modff(x, iptr); } +float test_nanf(const char *s) { return nanf(s); } +float test_nearbyintf(float x) { return nearbyintf(x); } +float test_powf(float x, float y) { return powf(x, y); } +float test_roundf(float x) { return roundf(x); } +float test_scalbnf(float x, int exp) { return scalbnf(x, exp); } +float test_sinf(float x) { return sinf(x); } +float test_sinhf(float x) { return sinhf(x); } +float test_sqrtf(float x) { return sqrtf(x); } +float test_tanf(float x) { return tanf(x); } +float test_tanhf(float x) { return tanhf(x); } +float test_truncf(float x) { return truncf(x); } -int test_fpclassify(double x) { - return fpclassify(x); -} -int test_isfinite(double x) { - return isfinite(x); -} -int test_isnormal(double x) { - return isnormal(x); -} -int test_isnan(double x) { - return isnan(x); -} -int test_isinf(double x) { - return isinf(x); -} +int test_fpclassify(double x) { return fpclassify(x); } +int test_signbit(double x) { return signbit(x); } +int test_finite(double x) { return finite(x); } +int test_isfinite(double x) { return isfinite(x); } +int test_isnormal(double x) { return isnormal(x); } +int test_isnan(double x) { return isnan(x); } +int test_isinf(double x) { return isinf(x); } -double test_acos(double x) { - return acos(x); -} -double test_acosh(double x) { - return acosh(x); -} -double test_asin(double x) { - return asin(x); -} -double test_asinh(double x) { - return asinh(x); -} -double test_atan(double x) { - return atan(x); -} -double test_atanh(double x) { - return atanh(x); -} -double test_ceil(double x) { - return ceil(x); -} -double test_copysign(double x, double y) { - return copysign(x, y); -} -double test_cos(double x) { - return cos(x); -} -double test_cosh(double x) { - return cosh(x); -} -double test_exp(double x) { - return exp(x); -} -double test_expm1(double x) { - return expm1(x); -} -double test_fabs(double x) { - return fabs(x); -} -double test_floor(double x) { - return floor(x); -} -double test_lgamma(double x) { - return lgamma(x); -} -double test_lgamma_r(double x, int *signgamp) { - return lgamma_r(x, signgamp); -} -double test_log1p(double x) { - return log1p(x); -} -double test_log10(double x) { - return log10(x); -} -double test_log(double x) { - return log(x); -} -double test_pow(double x, double y) { - return pow(x, y); -} -double test_round(double x) { - return round(x); -} -double test_scalbn(double x, int n) { - return scalbn(x, n); -} -double test_sin(double x) { - return sin(x); -} -double test_sinh(double x) { - return sinh(x); -} -double test_sqrt(double x) { - return sqrt(x); -} -double test_tan(double x) { - return tan(x); -} -double test_tanh(double x) { - return tanh(x); -} +double test_acos(double x) { return acos(x); } +double test_acosh(double x) { return acosh(x); } +double test_asin(double x) { return asin(x); } +double test_asinh(double x) { return asinh(x); } +double test_atan(double x) { return atan(x); } +double test_atan2(double y, double x) { return atan2(y, x); } +double test_atanh(double x) { return atanh(x); } +double test_ceil(double x) { return ceil(x); } +double test_copysign(double x, double y) { return copysign(x, y); } +double test_cos(double x) { return cos(x); } +double test_cosh(double x) { return cosh(x); } +double test_erf(double x) { return erf(x); } +double test_erfc(double x) { return erfc(x); } +double test_exp(double x) { return exp(x); } +double test_expm1(double x) { return expm1(x); } +double test_fabs(double x) { return fabs(x); } +double test_floor(double x) { return floor(x); } +double test_fmod(double x, double y) { return fmod(x, y); } +double test_frexp(double x, int *exp) { return frexp(x, exp); } +double test_ldexp(double x, int exp) { return ldexp(x, exp); } +double test_lgamma(double x) { return lgamma(x); } +double test_lgamma_r(double x, int *signgamp) { return lgamma_r(x, signgamp); } +double test_log(double x) { return log(x); } +double test_log1p(double x) { return log1p(x); } +double test_log10(double x) { return log10(x); } +double test_log2(double x) { return log2(x); } +double test_logb(double x) { return logb(x); } +double test_modf(double x, double *iptr) { return modf(x, iptr); } +double test_nan(const char *s) { return nan(s); } +double test_nearbyint(double x) { return nearbyint(x); } +double test_pow(double x, double y) { return pow(x, y); } +double test_rint(double x) { return rint(x); } +double test_round(double x) { return round(x); } +double test_scalb(double x, double exp) { return scalb(x, exp); } +double test_scalbn(double x, int exp) { return scalbn(x, exp); } +double test_sin(double x) { return sin(x); } +double test_sinh(double x) { return sinh(x); } +double test_sqrt(double x) { return sqrt(x); } +double test_tan(double x) { return tan(x); } +double test_tanh(double x) { return tanh(x); } +double test_tgamma(double x) { return tgamma(x); } +double test_trunc(double x) { return trunc(x); } diff --git a/libaxx/include/cmath b/libaxx/include/cmath index e3c7835e2..6a3e3894e 100644 --- a/libaxx/include/cmath +++ b/libaxx/include/cmath @@ -3,11 +3,13 @@ #include +#undef finite #undef fpclassify #undef isfinite #undef isnormal #undef isnan #undef isinf +#undef signbit #undef acosf #undef acoshf @@ -26,12 +28,15 @@ #undef fmaxf #undef floorf #undef fmodf +#undef frexpf #undef hypotf +#undef ldexpf #undef lgammaf #undef lgammaf_r #undef log1pf #undef log10f #undef logf +#undef modff #undef nanf #undef nearbyintf #undef powf @@ -42,12 +47,14 @@ #undef sqrtf #undef tanf #undef tanhf +#undef truncf #undef acos #undef acosh #undef asin #undef asinh #undef atan +#undef atan2 #undef atanh #undef ceil #undef copysign @@ -61,88 +68,129 @@ #undef fmax #undef floor #undef fmod +#undef frexp #undef hypot +#undef ldexp #undef lgamma #undef lgamma_r +#undef log #undef log1p #undef log10 -#undef log +#undef log2 +#undef logb +#undef modf +#undef nan +#undef nearbyint #undef pow +#undef rint #undef round +#undef scalb #undef scalbn #undef sin #undef sinh #undef sqrt #undef tan #undef tanh +#undef tgamma +#undef trunc namespace std { +inline constexpr int fpclassify(float x) { return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x); } +inline constexpr bool isfinite(float x) { return __builtin_isfinite(x); } +inline constexpr bool isinf(float x) { return __builtin_isinf(x); } +inline constexpr bool isnan(float x) { return __builtin_isnan(x); } +inline constexpr bool isnormal(float x) { return __builtin_isnormal(x); } +inline constexpr bool signbit(float x) { return __builtin_signbit(x); } -static inline double acos(double x) { return __builtin_acos(x); } -static inline float acos(float x) { return __builtin_acosf(x); } -static inline double acosh(double x) { return __builtin_acosh(x); } -static inline float acosh(float x) { return __builtin_acoshf(x); } -static inline double asin(double x) { return __builtin_asin(x); } -static inline float asin(float x) { return __builtin_asinf(x); } -static inline double asinh(double x) { return __builtin_asinh(x); } -static inline float asinh(float x) { return __builtin_asinhf(x); } -static inline double atan(double x) { return __builtin_atan(x); } -static inline float atan(float x) { return __builtin_atanf(x); } -static inline double atanh(double x) { return __builtin_atanh(x); } -static inline float atanh(float x) { return __builtin_atanhf(x); } -static inline double ceil(double x) { return __builtin_ceil(x); } -static inline float ceil(float x) { return __builtin_ceilf(x); } -static inline double copysign(double x, double y) { return __builtin_copysign(x, y); } -static inline float copysign(float x, float y) { return __builtin_copysignf(x, y); } -static inline double cos(double x) { return __builtin_cos(x); } -static inline float cos(float x) { return __builtin_cosf(x); } -static inline double cosh(double x) { return __builtin_cosh(x); } -static inline float cosh(float x) { return __builtin_coshf(x); } -static inline double erf(double x) { return __builtin_erf(x); } -static inline double erfc(double x) { return __builtin_erfc(x); } -static inline double exp(double x) { return __builtin_exp(x); } -static inline float exp(float x) { return __builtin_expf(x); } -static inline double fabs(double x) { return __builtin_fabs(x); } -static inline float fabs(float x) { return __builtin_fabsf(x); } -static inline double fmax(double x, double y) { return __builtin_fmax(x, y); } -static inline float fmax(float x, float y) { return __builtin_fmaxf(x, y); } -static inline double floor(double x) { return __builtin_floor(x); } -static inline float floor(float x) { return __builtin_floorf(x); } -static inline double fmod(double x, double y) { return __builtin_fmod(x, y); } -static inline float fmod(float x, float y) { return __builtin_fmodf(x, y); } -static inline int fpclassify(double x) { return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x); } -static inline int fpclassify(float x) { return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x); } -static inline double hypot(double x, double y) { return __builtin_hypot(x, y); } -static inline float hypotf(float x, float y) { return __builtin_hypotf(x, y); } -static inline bool isfinite(double x) { return __builtin_isfinite(x); } -static inline bool isfinite(float x) { return __builtin_isfinite(x); } -static inline bool isinf(double x) { return __builtin_isinf(x); } -static inline bool isinf(float x) { return __builtin_isinf(x); } -static inline bool isnan(double x) { return __builtin_isnan(x); } -static inline bool isnan(float x) { return __builtin_isnan(x); } -static inline bool isnormal(double x) { return __builtin_isnormal(x); } -static inline bool isnormal(float x) { return __builtin_isnormal(x); } -static inline double lgamma(double x) { return __builtin_lgamma(x); } -static inline float lgamma(float x) { return __builtin_lgammaf(x); } -static inline double log10(double x) { return __builtin_log10(x); } -static inline float log10(float x) { return __builtin_log10f(x); } -static inline double log(double x) { return __builtin_log(x); } -static inline float log(float x) { return __builtin_logf(x); } -static inline double pow(double x, double y) { return __builtin_pow(x, y); } -static inline float pow(float x, float y) { return __builtin_powf(x, y); } -static inline double round(double x) { return __builtin_round(x); } -static inline float round(float x) { return __builtin_roundf(x); } -static inline double sin(double x) { return __builtin_sin(x); } -static inline float sin(float x) { return __builtin_sinf(x); } -static inline double sinh(double x) { return __builtin_sinh(x); } -static inline float sinh(float x) { return __builtin_sinhf(x); } -static inline double sqrt(double x) { return __builtin_sqrt(x); } -static inline float sqrt(float x) { return __builtin_sqrtf(x); } -static inline double tan(double x) { return __builtin_tan(x); } -static inline float tan(float x) { return __builtin_tanf(x); } -static inline double tanh(double x) { return __builtin_tanh(x); } -static inline float tanh(float x) { return __builtin_tanhf(x); } +inline constexpr float acos(float x) { return __builtin_acosf(x); } +inline constexpr float acosh(float x) { return __builtin_acoshf(x); } +inline constexpr float asin(float x) { return __builtin_asinf(x); } +inline constexpr float asinh(float x) { return __builtin_asinhf(x); } +inline constexpr float atan(float x) { return __builtin_atanf(x); } +inline constexpr float atan2(float y, float x) { return __builtin_atan2f(y, x); } +inline constexpr float atanh(float x) { return __builtin_atanhf(x); } +inline constexpr float ceil(float x) { return __builtin_ceilf(x); } +inline constexpr float copysign(float x, float y) { return __builtin_copysignf(x, y); } +inline constexpr float cos(float x) { return __builtin_cosf(x); } +inline constexpr float cosh(float x) { return __builtin_coshf(x); } +inline constexpr float exp(float x) { return __builtin_expf(x); } +inline constexpr float expm1(float x) { return __builtin_expm1f(x); } +inline constexpr float fabs(float x) { return __builtin_fabsf(x); } +inline constexpr float floor(float x) { return __builtin_floorf(x); } +inline constexpr float fmax(float x, float y) { return __builtin_fmaxf(x, y); } +inline constexpr float fmod(float x, float y) { return __builtin_fmodf(x, y); } +inline constexpr float frexp(float x, int *exp) { return __builtin_frexpf(x, exp); } +inline constexpr float ldexp(float x, int exp) { return __builtin_ldexpf(x, exp); } +inline constexpr float lgamma(float x) { return __builtin_lgammaf(x); } +inline constexpr float lgamma_r(float x, int *signgamp) { return __builtin_lgammaf_r(x, signgamp); } +inline constexpr float log1p(float x) { return __builtin_log1pf(x); } +inline constexpr float log10(float x) { return __builtin_log10f(x); } +inline constexpr float log(float x) { return __builtin_logf(x); } +inline constexpr float modf(float x, float *iptr) { return __builtin_modff(x, iptr); } +inline constexpr float nanf(const char *tagp) { return __builtin_nanf(tagp); } +inline constexpr float nearbyint(float x) { return __builtin_nearbyintf(x); } +inline constexpr float pow(float x, float y) { return __builtin_powf(x, y); } +inline constexpr float round(float x) { return __builtin_roundf(x); } +inline constexpr float scalbn(float x, int exp) { return __builtin_scalbnf(x, exp); } +inline constexpr float sin(float x) { return __builtin_sinf(x); } +inline constexpr float sinh(float x) { return __builtin_sinhf(x); } +inline constexpr float sqrt(float x) { return __builtin_sqrtf(x); } +inline constexpr float tan(float x) { return __builtin_tanf(x); } +inline constexpr float tanh(float x) { return __builtin_tanhf(x); } +inline constexpr float trunc(float x) { return __builtin_truncf(x); } + +inline constexpr int fpclassify(double x) { return __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x); } +inline constexpr bool isfinite(double x) { return __builtin_isfinite(x); } +inline constexpr bool isinf(double x) { return __builtin_isinf(x); } +inline constexpr bool isnan(double x) { return __builtin_isnan(x); } +inline constexpr bool isnormal(double x) { return __builtin_isnormal(x); } +inline constexpr bool signbit(double x) { return __builtin_signbit(x); } + +inline constexpr double acos(double x) { return __builtin_acos(x); } +inline constexpr double acosh(double x) { return __builtin_acosh(x); } +inline constexpr double asin(double x) { return __builtin_asin(x); } +inline constexpr double asinh(double x) { return __builtin_asinh(x); } +inline constexpr double atan(double x) { return __builtin_atan(x); } +inline constexpr double atan2(double y, double x) { return __builtin_atan2(y, x); } +inline constexpr double atanh(double x) { return __builtin_atanh(x); } +inline constexpr double ceil(double x) { return __builtin_ceil(x); } +inline constexpr double copysign(double x, double y) { return __builtin_copysign(x, y); } +inline constexpr double cos(double x) { return __builtin_cos(x); } +inline constexpr double cosh(double x) { return __builtin_cosh(x); } +inline constexpr double erf(double x) { return __builtin_erf(x); } +inline constexpr double erfc(double x) { return __builtin_erfc(x); } +inline constexpr double exp(double x) { return __builtin_exp(x); } +inline constexpr double expm1(double x) { return __builtin_expm1(x); } +inline constexpr double fabs(double x) { return __builtin_fabs(x); } +inline constexpr double floor(double x) { return __builtin_floor(x); } +inline constexpr double fmax(double x, double y) { return __builtin_fmax(x, y); } +inline constexpr double fmod(double x, double y) { return __builtin_fmod(x, y); } +inline constexpr double frexp(double x, int *exp) { return __builtin_frexp(x, exp); } +inline constexpr double hypot(double x, double y) { return __builtin_hypot(x, y); } +inline constexpr double ldexp(double x, int exp) { return __builtin_scalbn(x, exp); } +inline constexpr double lgamma(double x) { return __builtin_lgamma(x); } +inline constexpr double lgamma_r(double x, int *signgamp) { return __builtin_lgamma_r(x, signgamp); } +inline constexpr double log(double x) { return __builtin_log(x); } +inline constexpr double log1p(double x) { return __builtin_log1p(x); } +inline constexpr double log10(double x) { return __builtin_log10(x); } +inline constexpr double log2(double x) { return __builtin_log2(x); } +inline constexpr double logb(double x) { return __builtin_logb(x); } +inline constexpr double modf(double x, double *iptr) { return __builtin_modf(x, iptr); } +inline constexpr double nan(const char *tagp) { return __builtin_nan(tagp); } +inline constexpr double nearbyint(double x) { return __builtin_nearbyint(x); } +inline constexpr double pow(double x, double y) { return __builtin_pow(x, y); } +inline constexpr double rint(double x) { return __builtin_rint(x); } +inline constexpr double round(double x) { return __builtin_round(x); } +inline constexpr double scalb(double x, double exp) { return __builtin_scalb(x, exp); } +inline constexpr double scalbn(double x, int exp) { return __builtin_scalbn(x, exp); } +inline constexpr double sin(double x) { return __builtin_sin(x); } +inline constexpr double sinh(double x) { return __builtin_sinh(x); } +inline constexpr double sqrt(double x) { return __builtin_sqrt(x); } +inline constexpr double tan(double x) { return __builtin_tan(x); } +inline constexpr double tanh(double x) { return __builtin_tanh(x); } +inline constexpr double tgamma(double x) { return __builtin_tgamma(x); } +inline constexpr double trunc(double x) { return __builtin_trunc(x); } } diff --git a/poincare/include/poincare/approximation_helper.h b/poincare/include/poincare/approximation_helper.h index 169143faf..9b0b8ce17 100644 --- a/poincare/include/poincare/approximation_helper.h +++ b/poincare/include/poincare/approximation_helper.h @@ -10,7 +10,7 @@ namespace Poincare { namespace ApproximationHelper { template int PositiveIntegerApproximationIfPossible(const ExpressionNode * expression, bool * isUndefined, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); - template std::complex TruncateRealOrImaginaryPartAccordingToArgument(std::complex c); + template std::complex NeglectRealOrImaginaryPartIfNeglectable(std::complex result, std::complex input1, std::complex input2 = 1.0); template using ComplexCompute = Complex(*)(const std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); template Evaluation Map(const ExpressionNode * expression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexCompute compute); diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index d1a887c03..25ec2da9c 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -112,6 +112,7 @@ class Expression : public TreeHandle { friend class IntegralNode; template friend class LogarithmNode; + friend class MatrixNode; friend class NaperianLogarithmNode; friend class NAryExpressionNode; friend class StoreNode; @@ -274,7 +275,7 @@ public: m_numberOfChildren(numberOfChildren), m_untypedBuilder(builder) {} const char * name() const { return m_name; } - const int numberOfChildren() const { return m_numberOfChildren; } + int numberOfChildren() const { return m_numberOfChildren; } Expression build(Expression children) const { return (*m_untypedBuilder)(children); } private: const char * m_name; @@ -413,10 +414,8 @@ private: constexpr static double k_maxFloat = 1e100; Coordinate2D nextMinimumOfExpression(const char * symbol, double start, double step, double max, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression(), bool lookForRootMinimum = false) const; void bracketMinimum(const char * symbol, double start, double step, double max, double result[3], Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; - Coordinate2D brentMinimum(const char * symbol, double ax, double bx, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; double nextIntersectionWithExpression(const char * symbol, double start, double step, double max, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; void bracketRoot(const char * symbol, double start, double step, double max, double result[2], Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; - double brentRoot(const char * symbol, double ax, double bx, double precision, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; }; } diff --git a/poincare/include/poincare/grid_layout.h b/poincare/include/poincare/grid_layout.h index 91890c5b0..8a7534386 100644 --- a/poincare/include/poincare/grid_layout.h +++ b/poincare/include/poincare/grid_layout.h @@ -95,9 +95,7 @@ public: static GridLayout Builder() { return TreeHandle::NAryBuilder(); } void setDimensions(int rows, int columns); - void addChildAtIndex(Layout l, int index, int currentNumberOfChildren, LayoutCursor * cursor) { - Layout::addChildAtIndex(l, index, currentNumberOfChildren, cursor); - } + using Layout::addChildAtIndex; int numberOfRows() const { return node()->numberOfRows(); } int numberOfColumns() const { return node()->numberOfColumns(); } private: diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index 01032861b..b4ef1485a 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -62,7 +62,6 @@ public: // Rendering void draw(KDContext * ctx, KDPoint p, KDColor expressionColor = Palette::PrimaryText, KDColor backgroundColor = Palette::BackgroundHard, Layout * selectionStart = nullptr, Layout * selectionEnd = nullptr, KDColor selectionColor = KDColorRed); - KDPoint origin(); KDPoint absoluteOrigin(); KDSize layoutSize(); KDCoordinate baseline(); diff --git a/poincare/include/poincare/matrix.h b/poincare/include/poincare/matrix.h index 487e9b872..96f6e73cf 100644 --- a/poincare/include/poincare/matrix.h +++ b/poincare/include/poincare/matrix.h @@ -11,7 +11,7 @@ public: m_numberOfRows(0), m_numberOfColumns(0) {} - + bool hasMatrixChild(Context * context) const; int numberOfRows() const { return m_numberOfRows; } int numberOfColumns() const { return m_numberOfColumns; } virtual void setNumberOfRows(int rows) { assert(rows >= 0); m_numberOfRows = rows; } @@ -87,7 +87,7 @@ public: static constexpr int k_maxNumberOfCoefficients = 100; // Expression - Expression shallowReduce(); + Expression shallowReduce(Context * context); private: MatrixNode * node() const { return static_cast(Expression::node()); } diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index c5860e77e..77f2dda77 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -34,6 +34,7 @@ public: int polynomialDegree(Context * context, const char * symbolName) const override; int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const override; + template static Complex computeNotPrincipalRealRootOfRationalPow(const std::complex c, T p, T q); template static Complex compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat); private: @@ -59,11 +60,12 @@ private: template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex d, Preferences::ComplexFormat complexFormat); template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat); Evaluation approximate(SinglePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + return templatedApproximate(context, complexFormat, angleUnit); } Evaluation approximate(DoublePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Power final : public Expression { diff --git a/poincare/include/poincare/tree_handle.h b/poincare/include/poincare/tree_handle.h index 68f99b41a..5e2a07d9b 100644 --- a/poincare/include/poincare/tree_handle.h +++ b/poincare/include/poincare/tree_handle.h @@ -128,7 +128,7 @@ protected: void setIdentifierAndRetain(uint16_t newId); void setTo(const TreeHandle & tr); - static bool hasNode(uint16_t identifier) { return identifier < TreeNode::NoNodeIdentifier; } + static bool hasNode(uint16_t identifier) { return TreeNode::IsValidIdentifier(identifier); } /* Hierarchy operations */ // Add diff --git a/poincare/include/poincare/tree_node.h b/poincare/include/poincare/tree_node.h index f46d6ff9a..71d1df351 100644 --- a/poincare/include/poincare/tree_node.h +++ b/poincare/include/poincare/tree_node.h @@ -172,6 +172,8 @@ public: void log(std::ostream & stream, bool recursive = true); #endif + static bool IsValidIdentifier(uint16_t id) { return id < NoNodeIdentifier; } + protected: TreeNode() : m_identifier(NoNodeIdentifier), diff --git a/poincare/include/poincare/tree_pool.h b/poincare/include/poincare/tree_pool.h index 7291bfb79..cf5108d73 100644 --- a/poincare/include/poincare/tree_pool.h +++ b/poincare/include/poincare/tree_pool.h @@ -27,7 +27,7 @@ public: // Node TreeNode * node(uint16_t identifier) const { - assert(identifier >= 0 && identifier < MaxNumberOfNodes); + assert(TreeNode::IsValidIdentifier(identifier) && identifier < MaxNumberOfNodes); if (m_nodeForIdentifierOffset[identifier] != UINT16_MAX) { return const_cast(reinterpret_cast(m_alignedBuffer + m_nodeForIdentifierOffset[identifier])); } @@ -125,7 +125,7 @@ private: } } void push(uint16_t i) { - assert(m_currentIndex >= 0 && m_currentIndex < MaxNumberOfNodes); + assert(TreeNode::IsValidIdentifier(m_currentIndex) && m_currentIndex < MaxNumberOfNodes); m_availableIdentifiers[m_currentIndex++] = i; } uint16_t pop() { diff --git a/poincare/include/poincare/trigonometry.h b/poincare/include/poincare/trigonometry.h index 4b0e3fd6e..0eac9d5c2 100644 --- a/poincare/include/poincare/trigonometry.h +++ b/poincare/include/poincare/trigonometry.h @@ -23,9 +23,6 @@ public: static Expression table(const Expression e, ExpressionNode::Type type, ExpressionNode::ReductionContext reductionContext); // , Function f, bool inverse template static std::complex ConvertToRadian(const std::complex c, Preferences::AngleUnit angleUnit); template static std::complex ConvertRadianToAngleUnit(const std::complex c, Preferences::AngleUnit angleUnit); - template static std::complex RoundToMeaningfulDigits(const std::complex result, const std::complex input); -private: - template static T RoundToMeaningfulDigits(T result, T input); }; } diff --git a/poincare/include/poincare/unit.h b/poincare/include/poincare/unit.h index 3b0716ae3..97021c50c 100644 --- a/poincare/include/poincare/unit.h +++ b/poincare/include/poincare/unit.h @@ -29,7 +29,7 @@ public: m_exponent(exponent) {} const char * symbol() const { return m_symbol; } - const int8_t exponent() const { return m_exponent; } + int8_t exponent() const { return m_exponent; } int serialize(char * buffer, int bufferSize) const; private: const char * m_symbol; @@ -235,7 +235,7 @@ public: Representative("week", "7*24*60*60*_s", Representative::Prefixable::No, NoPrefix), - Representative("month", "30*7*24*60*60*_s", + Representative("month", "365.25/12*24*60*60*_s", Representative::Prefixable::No, NoPrefix), Representative("year", "365.25*24*60*60*_s", diff --git a/poincare/src/absolute_value.cpp b/poincare/src/absolute_value.cpp index 18bbafe14..bf4aee0d1 100644 --- a/poincare/src/absolute_value.cpp +++ b/poincare/src/absolute_value.cpp @@ -50,21 +50,21 @@ Expression AbsoluteValue::shallowReduce(ExpressionNode::ReductionContext reducti } // |x| = ±x if x is real if (c.isReal(reductionContext.context())) { - float app = c.node()->approximate(float(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()).toScalar(); - if (!std::isnan(app) && - ((c.isNumber() && app >= 0) || app >= Expression::Epsilon())) { - /* abs(a) = a with a >= 0 - * To check that a > 0, if a is a number we can use float comparison; - * in other cases, we are more conservative and rather check that - * a > epsilon ~ 1E-7 to avoid potential error due to float precision. */ - replaceWithInPlace(c); - return c; - } else if (!std::isnan(app) && - ((c.isNumber() && app < 0.0f) || app <= -Expression::Epsilon())) { - // abs(a) = -a with a < 0 (same comment as above to check that a < 0) - Multiplication m = Multiplication::Builder(Rational::Builder(-1), c); - replaceWithInPlace(m); - return m.shallowReduce(reductionContext); + double app = c.node()->approximate(double(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()).toScalar(); + if (!std::isnan(app)) { + if ((c.isNumber() && app >= 0) || app >= Expression::Epsilon()) { + /* abs(a) = a with a >= 0 + * To check that a > 0, if a is a number we can use float comparison; + * in other cases, we are more conservative and rather check that + * a > epsilon ~ 1E-7 to avoid potential error due to float precision. */ + replaceWithInPlace(c); + return c; + } else if ((c.isNumber() && app < 0.0f) || app <= -Expression::Epsilon()) { + // abs(a) = -a with a < 0 (same comment as above to check that a < 0) + Multiplication m = Multiplication::Builder(Rational::Builder(-1), c); + replaceWithInPlace(m); + return m.shallowReduce(reductionContext); + } } } // |a+ib| = sqrt(a^2+b^2) diff --git a/poincare/src/approximation_helper.cpp b/poincare/src/approximation_helper.cpp index 32f1f88ad..61c5f8d20 100644 --- a/poincare/src/approximation_helper.cpp +++ b/poincare/src/approximation_helper.cpp @@ -10,9 +10,19 @@ extern "C" { namespace Poincare { -template T absMod(T a, T b) { - T result = std::fmod(std::fabs(a), b); - return result > b/2 ? b-result : result; +template bool isNegligeable(T x, T precision, T norm1, T norm2) { + T absX = std::fabs(x); + return absX <= 10.0*precision && absX/norm1 <= precision && absX/norm2 <= precision; +} + +template < typename T> T minimalNonNullMagnitudeOfParts(std::complex c) { + T absRealInput = std::fabs(c.real()); + T absImagInput = std::fabs(c.imag()); + // If the magnitude of one part is null, ignore it + if (absRealInput == 0.0 || (absImagInput > 0.0 && absImagInput < absRealInput)) { + return absImagInput; + } + return absRealInput; } static inline int absInt(int x) { return x < 0 ? -x : x; } @@ -27,16 +37,26 @@ template int ApproximationHelper::PositiveIntegerApproximationIfPos return absInt((int)scalar); } -template std::complex ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex c) { - T arg = std::arg(c); - T precision = 10*Expression::Epsilon(); - if (absMod(arg, (T)M_PI) <= precision) { - c.imag(0); +template std::complex ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::complex result, std::complex input1, std::complex input2) { + /* Cheat: openbsd functions (cos, sin, tan, cosh, acos, pow...) are + * numerical implementation and thus are approximative. + * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to avoid + * weird results as acos(1) = 6E-17 or cos(π/2) = 4E-17, we round the result + * to its 1E-6 or 1E-14 precision when its ratio with the argument (π/2 in the + * example) is smaller than epsilon. This way, we have sin(π) ~ 0 and + * sin(1E-15)=1E-15. + * We can't do that for all evaluation as the user can operate on values as + * small as 1E-308 (in double) and most results still be correct. */ + T magnitude1 = minimalNonNullMagnitudeOfParts(input1); + T magnitude2 = minimalNonNullMagnitudeOfParts(input2); + T precision = 10.0*Expression::Epsilon(); + if (isNegligeable(result.imag(), precision, magnitude1, magnitude2)) { + result.imag(0); } - if (absMod(arg-(T)M_PI/2.0, (T)M_PI) <= precision) { - c.real(0); + if (isNegligeable(result.real(), precision, magnitude1, magnitude2)) { + result.real(0); } - return c; + return result; } template Evaluation ApproximationHelper::Map(const ExpressionNode * expression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexCompute compute) { @@ -106,8 +126,8 @@ template MatrixComplex ApproximationHelper::ElementWiseOnComplexM template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); template int Poincare::ApproximationHelper::PositiveIntegerApproximationIfPossible(Poincare::ExpressionNode const*, bool*, Poincare::Context*, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); -template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex); -template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex); +template std::complex Poincare::ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::complex,std::complex,std::complex); +template std::complex Poincare::ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::complex,std::complex,std::complex); template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); template Poincare::Evaluation Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context * context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction computeOnMatrices); diff --git a/poincare/src/arc_cosine.cpp b/poincare/src/arc_cosine.cpp index 353c4b974..9f27e4f5d 100644 --- a/poincare/src/arc_cosine.cpp +++ b/poincare/src/arc_cosine.cpp @@ -44,7 +44,7 @@ Complex ArcCosineNode::computeOnComplex(const std::complex c, Preferences: result.imag(-result.imag()); // other side of the cut } } - result = Trigonometry::RoundToMeaningfulDigits(result, c); + result = ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c); return Complex::Builder(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); } diff --git a/poincare/src/arc_sine.cpp b/poincare/src/arc_sine.cpp index 2ee8b0fd1..4d7100ac8 100644 --- a/poincare/src/arc_sine.cpp +++ b/poincare/src/arc_sine.cpp @@ -44,7 +44,7 @@ Complex ArcSineNode::computeOnComplex(const std::complex c, Preferences::C result.imag(-result.imag()); // other side of the cut } } - result = Trigonometry::RoundToMeaningfulDigits(result, c); + result = ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c); return Complex::Builder(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); } diff --git a/poincare/src/arc_tangent.cpp b/poincare/src/arc_tangent.cpp index 2f8152411..19bb310bb 100644 --- a/poincare/src/arc_tangent.cpp +++ b/poincare/src/arc_tangent.cpp @@ -40,7 +40,7 @@ Complex ArcTangentNode::computeOnComplex(const std::complex c, Preferences result.real(-result.real()); // other side of the cut } } - result = Trigonometry::RoundToMeaningfulDigits(result, c); + result = ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c); return Complex::Builder(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); } diff --git a/poincare/src/arithmetic.cpp b/poincare/src/arithmetic.cpp index f4563db79..a7b50149b 100644 --- a/poincare/src/arithmetic.cpp +++ b/poincare/src/arithmetic.cpp @@ -7,6 +7,9 @@ Integer Arithmetic::LCM(const Integer & a, const Integer & b) { if (a.isZero() || b.isZero()) { return Integer(0); } + if (a.isEqualTo(b)) { + return a; + } Integer signResult = Integer::Division(Integer::Multiplication(a, b), GCD(a, b)).quotient; signResult.setNegative(false); return signResult; @@ -16,7 +19,9 @@ Integer Arithmetic::GCD(const Integer & a, const Integer & b) { if (a.isOverflow() || b.isOverflow()) { return Integer::Overflow(false); } - + if (a.isEqualTo(b)) { + return a; + } Integer i = a; Integer j = b; i.setNegative(false); diff --git a/poincare/src/code_point_layout.cpp b/poincare/src/code_point_layout.cpp index b85913194..5d32863b1 100644 --- a/poincare/src/code_point_layout.cpp +++ b/poincare/src/code_point_layout.cpp @@ -58,22 +58,22 @@ bool CodePointLayoutNode::isCollapsable(int * numberOfOpenParenthesis, bool goin } return false; } - } - if (isMultiplicationCodePoint()) { - /* We want '*' to be collapsable only if the following brother is not a - * fraction, so that the user can write intuitively "1/2 * 3/4". */ - Layout thisRef = CodePointLayout(this); - Layout parent = thisRef.parent(); - if (!parent.isUninitialized()) { - int indexOfThis = parent.indexOfChild(thisRef); - Layout brother; - if (indexOfThis > 0 && goingLeft) { - brother = parent.childAtIndex(indexOfThis-1); - } else if (indexOfThis < parent.numberOfChildren() - 1 && !goingLeft) { - brother = parent.childAtIndex(indexOfThis+1); - } - if (!brother.isUninitialized() && brother.type() == LayoutNode::Type::FractionLayout) { - return false; + if (isMultiplicationCodePoint()) { + /* We want '*' to be collapsable only if the following brother is not a + * fraction, so that the user can write intuitively "1/2 * 3/4". */ + Layout thisRef = CodePointLayout(this); + Layout parent = thisRef.parent(); + if (!parent.isUninitialized()) { + int indexOfThis = parent.indexOfChild(thisRef); + Layout brother; + if (indexOfThis > 0 && goingLeft) { + brother = parent.childAtIndex(indexOfThis-1); + } else if (indexOfThis < parent.numberOfChildren() - 1 && !goingLeft) { + brother = parent.childAtIndex(indexOfThis+1); + } + if (!brother.isUninitialized() && brother.type() == LayoutNode::Type::FractionLayout) { + return false; + } } } } diff --git a/poincare/src/cosine.cpp b/poincare/src/cosine.cpp index 91414e60c..2e216ce8e 100644 --- a/poincare/src/cosine.cpp +++ b/poincare/src/cosine.cpp @@ -19,7 +19,7 @@ template Complex CosineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); std::complex res = std::cos(angleInput); - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(res, angleInput)); } Layout CosineNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index a4793bc69..602bd3663 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -684,7 +684,6 @@ void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expre /* Case 1: the reduced expression is a matrix: We scan the matrix children to * beautify them with the right complex format. */ if (e.type() == ExpressionNode::Type::Matrix) { - // TODO: this method enables to take the complex format into account when the result is a matrix of scalar. It won't work for nested matrices... Find a more elegant and general solution? Matrix m = static_cast(e); *simplifiedExpression = Matrix::Builder(); if (approximateExpression) { @@ -694,6 +693,7 @@ void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expre Expression simplifiedChild; Expression approximateChild = approximateExpression ? Expression() : nullptr; e.childAtIndex(i).beautifyAndApproximateScalar(&simplifiedChild, &approximateChild, userReductionContext, context, complexFormat, angleUnit); + assert(!simplifiedChild.deepIsMatrix(context)); static_cast(simplifiedExpression)->addChildAtIndexInPlace(simplifiedChild, i, i); if (approximateExpression) { static_cast(approximateExpression)->addChildAtIndexInPlace(approximateChild, i, i); @@ -770,7 +770,7 @@ Expression Expression::mapOnMatrixFirstChild(ExpressionNode::ReductionContext re } matrix.setDimensions(static_cast(c).numberOfRows(), static_cast(c).numberOfColumns()); replaceWithInPlace(matrix); - return matrix.shallowReduce(); + return matrix.shallowReduce(reductionContext.context()); } Expression Expression::radianToAngleUnit(Preferences::AngleUnit angleUnit) { @@ -1003,7 +1003,7 @@ Coordinate2D Expression::nextIntersection(const char * symbol, double st return expression0->approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression1->approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); }, context, complexFormat, angleUnit, expression); Coordinate2D result(resultAbscissa, approximateWithValueForSymbol(symbol, resultAbscissa, context, complexFormat, angleUnit)); - if (std::fabs(result.x2()) < step*k_solverPrecision) { + if (std::fabs(result.x2()) < std::fabs(step)*k_solverPrecision) { result.setX2(0.0); } return result; @@ -1019,7 +1019,7 @@ Coordinate2D Expression::nextMinimumOfExpression(const char * symbol, do bool endCondition = false; do { bracketMinimum(symbol, x, step, max, bracket, evaluate, context, complexFormat, angleUnit, expression); - result = brentMinimum(symbol, bracket[0], bracket[2], evaluate, context, complexFormat, angleUnit, expression); + result = Solver::BrentMinimum(bracket[0], bracket[2], evaluate, context, complexFormat, angleUnit, this, symbol, &expression); x = bracket[1]; // Because of float approximation, exact zero is never reached if (std::fabs(result.x1()) < std::fabs(step)*k_solverPrecision) { @@ -1077,19 +1077,6 @@ void Expression::bracketMinimum(const char * symbol, double start, double step, result[2] = NAN; } -Coordinate2D Expression::brentMinimum(const char * symbol, double ax, double bx, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { - return Solver::BrentMinimum( - ax, - bx, - evaluation, - context, - complexFormat, - angleUnit, - this, - symbol, - &expression); -} - double Expression::nextIntersectionWithExpression(const char * symbol, double start, double step, double max, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { if (start == max || step == 0.0) { return NAN; @@ -1100,7 +1087,7 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st double x = start+step; do { bracketRoot(symbol, x, step, max, bracket, evaluation, context, complexFormat, angleUnit, expression); - result = brentRoot(symbol, bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, complexFormat, angleUnit, expression); + result = Solver::BrentRoot(bracket[0], bracket[1], std::fabs(step/precisionByGradUnit), evaluation, context, complexFormat, angleUnit, this, symbol, &expression); x = bracket[1]; } while (std::isnan(result) && (step > 0.0 ? x <= max : x >= max)); @@ -1149,20 +1136,6 @@ void Expression::bracketRoot(const char * symbol, double start, double step, dou result[1] = NAN; } -double Expression::brentRoot(const char * symbol, double ax, double bx, double precision, Solver::ValueAtAbscissa evaluation, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { - return Solver::BrentRoot( - ax, - bx, - precision, - evaluation, - context, - complexFormat, - angleUnit, - this, - symbol, - &expression); -} - template float Expression::Epsilon(); template double Expression::Epsilon(); diff --git a/poincare/src/hyperbolic_arc_cosine.cpp b/poincare/src/hyperbolic_arc_cosine.cpp index 8ea38dd6c..eb6f4b668 100644 --- a/poincare/src/hyperbolic_arc_cosine.cpp +++ b/poincare/src/hyperbolic_arc_cosine.cpp @@ -23,7 +23,7 @@ Complex HyperbolicArcCosineNode::computeOnComplex(const std::complex c, Pr * on this cut. We followed the convention chosen by the lib c++ of llvm on * ]-inf+0i, 1+0i] (warning: atanh takes the other side of the cut values on * ]-inf-0i, 1-0i[).*/ - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(result, c)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c)); } template Complex Poincare::HyperbolicArcCosineNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); diff --git a/poincare/src/hyperbolic_arc_sine.cpp b/poincare/src/hyperbolic_arc_sine.cpp index 7ee51048c..3952a4f29 100644 --- a/poincare/src/hyperbolic_arc_sine.cpp +++ b/poincare/src/hyperbolic_arc_sine.cpp @@ -27,7 +27,7 @@ Complex HyperbolicArcSineNode::computeOnComplex(const std::complex c, Pref if (c.real() == 0 && c.imag() < 1) { result.real(-result.real()); // other side of the cut } - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(result, c)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c)); } template Complex Poincare::HyperbolicArcSineNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); diff --git a/poincare/src/hyperbolic_arc_tangent.cpp b/poincare/src/hyperbolic_arc_tangent.cpp index 13c63bba5..09453d22a 100644 --- a/poincare/src/hyperbolic_arc_tangent.cpp +++ b/poincare/src/hyperbolic_arc_tangent.cpp @@ -27,7 +27,7 @@ Complex HyperbolicArcTangentNode::computeOnComplex(const std::complex c, P if (c.imag() == 0 && c.real() > 1) { result.imag(-result.imag()); // other side of the cut } - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(result, c)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c)); } template Complex Poincare::HyperbolicArcTangentNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); diff --git a/poincare/src/hyperbolic_cosine.cpp b/poincare/src/hyperbolic_cosine.cpp index c722ec209..c963ee4ad 100644 --- a/poincare/src/hyperbolic_cosine.cpp +++ b/poincare/src/hyperbolic_cosine.cpp @@ -16,7 +16,7 @@ int HyperbolicCosineNode::serialize(char * buffer, int bufferSize, Preferences:: template Complex HyperbolicCosineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(std::cosh(c), c)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::cosh(c), c)); } template Complex Poincare::HyperbolicCosineNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); diff --git a/poincare/src/hyperbolic_sine.cpp b/poincare/src/hyperbolic_sine.cpp index 232bd8fcb..237804747 100644 --- a/poincare/src/hyperbolic_sine.cpp +++ b/poincare/src/hyperbolic_sine.cpp @@ -16,7 +16,7 @@ int HyperbolicSineNode::serialize(char * buffer, int bufferSize, Preferences::Pr template Complex HyperbolicSineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(std::sinh(c), c)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::sinh(c), c)); } template Complex Poincare::HyperbolicSineNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); diff --git a/poincare/src/hyperbolic_tangent.cpp b/poincare/src/hyperbolic_tangent.cpp index 7c4ad545d..48a5e8830 100644 --- a/poincare/src/hyperbolic_tangent.cpp +++ b/poincare/src/hyperbolic_tangent.cpp @@ -16,7 +16,7 @@ int HyperbolicTangentNode::serialize(char * buffer, int bufferSize, Preferences: template Complex HyperbolicTangentNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(std::tanh(c), c)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(std::tanh(c), c)); } template Complex Poincare::HyperbolicTangentNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); diff --git a/poincare/src/layout_node.cpp b/poincare/src/layout_node.cpp index 2c50dd9a6..7c33a2f3d 100644 --- a/poincare/src/layout_node.cpp +++ b/poincare/src/layout_node.cpp @@ -39,16 +39,6 @@ void LayoutNode::draw(KDContext * ctx, KDPoint p, KDColor expressionColor, KDCol } } -KDPoint LayoutNode::origin() { - LayoutNode * p = parent(); - if (p == nullptr) { - return absoluteOrigin(); - } else { - return KDPoint(absoluteOrigin().x() - p->absoluteOrigin().x(), - absoluteOrigin().y() - p->absoluteOrigin().y()); - } -} - KDPoint LayoutNode::absoluteOrigin() { LayoutNode * p = parent(); if (!m_positioned) { diff --git a/poincare/src/matrix.cpp b/poincare/src/matrix.cpp index fc541db09..e34393aef 100644 --- a/poincare/src/matrix.cpp +++ b/poincare/src/matrix.cpp @@ -20,6 +20,15 @@ namespace Poincare { static inline int minInt(int x, int y) { return x < y ? x : y; } +bool MatrixNode::hasMatrixChild(Context * context) const { + for (ExpressionNode * c : children()) { + if (Expression(c).deepIsMatrix(context)) { + return true; + } + } + return false; +} + void MatrixNode::didAddChildAtIndex(int newNumberOfChildren) { setNumberOfRows(1); setNumberOfColumns(newNumberOfChildren); @@ -30,7 +39,7 @@ int MatrixNode::polynomialDegree(Context * context, const char * symbolName) con } Expression MatrixNode::shallowReduce(ReductionContext reductionContext) { - return Matrix(this).shallowReduce(); + return Matrix(this).shallowReduce(reductionContext.context()); } Layout MatrixNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -390,7 +399,7 @@ Expression Matrix::determinant(ExpressionNode::ReductionContext reductionContext return result; } -Expression Matrix::shallowReduce() { +Expression Matrix::shallowReduce(Context * context) { { Expression e = Expression::defaultShallowReduce(); e = e.defaultHandleUnitsInChildren(); @@ -398,6 +407,9 @@ Expression Matrix::shallowReduce() { return e; } } + if (node()->hasMatrixChild(context)) { + return replaceWithUndefinedInPlace(); + } return *this; } diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 3d12f0245..f0767a8c5 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -607,8 +607,10 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext * which also are multiplications themselves. */ mergeMultiplicationChildrenInPlace(); + Context * context = reductionContext.context(); + // Step 2: Sort the children - sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true); // Step 3: Handle matrices /* Thanks to the simplification order, all matrix children (if any) are the @@ -683,7 +685,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext * interval). */ if (multiplicationChildIndex >= 0) { - if (childAtIndex(multiplicationChildIndex).deepIsMatrix(reductionContext.context())) { + if (childAtIndex(multiplicationChildIndex).deepIsMatrix(context)) { return *this; } removeChildInPlace(resultMatrix, resultMatrix.numberOfChildren()); @@ -696,7 +698,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext } } replaceWithInPlace(resultMatrix); - return resultMatrix.shallowReduce(); + return resultMatrix.shallowReduce(context); } /* Step 4: Gather like terms. For example, turn pi^2*pi^3 into pi^5. Thanks to @@ -706,7 +708,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext while (i < numberOfChildren()-1) { Expression oi = childAtIndex(i); Expression oi1 = childAtIndex(i+1); - if (oi.recursivelyMatches(Expression::IsRandom, reductionContext.context(), true)) { + if (oi.recursivelyMatches(Expression::IsRandom, context, true)) { // Do not factorize random or randint i++; continue; @@ -754,7 +756,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext /* Replacing sin/cos by tan factors may have mixed factors and factors are * guaranteed to be sorted (according ot SimplificationOrder) at the end of * shallowReduce */ - sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true); } /* Step 6: We remove rational children that appeared in the middle of sorted @@ -777,6 +779,14 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext if (childAtIndex(0).isNumber()) { Number o0 = childAtIndex(0).convert(); Number m = Number::Multiplication(o0, static_cast(o)); + if ((IsInfinity(m, context) || m.isUndefined()) + && !IsInfinity(o0, context) && !o0.isUndefined() + && !IsInfinity(o, context) && !o.isUndefined()) + { + // Stop the reduction due to a multiplication overflow + SetInterruption(true); + return *this; + } replaceChildAtIndexInPlace(0, m); removeChildAtIndexInPlace(i); } else { @@ -798,7 +808,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext const Expression c = childAtIndex(0); if (c.type() == ExpressionNode::Type::Rational && static_cast(c).isZero()) { // Check that other children don't match inf or unit - bool infiniteOrUnitFactor = recursivelyMatches([](const Expression e, Context * context) { return Expression::IsInfinity(e,context) || e.type() == ExpressionNode::Type::Unit; }, reductionContext.context()); + bool infiniteOrUnitFactor = recursivelyMatches([](const Expression e, Context * context) { return Expression::IsInfinity(e,context) || e.type() == ExpressionNode::Type::Unit; }, context); if (!infiniteOrUnitFactor) { replaceWithInPlace(c); return c; @@ -817,7 +827,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext * reduce expressions such as (x+y)^(-1)*(x+y)(a+b). * If there is a random somewhere, do not expand. */ Expression p = parent(); - bool hasRandom = recursivelyMatches(Expression::IsRandom, reductionContext.context(), true); + bool hasRandom = recursivelyMatches(Expression::IsRandom, context, true); if (shouldExpand && (p.isUninitialized() || p.type() != ExpressionNode::Type::Multiplication) && !hasRandom) @@ -844,7 +854,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext * - All children are either real or ComplexCartesian (allChildrenAreReal == 0) * We can bubble up ComplexCartesian nodes. * Do not simplify if there are randoms !*/ - if (!hasRandom && allChildrenAreReal(reductionContext.context()) == 0) { + if (!hasRandom && allChildrenAreReal(context) == 0) { int nbChildren = numberOfChildren(); int i = nbChildren-1; // Children are sorted so ComplexCartesian nodes are at the end diff --git a/poincare/src/nth_root.cpp b/poincare/src/nth_root.cpp index 74ee32f52..db4748a47 100644 --- a/poincare/src/nth_root.cpp +++ b/poincare/src/nth_root.cpp @@ -46,18 +46,12 @@ Evaluation NthRootNode::templatedApproximate(Context * context, Preferences:: /* If the complexFormat is Real, we look for nthroot of form root(x,q) with * x real and q integer because they might have a real form which does not * correspond to the principale angle. */ - if (complexFormat == Preferences::ComplexFormat::Real) { + if (complexFormat == Preferences::ComplexFormat::Real && indexc.imag() == 0.0 && std::round(indexc.real()) == indexc.real()) { // root(x, q) with q integer and x real - if (basec.imag() == 0.0 && indexc.imag() == 0.0 && std::round(indexc.real()) == indexc.real()) { - std::complex absBasec = basec; - absBasec.real(std::fabs(absBasec.real())); - // compute root(|x|, q) - Complex absBasePowIndex = PowerNode::compute(absBasec, std::complex(1.0)/(indexc), complexFormat); - // q odd if (-1)^q = -1 - if (std::pow((T)-1.0, (T)indexc.real()) < 0.0) { - return basec.real() < 0 ? Complex::Builder(-absBasePowIndex.stdComplex()) : absBasePowIndex; - } - } + Complex result = PowerNode::computeNotPrincipalRealRootOfRationalPow(basec, (T)1.0, indexc.real()); + if (!result.isUndefined()) { + return result; + } } result = PowerNode::compute(basec, std::complex(1.0)/(indexc), complexFormat); } diff --git a/poincare/src/parsing/parser.cpp b/poincare/src/parsing/parser.cpp index c5671b602..1f7d63d90 100644 --- a/poincare/src/parsing/parser.cpp +++ b/poincare/src/parsing/parser.cpp @@ -46,13 +46,9 @@ bool Parser::IsSpecialIdentifierName(const char * name, size_t nameLength) { Token::CompareNonNullTerminatedName(name, nameLength, Infinity::Name()) == 0 || Token::CompareNonNullTerminatedName(name, nameLength, Undefined::Name()) == 0 || Token::CompareNonNullTerminatedName(name, nameLength, Unreal::Name()) == 0 || - Token::CompareNonNullTerminatedName(name, nameLength, "u_") == 0 || - Token::CompareNonNullTerminatedName(name, nameLength, "v_") == 0 || - Token::CompareNonNullTerminatedName(name, nameLength, "w_") == 0 || Token::CompareNonNullTerminatedName(name, nameLength, "u") == 0 || Token::CompareNonNullTerminatedName(name, nameLength, "v") == 0 || - Token::CompareNonNullTerminatedName(name, nameLength, "w") == 0 || - Token::CompareNonNullTerminatedName(name, nameLength, "log_") == 0 + Token::CompareNonNullTerminatedName(name, nameLength, "w") == 0 ); } @@ -370,6 +366,25 @@ void Parser::parseUnit(Expression & leftHandSide, Token::Type stoppingType) { void Parser::parseReservedFunction(Expression & leftHandSide, const Expression::FunctionHelper * const * functionHelper) { const char * name = (**functionHelper).name(); + + if (strcmp(name, "log") == 0 && popTokenIfType(Token::LeftBrace)) { + // Special case for the log function (e.g. "log{2}(8)") + Expression base = parseUntil(Token::RightBrace); + if (m_status != Status::Progress) { + } else if (!popTokenIfType(Token::RightBrace)) { + m_status = Status::Error; // Right brace missing. + } else { + Expression parameter = parseFunctionParameters(); + if (m_status != Status::Progress) { + } else if (parameter.numberOfChildren() != 1) { + m_status = Status::Error; // Unexpected number of many parameters. + } else { + leftHandSide = Logarithm::Builder(parameter.childAtIndex(0), base); + } + } + return; + } + Expression parameters = parseFunctionParameters(); if (m_status != Status::Progress) { return; @@ -393,10 +408,12 @@ void Parser::parseReservedFunction(Expression & leftHandSide, const Expression:: } } -void Parser::parseSequence(Expression & leftHandSide, const char name, Token::Type leftDelimiter, Token::Type rightDelimiter) { - if (!popTokenIfType(leftDelimiter)) { +void Parser::parseSequence(Expression & leftHandSide, const char name, Token::Type leftDelimiter1, Token::Type rightDelimiter1, Token::Type leftDelimiter2, Token::Type rightDelimiter2) { + bool delimiterTypeIsOne = popTokenIfType(leftDelimiter1); + if (!delimiterTypeIsOne && !popTokenIfType(leftDelimiter2)) { m_status = Status::Error; // Left delimiter missing. } else { + Token::Type rightDelimiter = delimiterTypeIsOne ? rightDelimiter1 : rightDelimiter2; Expression rank = parseUntil(rightDelimiter); if (m_status != Status::Progress) { } else if (!popTokenIfType(rightDelimiter)) { @@ -424,32 +441,14 @@ void Parser::parseSpecialIdentifier(Expression & leftHandSide) { leftHandSide = Undefined::Builder(); } else if (m_currentToken.compareTo(Unreal::Name()) == 0) { leftHandSide = Unreal::Builder(); - } else if (m_currentToken.compareTo("u_") == 0 || m_currentToken.compareTo("v_") == 0 || m_currentToken.compareTo("w_") == 0) { // Special case for sequences (e.g. "u_{n}") - /* We now that m_currentToken.text()[0] is either 'u' or 'v', so we do not - * need to pass a code point to parseSequence. */ - parseSequence(leftHandSide, m_currentToken.text()[0], Token::LeftBrace, Token::RightBrace); - } else if (m_currentToken.compareTo("u") == 0 || m_currentToken.compareTo("v") == 0|| m_currentToken.compareTo("w") == 0) { // Special case for sequences (e.g. "u(n)") - /* We now that m_currentToken.text()[0] is either 'u' or 'v', so we do not - * need to pass a code point to parseSequence. */ - parseSequence(leftHandSide, m_currentToken.text()[0], Token::LeftParenthesis, Token::RightParenthesis); - } else if (m_currentToken.compareTo("log_") == 0) { // Special case for the log function (e.g. "log_{2}(8)") - if (!popTokenIfType(Token::LeftBrace)) { - m_status = Status::Error; // Left brace missing. - } else { - Expression base = parseUntil(Token::RightBrace); - if (m_status != Status::Progress) { - } else if (!popTokenIfType(Token::RightBrace)) { - m_status = Status::Error; // Right brace missing. - } else { - Expression parameter = parseFunctionParameters(); - if (m_status != Status::Progress) { - } else if (parameter.numberOfChildren() != 1) { - m_status = Status::Error; // Unexpected number of many parameters. - } else { - leftHandSide = Logarithm::Builder(parameter.childAtIndex(0), base); - } - } - } + } else { + assert(m_currentToken.compareTo("u") == 0 + || m_currentToken.compareTo("v") == 0 + || m_currentToken.compareTo("w") == 0); + /* Special case for sequences (e.g. "u(n)", "u{n}", ...) + * We know that m_currentToken.text()[0] is either 'u', 'v' or 'w', so we do + * not need to pass a code point to parseSequence. */ + parseSequence(leftHandSide, m_currentToken.text()[0], Token::LeftParenthesis, Token::RightParenthesis, Token::LeftBrace, Token::RightBrace); } } diff --git a/poincare/src/parsing/parser.h b/poincare/src/parsing/parser.h index f0b41cf63..08e04a211 100644 --- a/poincare/src/parsing/parser.h +++ b/poincare/src/parsing/parser.h @@ -74,7 +74,7 @@ private: Expression parseCommaSeparatedList(); void parseReservedFunction(Expression & leftHandSide, const Expression::FunctionHelper * const * functionHelper); void parseSpecialIdentifier(Expression & leftHandSide); - void parseSequence(Expression & leftHandSide, const char name, Token::Type leftDelimiter, Token::Type rightDelimiter); + void parseSequence(Expression & leftHandSide, const char name, Token::Type leftDelimiter1, Token::Type rightDelimiter1, Token::Type leftDelimiter2, Token::Type rightDelimiter2); void parseCustomIdentifier(Expression & leftHandSide, const char * name, size_t length, bool symbolPlusParenthesesAreFunctions); void defaultParseLeftParenthesis(bool isSystemParenthesis, Expression & leftHandSide, Token::Type stoppingType); diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 495aa697a..c7362383e 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -127,6 +127,32 @@ bool PowerNode::childAtIndexNeedsUserParentheses(const Expression & child, int c // Private +template +Complex PowerNode::computeNotPrincipalRealRootOfRationalPow(const std::complex c, T p, T q) { + // Assert p and q are in fact integers + assert(std::round(p) == p); + assert(std::round(q) == q); + /* Try to find a real root of c^(p/q) with p, q integers. We ignore cases + * where the principal root is real as these cases are handled generically + * later (for instance 1232^(1/8) which has a real principal root is not + * handled here). */ + if (c.imag() == 0 && std::pow((T)-1.0, q) < 0.0) { + /* If c real and q odd integer (q odd if (-1)^q = -1), a real root does + * exist (which is not necessarily the principal root)! + * For q even integer, a real root does not necessarily exist (example: + * -2 ^(1/2)). */ + std::complex absc = c; + absc.real(std::fabs(absc.real())); + // compute |c|^(p/q) which is a real + Complex absCPowD = PowerNode::compute(absc, std::complex(p/q), Preferences::ComplexFormat::Real); + /* As q is odd, c^(p/q) = (sign(c)^(1/q))^p * |c|^(p/q) + * = sign(c)^p * |c|^(p/q) + * = -|c|^(p/q) iff c < 0 and p odd */ + return c.real() < 0 && std::pow((T)-1.0, p) < 0.0 ? Complex::Builder(-absCPowD.stdComplex()) : absCPowD; + } + return Complex::Undefined(); +} + template Complex PowerNode::compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat) { std::complex result; @@ -151,8 +177,13 @@ Complex PowerNode::compute(const std::complex c, const std::complex d, * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to * avoid weird results as e(i*pi) = -1+6E-17*i, we compute the argument of * the result of c^d and if arg ~ 0 [Pi], we discard the residual imaginary - * part and if arg ~ Pi/2 [Pi], we discard the residual real part. */ - return Complex::Builder(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result)); + * part and if arg ~ Pi/2 [Pi], we discard the residual real part. + * Let's determine when the arg [Pi] (or arg [Pi/2]) is negligeable: + * With c = r*e^(iθ) and d = x+iy, c^d = r^x*e^(yθ)*e^i(yln(r)+xθ) + * so arg(c^d) = y*ln(r)+xθ. + * We consider that arg[π] is negligeable if it is negligeable compared to + * norm(d) = sqrt(x^2+y^2) and ln(r) = ln(norm(c)).*/ + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, c, d)); } // Layout @@ -257,6 +288,50 @@ template MatrixComplex PowerNode::computeOnMatrices(const MatrixC return MatrixComplex::Undefined(); } +template Evaluation PowerNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + /* Special case: c^(p/q) with p, q integers + * In real mode, c^(p/q) might have a real root which is not the principal + * root. We return this value in that case to avoid returning "unreal". */ + if (complexFormat == Preferences::ComplexFormat::Real) { + Evaluation base = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + if (base.type() != EvaluationNode::Type::Complex) { + goto defaultApproximation; + } + std::complex c = static_cast &>(base).stdComplex(); + T p = NAN; + T q = NAN; + // If the power has been reduced, we look for a rational index + if (childAtIndex(1)->type() == ExpressionNode::Type::Rational) { + const RationalNode * r = static_cast(childAtIndex(1)); + p = r->signedNumerator().approximate(); + q = r->denominator().approximate(); + } + /* If the power has been simplified (reduced + beautified), we look for an + * index of the for Division(Rational,Rational). */ + if (childAtIndex(1)->type() == ExpressionNode::Type::Division && childAtIndex(1)->childAtIndex(0)->type() == ExpressionNode::Type::Rational && childAtIndex(1)->childAtIndex(1)->type() == ExpressionNode::Type::Rational) { + const RationalNode * pRat = static_cast(childAtIndex(1)->childAtIndex(0)); + const RationalNode * qRat = static_cast(childAtIndex(1)->childAtIndex(1)); + if (!pRat->denominator().isOne() || !qRat->denominator().isOne()) { + goto defaultApproximation; + } + p = pRat->signedNumerator().approximate(); + q = qRat->signedNumerator().approximate(); + } + /* We don't handle power that haven't been reduced or simplified as the + * index can take to many forms and still be equivalent to p/q, + * with p, q integers. */ + if (std::isnan(p) || std::isnan(q)) { + goto defaultApproximation; + } + Complex result = computeNotPrincipalRealRootOfRationalPow(c, p, q); + if (!result.isUndefined()) { + return result; + } + } +defaultApproximation: + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); +} + // Power Expression Power::setSign(ExpressionNode::Sign s, ExpressionNode::ReductionContext reductionContext) { @@ -896,20 +971,6 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont return result; } - /* Optional Step 3: if the ReductionTarget is the SystemForApproximation or - * SystemForAnalysis, turn a^(p/q) into (root(a, q))^p - * Indeed, root(a, q) can have a real root which is not the principale angle - * but that we want to return in real complex format. This special case is - * handled in NthRoot approximation but not in Power approximation. */ - if (reductionContext.target() != ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) { - Integer p = childAtIndex(1).convert().signedIntegerNumerator(); - Integer q = childAtIndex(1).convert().integerDenominator(); - Expression nthRoot = q.isOne() ? childAtIndex(0) : NthRoot::Builder(childAtIndex(0), Rational::Builder(q)); - Expression result = p.isOne() ? nthRoot : Power::Builder(nthRoot, Rational::Builder(p)); - replaceWithInPlace(result); - return result; - } - // Step 4: Force Float(1) in front of an orphan Power of Unit if (parent().isUninitialized() && childAtIndex(0).type() == ExpressionNode::Type::Unit) { Multiplication m = Multiplication::Builder(Float::Builder(1.0)); diff --git a/poincare/src/sine.cpp b/poincare/src/sine.cpp index a4701e0c6..3668d8a3a 100644 --- a/poincare/src/sine.cpp +++ b/poincare/src/sine.cpp @@ -19,7 +19,7 @@ template Complex SineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); std::complex res = std::sin(angleInput); - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(res, angleInput)); } Layout SineNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { diff --git a/poincare/src/solver.cpp b/poincare/src/solver.cpp index d78b1e5bf..33914717e 100644 --- a/poincare/src/solver.cpp +++ b/poincare/src/solver.cpp @@ -133,12 +133,12 @@ double Solver::BrentRoot(double ax, double bx, double precision, ValueAtAbscissa double xm = 0.5*(c-b); if (std::fabs(xm) <= tol1 || fb == 0.0) { double fbcMiddle = evaluation(0.5*(b+c), context, complexFormat, angleUnit, context1, context2, context3); - double isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb); + bool isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb); if (isContinuous) { return b; } } - if (std::fabs(e) >= tol1 && std::fabs(fa) > std::fabs(b)) { + if (std::fabs(e) >= tol1 && std::fabs(fa) > std::fabs(fb)) { double s = fb/fa; double p = 2.0*xm*s; double q = 1.0-s; @@ -168,7 +168,7 @@ double Solver::BrentRoot(double ax, double bx, double precision, ValueAtAbscissa if (std::fabs(d) > tol1) { b += d; } else { - b += xm > 0.0 ? tol1 : tol1; + b += xm > 0.0 ? tol1 : -tol1; } fb = evaluation(b, context, complexFormat, angleUnit, context1, context2, context3); } diff --git a/poincare/src/square_root.cpp b/poincare/src/square_root.cpp index ed0954476..0c8361d1e 100644 --- a/poincare/src/square_root.cpp +++ b/poincare/src/square_root.cpp @@ -30,13 +30,7 @@ int SquareRootNode::serialize(char * buffer, int bufferSize, Preferences::PrintF template Complex SquareRootNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex result = std::sqrt(c); - /* Openbsd trigonometric functions are numerical implementation and thus are - * approximative. - * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to avoid - * weird results as sqrt(-1) = 6E-16+i, we compute the argument of the result - * of sqrt(c) and if arg ~ 0 [Pi], we discard the residual imaginary part and - * if arg ~ Pi/2 [Pi], we discard the residual real part.*/ - return Complex::Builder(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(result, std::complex(std::log(std::abs(c)), std::arg(c)))); } Expression SquareRootNode::shallowReduce(ReductionContext reductionContext) { diff --git a/poincare/src/tangent.cpp b/poincare/src/tangent.cpp index 0d44b0a5a..e140a4992 100644 --- a/poincare/src/tangent.cpp +++ b/poincare/src/tangent.cpp @@ -30,7 +30,7 @@ template Complex TangentNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); std::complex res = std::tan(angleInput); - return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); + return Complex::Builder(ApproximationHelper::NeglectRealOrImaginaryPartIfNeglectable(res, angleInput)); } Expression TangentNode::shallowReduce(ReductionContext reductionContext) { diff --git a/poincare/src/tree_pool.cpp b/poincare/src/tree_pool.cpp index 00895e05d..7ab75d0d7 100644 --- a/poincare/src/tree_pool.cpp +++ b/poincare/src/tree_pool.cpp @@ -14,7 +14,7 @@ namespace Poincare { TreePool * TreePool::SharedStaticPool = nullptr; void TreePool::freeIdentifier(uint16_t identifier) { - if (identifier >= 0 && identifier < MaxNumberOfNodes) { + if (TreeNode::IsValidIdentifier(identifier) && identifier < MaxNumberOfNodes) { m_nodeForIdentifierOffset[identifier] = UINT16_MAX; m_identifiers.push(identifier); } diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index dd802f97b..168f7e49d 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -412,34 +412,9 @@ std::complex Trigonometry::ConvertRadianToAngleUnit(const std::complex c, return c; } -template -T Trigonometry::RoundToMeaningfulDigits(T result, T input) { - /* Cheat: openbsd trigonometric functions are numerical implementation and - * thus are approximative. - * The error epsilon is ~1E-7 on float and ~1E-15 on double. In order to avoid - * weird results as acos(1) = 6E-17 or cos(π/2) = 4E-17, we round the result - * to its 1E-6 or 1E-14 precision when its ratio with the argument (π/2 in the - * example) is smaller than epsilon. This way, we have sin(π) ~ 0 and - * sin(1E-15)=1E-15. - * We can't do that for all evaluation as the user can operate on values as - * small as 1E-308 (in double) and most results still be correct. */ - if (input == 0.0 || std::fabs(result/input) <= Expression::Epsilon()) { - T precision = 10*Expression::Epsilon(); - result = std::round(result/precision)*precision; - } - return result; -} - -template -std::complex Trigonometry::RoundToMeaningfulDigits(const std::complex result, const std::complex input) { - return std::complex(RoundToMeaningfulDigits(result.real(), std::abs(input)), RoundToMeaningfulDigits(result.imag(), std::abs(input))); -} - template std::complex Trigonometry::ConvertToRadian(std::complex, Preferences::AngleUnit); template std::complex Trigonometry::ConvertToRadian(std::complex, Preferences::AngleUnit); template std::complex Trigonometry::ConvertRadianToAngleUnit(std::complex, Preferences::AngleUnit); template std::complex Trigonometry::ConvertRadianToAngleUnit(std::complex, Preferences::AngleUnit); -template std::complex Trigonometry::RoundToMeaningfulDigits(std::complex, std::complex); -template std::complex Trigonometry::RoundToMeaningfulDigits(std::complex, std::complex); } diff --git a/poincare/src/vertical_offset_layout.cpp b/poincare/src/vertical_offset_layout.cpp index 538855a04..df163f402 100644 --- a/poincare/src/vertical_offset_layout.cpp +++ b/poincare/src/vertical_offset_layout.cpp @@ -156,11 +156,9 @@ int VerticalOffsetLayoutNode::serialize(char * buffer, int bufferSize, Preferenc if (bufferSize == 1) { return 0; } - // If the layout is a subscript, write "_{indice}" - int numberOfChar = SerializationHelper::CodePoint(buffer, bufferSize, '_'); - if (numberOfChar >= bufferSize-1) { return bufferSize-1; } - numberOfChar += SerializationHelper::CodePoint(buffer+numberOfChar, bufferSize-numberOfChar, '{'); + // If the layout is a subscript, write "{indice}" + int numberOfChar = SerializationHelper::CodePoint(buffer, bufferSize, '{'); if (numberOfChar >= bufferSize-1) { return bufferSize-1; } numberOfChar += const_cast(this)->indiceLayout()->serialize(buffer+numberOfChar, bufferSize-numberOfChar, floatDisplayMode, numberOfSignificantDigits); diff --git a/poincare/test/approximation.cpp b/poincare/test/approximation.cpp index 89bc3b8ac..723027d6e 100644 --- a/poincare/test/approximation.cpp +++ b/poincare/test/approximation.cpp @@ -149,6 +149,12 @@ QUIZ_CASE(poincare_approximation_power) { assert_expression_approximates_to_scalar("2^3", 8.0f); assert_expression_approximates_to_scalar("(3+𝐢)^(4+𝐢)", NAN); assert_expression_approximates_to_scalar("[[1,2][3,4]]^2", NAN); + + + assert_expression_approximates_to("(-10)^0.00000001", "unreal", Radian, Real); + assert_expression_approximates_to("(-10)^0.00000001", "1+3.141593ᴇ-8×𝐢", Radian, Cartesian); + assert_expression_simplifies_approximates_to("3.5^2.0000001", "12.25"); + assert_expression_simplifies_approximates_to("3.7^2.0000001", "13.69"); } QUIZ_CASE(poincare_approximation_subtraction) { @@ -772,6 +778,13 @@ QUIZ_CASE(poincare_approximation_trigonometry_functions) { assert_expression_approximates_to("atanh(𝐢-4)", "-0.238878+1.50862×𝐢", Radian, Cartesian, 6); assert_expression_approximates_to("atanh(𝐢-4)", "-0.238878+1.50862×𝐢", Degree, Cartesian, 6); + // Check that the complex part is not neglected + assert_expression_approximates_to("atanh(0.99999999999+1.0ᴇ-26×𝐢)", "13.01+5ᴇ-16×𝐢", Radian, Cartesian, 4); + assert_expression_approximates_to("atanh(0.99999999999+1.0ᴇ-60×𝐢)", "13.01+5ᴇ-50×𝐢", Radian, Cartesian, 4); + assert_expression_approximates_to("atanh(0.99999999999+1.0ᴇ-150×𝐢)", "13.01+5ᴇ-140×𝐢", Radian, Cartesian, 4); + assert_expression_approximates_to("atanh(0.99999999999+1.0ᴇ-250×𝐢)", "13.01+5ᴇ-240×𝐢", Radian, Cartesian, 4); + assert_expression_approximates_to("atanh(0.99999999999+1.0ᴇ-300×𝐢)", "13.01+5ᴇ-290×𝐢", Radian, Cartesian, 4); + // WARNING: evaluate on branch cut can be multivalued assert_expression_approximates_to("acos(2)", "1.3169578969248×𝐢", Radian); assert_expression_approximates_to("acos(2)", "75.456129290217×𝐢", Degree); @@ -831,11 +844,12 @@ QUIZ_CASE(poincare_approximation_complex_format) { assert_expression_approximates_to("√(-1)", "unreal", Radian, Real); assert_expression_approximates_to("√(-1)×√(-1)", "unreal", Radian, Real); assert_expression_approximates_to("ln(-2)", "unreal", Radian, Real); - assert_expression_approximates_to("(-8)^(1/3)", "unreal", Radian, Real); // Power always approximates to the principal root (even if unreal) + // Power/Root approximates to the first REAL root in Real mode + assert_expression_simplifies_approximates_to("(-8)^(1/3)", "-2", Radian, Real); // Power have to be simplified first in order to spot the right form c^(p/q) with p, q integers assert_expression_approximates_to("root(-8,3)", "-2", Radian, Real); // Root approximates to the first REAL root in Real mode assert_expression_approximates_to("8^(1/3)", "2", Radian, Real); - assert_expression_approximates_to("(-8)^(2/3)", "unreal", Radian, Real); // Power always approximates to the principal root (even if unreal) - assert_expression_approximates_to("root(-8, 3)^2", "4", Radian, Real); // Root approximates to the first REAL root in Real mode + assert_expression_simplifies_approximates_to("(-8)^(2/3)", "4", Radian, Real); // Power have to be simplified first (cf previous comment) + assert_expression_approximates_to("root(-8, 3)^2", "4", Radian, Real); assert_expression_approximates_to("root(-8,3)", "-2", Radian, Real); // Cartesian @@ -924,6 +938,19 @@ QUIZ_CASE(poincare_approximation_mix) { assert_expression_approximates_to("sin(3)2(4+2)", "1.6934400967184", Radian); assert_expression_approximates_to("4/2×(2+3)", "10"); assert_expression_approximates_to("4/2×(2+3)", "10"); + + assert_expression_simplifies_and_approximates_to("1.0092^(20)", "1.2010050593402"); + assert_expression_simplifies_and_approximates_to("1.0092^(50)×ln(3/2)", "0.6409373488899", Degree, Cartesian, 13); + assert_expression_simplifies_and_approximates_to("1.0092^(50)×ln(1.0092)", "1.447637354655ᴇ-2", Degree, Cartesian, 13); + assert_expression_approximates_to("1.0092^(20)", "1.2010050593402"); + assert_expression_approximates_to("1.0092^(50)×ln(3/2)", "0.6409373488899", Degree, Cartesian, 13); + assert_expression_approximates_to("1.0092^(50)×ln(1.0092)", "1.447637354655ᴇ-2", Degree, Cartesian, 13); + assert_expression_simplifies_approximates_to("1.0092^(20)", "1.2010050593402"); + assert_expression_simplifies_approximates_to("1.0092^(50)×ln(3/2)", "0.6409373488899", Degree, Cartesian, 13); + //assert_expression_approximates_to("1.0092^(20)", "1.201005"); TODO does not work + assert_expression_approximates_to("1.0092^(50)×ln(3/2)", "0.6409366"); + //assert_expression_simplifies_approximates_to("1.0092^(20)", "1.2010050593402"); TODO does not work + //assert_expression_simplifies_approximates_to("1.0092^(50)×ln(3/2)", "6.4093734888993ᴇ-1"); TODO does not work } diff --git a/poincare/test/function_solver.cpp b/poincare/test/function_solver.cpp index e95d77b30..4dee3c65f 100644 --- a/poincare/test/function_solver.cpp +++ b/poincare/test/function_solver.cpp @@ -1,13 +1,13 @@ #include -#include #include "helper.h" using namespace Poincare; -enum class ExtremumType : uint8_t { +enum class PointOfInterestType { Maximum, Minimum, - Root + Root, + Intersection, }; bool doubles_are_approximately_equal(double d1, double d2) { @@ -21,220 +21,267 @@ bool doubles_are_approximately_equal(double d1, double d2) { return std::abs(d1-d2) < 0.00001; } -void assert_next_extrema_are( - ExtremumType extremumType, - int numberOfExtrema, - Coordinate2D * extrema, - Expression e, +void assert_points_of_interest_are( + PointOfInterestType type, + int numberOfPointsOfInterest, + Coordinate2D * pointsOfInterest, + const char * expression1, + const char * expression2, const char * symbol, - Context * context, - double start = -1.0, - double step = 0.1, - double max = 100.0, + double start, + double step, + double max, Preferences::ComplexFormat complexFormat = Preferences::ComplexFormat::Real, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) { - double currentStart = start; - for (int i = 0; i < numberOfExtrema; i++) { - quiz_assert_log_if_failure(!std::isnan(currentStart), e); - Coordinate2D nextExtrema; - if (extremumType == ExtremumType::Maximum) { - nextExtrema = e.nextMaximum(symbol, currentStart, step, max, context, complexFormat, angleUnit); - } else if (extremumType == ExtremumType::Minimum) { - nextExtrema = e.nextMinimum(symbol, currentStart, step, max, context, complexFormat, angleUnit); - } else if (extremumType == ExtremumType::Root) { - nextExtrema = Coordinate2D(e.nextRoot(symbol, currentStart, step, max, context, complexFormat, angleUnit), 0.0 ); + Shared::GlobalContext context; + Poincare::Expression e1 = parse_expression(expression1, &context, false); + Poincare::Expression e2; + if (expression2) { + assert(type == PointOfInterestType::Intersection); + e2 = parse_expression(expression2, &context, false); + } + for (int i = 0; i < numberOfPointsOfInterest; i++) { + quiz_assert_log_if_failure(!std::isnan(start), e1); + Coordinate2D nextPointOfInterest; + if (type == PointOfInterestType::Maximum) { + nextPointOfInterest = e1.nextMaximum(symbol, start, step, max, &context, complexFormat, angleUnit); + } else if (type == PointOfInterestType::Minimum) { + nextPointOfInterest = e1.nextMinimum(symbol, start, step, max, &context, complexFormat, angleUnit); + } else if (type == PointOfInterestType::Root) { + nextPointOfInterest = Coordinate2D(e1.nextRoot(symbol, start, step, max, &context, complexFormat, angleUnit), 0.0); + } else if (type == PointOfInterestType::Intersection) { + nextPointOfInterest = e1.nextIntersection(symbol, start, step, max, &context, complexFormat, angleUnit, e2); } - currentStart = nextExtrema.x1() + step; quiz_assert_log_if_failure( - (doubles_are_approximately_equal(extrema[i].x1(), nextExtrema.x1())) - && (doubles_are_approximately_equal(extrema[i].x2(), nextExtrema.x2())), - e); + doubles_are_approximately_equal(pointsOfInterest[i].x1(), nextPointOfInterest.x1()) && + doubles_are_approximately_equal(pointsOfInterest[i].x2(), nextPointOfInterest.x2()), + e1); + start = nextPointOfInterest.x1() + step; } } QUIZ_CASE(poincare_function_extremum) { - const char * symbol = "a"; - int symbolLength = strlen(symbol); - Shared::GlobalContext globalContext; { - // cos - Expression e = Cosine::Builder(Symbol::Builder(symbol, symbolLength)); { constexpr int numberOfMaxima = 3; Coordinate2D maxima[numberOfMaxima] = { Coordinate2D(0.0, 1.0), Coordinate2D(360.0, 1.0), Coordinate2D(NAN, NAN)}; - assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext, -1.0, 0.1, 500.0); + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "cos(a)", nullptr, "a", -1.0, 0.1, 500.0); + } + { + constexpr int numberOfMaxima = 3; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(360.0, 1.0), + Coordinate2D(0.0, 1.0), + Coordinate2D(NAN, NAN)}; + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "cos(a)", nullptr, "a", 500.0, -0.1, -1.0); } { constexpr int numberOfMinima = 1; Coordinate2D minima[numberOfMinima] = { Coordinate2D(180.0, -1.0)}; - assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext, 0.0, 0.1, 300.0); + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "cos(a)", nullptr, "a", 0.0, 0.1, 300.0); + } + { + constexpr int numberOfMinima = 1; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(180.0, -1.0)}; + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "cos(a)", nullptr, "a", 300.0, -0.1, 0.0); } } { - // x^2 - Expression e = Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)); { constexpr int numberOfMaxima = 1; Coordinate2D maxima[numberOfMaxima] = { Coordinate2D(NAN, NAN)}; - assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "a^2", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMaxima = 1; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, NAN)}; + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "a^2", nullptr, "a", 100.0, -0.1, -1.0); } { constexpr int numberOfMinima = 1; Coordinate2D minima[numberOfMinima] = { Coordinate2D(0.0, 0.0)}; - assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "a^2", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMinima = 1; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(0.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "a^2", nullptr, "a", 100.0, -0.1, -1.0); } } - { - // 3 - Expression e = Rational::Builder(3); { constexpr int numberOfMaxima = 1; Coordinate2D maxima[numberOfMaxima] = { Coordinate2D(NAN, 3.0)}; - assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "3", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMaxima = 1; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, 3.0)}; + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "3", nullptr, "a", 100.0, -0.1, -1.0); } { constexpr int numberOfMinima = 1; Coordinate2D minima[numberOfMinima] = { Coordinate2D(NAN, 3.0)}; - assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "3", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMinima = 1; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(NAN, 3.0)}; + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "3", nullptr, "a", 100.0, -0.1, -1.0); } } - { - // 0 - Expression e = Rational::Builder(0); { constexpr int numberOfMaxima = 1; Coordinate2D maxima[numberOfMaxima] = { Coordinate2D(NAN, 0.0)}; - assert_next_extrema_are(ExtremumType::Maximum, numberOfMaxima, maxima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "0", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMaxima = 1; + Coordinate2D maxima[numberOfMaxima] = { + Coordinate2D(NAN, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Maximum, numberOfMaxima, maxima, "0", nullptr, "a", 100.0, -0.1, -1.0); } { constexpr int numberOfMinima = 1; Coordinate2D minima[numberOfMinima] = { Coordinate2D(NAN, 0.0)}; - assert_next_extrema_are(ExtremumType::Minimum, numberOfMinima, minima, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "0", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfMinima = 1; + Coordinate2D minima[numberOfMinima] = { + Coordinate2D(NAN, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Minimum, numberOfMinima, minima, "0", nullptr, "a", 100.0, -0.1, -1.0); } } } QUIZ_CASE(poincare_function_root) { - const char * symbol = "a"; - int symbolLength = strlen(symbol); - Shared::GlobalContext globalContext; { - // cos - Expression e = Cosine::Builder(Symbol::Builder(symbol, symbolLength)); constexpr int numberOfRoots = 3; Coordinate2D roots[numberOfRoots] = { Coordinate2D(90.0, 0.0), Coordinate2D(270.0, 0.0), Coordinate2D(450.0, 0.0)}; - assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext, 0.0, 0.1, 500.0); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "cos(a)", nullptr, "a", 0.0, 0.1, 500.0); + } + { + constexpr int numberOfRoots = 3; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(450.0, 0.0), + Coordinate2D(270.0, 0.0), + Coordinate2D(90.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "cos(a)", nullptr, "a", 500.0, -0.1, 0.0); } { - // x^2 - Expression e = Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)); constexpr int numberOfRoots = 1; Coordinate2D roots[numberOfRoots] = { Coordinate2D(0.0, 0.0)}; - assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "a^2", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfRoots = 1; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(0.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "a^2", nullptr, "a", 100.0, -0.1, -1.0); } { - // x^2-4 - Expression e = Subtraction::Builder(Power::Builder(Symbol::Builder(symbol, symbolLength), Rational::Builder(2)), Rational::Builder(4)); constexpr int numberOfRoots = 2; Coordinate2D roots[numberOfRoots] = { Coordinate2D(-2.0, 0.0), Coordinate2D(2.0, 0.0)}; - assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext, -5.0); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "a^2-4", nullptr, "a", -5.0, 0.1, 100.0); + } + { + constexpr int numberOfRoots = 2; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(2.0, 0.0), + Coordinate2D(-2.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "a^2-4", nullptr, "a", 100.0, -0.1, -5.0); } { - // 3 - Expression e = Rational::Builder(3); constexpr int numberOfRoots = 1; Coordinate2D roots[numberOfRoots] = { Coordinate2D(NAN, 0.0)}; - assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "3", nullptr, "a", -1.0, 0.1, 100.0); + } + { + constexpr int numberOfRoots = 1; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(NAN, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "3", nullptr, "a", 100.0, -0.1, -1.0); } - { - // 0 - Expression e = Rational::Builder(0); constexpr int numberOfRoots = 1; Coordinate2D roots[numberOfRoots] = { Coordinate2D(-0.9, 0.0)}; - assert_next_extrema_are(ExtremumType::Root, numberOfRoots, roots, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "0", nullptr, "a", -1.0, 0.1, 100.0); } - -} - -void assert_next_intersections_are( - Expression otherExpression, - int numberOfIntersections, - Coordinate2D * intersections, - Expression e, - const char * symbol, - Context * context, - double start = -1.0, - double step = 0.1, - double max = 500.0, - Preferences::ComplexFormat complexFormat = Preferences::ComplexFormat::Real, - Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) -{ - double currentStart = start; - for (int i = 0; i < numberOfIntersections; i++) { - quiz_assert_log_if_failure(!std::isnan(currentStart), e); - Coordinate2D nextIntersection = e.nextIntersection(symbol, currentStart, step, max, context, complexFormat, angleUnit, otherExpression); - currentStart = nextIntersection.x1() + step; - quiz_assert_log_if_failure( - (doubles_are_approximately_equal(intersections[i].x1(), nextIntersection.x1())) - && (doubles_are_approximately_equal(intersections[i].x2(), nextIntersection.x2())), - e); - } -} -QUIZ_CASE(poincare_function_intersection) { - const char * symbol = "a"; - int symbolLength = strlen(symbol); - Shared::GlobalContext globalContext; - Expression e = Cosine::Builder(Symbol::Builder(symbol, symbolLength)); - { - // cos with y=2 - Expression otherExpression = Rational::Builder(2); + constexpr int numberOfRoots = 1; + Coordinate2D roots[numberOfRoots] = { + Coordinate2D(99.8, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Root, numberOfRoots, roots, "0", nullptr, "a", 100.0, -0.1, -1.0); + } +} + +QUIZ_CASE(poincare_function_intersection) { + { constexpr int numberOfIntersections = 1; Coordinate2D intersections[numberOfIntersections] = { Coordinate2D(NAN, NAN)}; - assert_next_intersections_are(otherExpression, numberOfIntersections, intersections, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "2", "a", -1.0, 0.1, 500.0); + } + { + constexpr int numberOfIntersections = 1; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(NAN, NAN)}; + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "2", "a", 500.0, -0.1, -1.0); } - { - // cos with y=1 - Expression otherExpression = Rational::Builder(1); constexpr int numberOfIntersections = 2; Coordinate2D intersections[numberOfIntersections] = { Coordinate2D(0.0, 1.0), Coordinate2D(360.0, 1.0)}; - assert_next_intersections_are(otherExpression, numberOfIntersections, intersections, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "1", "a", -1.0, 0.1, 500.0); + } + { + constexpr int numberOfIntersections = 2; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(360.0, 1.0), + Coordinate2D(0.0, 1.0)}; + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "1", "a", 500.0, -0.1, -1.0); } - { - // cos with y=0 - Expression otherExpression = Rational::Builder(0); constexpr int numberOfIntersections = 3; Coordinate2D intersections[numberOfIntersections] = { Coordinate2D(90.0, 0.0), Coordinate2D(270.0, 0.0), Coordinate2D(450.0, 0.0)}; - assert_next_intersections_are(otherExpression, numberOfIntersections, intersections, e, symbol, &globalContext); + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "0", "a", -1.0, 0.1, 500.0); + } + { + constexpr int numberOfIntersections = 3; + Coordinate2D intersections[numberOfIntersections] = { + Coordinate2D(450.0, 0.0), + Coordinate2D(270.0, 0.0), + Coordinate2D(90.0, 0.0)}; + assert_points_of_interest_are(PointOfInterestType::Intersection, numberOfIntersections, intersections, "cos(a)", "0", "a", 500.0, -0.1, -1.0); } } diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index ecd631b65..67d035b41 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -108,6 +108,26 @@ void assert_expression_approximates_to(const char * expression, const char * app }, numberOfDigits); } +void assert_expression_simplifies_and_approximates_to(const char * expression, const char * approximation, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) { + int numberOfDigits = numberOfSignificantDigits > 0 ? numberOfSignificantDigits : PrintFloat::k_numberOfStoredSignificantDigits; + assert_parsed_expression_process_to(expression, approximation, SystemForApproximation, complexFormat, angleUnit, ReplaceAllSymbolsWithDefinitionsOrUndefined, [](Expression e, ExpressionNode::ReductionContext reductionContext) { + Expression reduced; + Expression approximated; + e.simplifyAndApproximate(&reduced, &approximated, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), reductionContext.symbolicComputation()); + return approximated; + }, numberOfDigits); +} + +template +void assert_expression_simplifies_approximates_to(const char * expression, const char * approximation, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) { + int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits; + numberOfDigits = numberOfSignificantDigits > 0 ? numberOfSignificantDigits : numberOfDigits; + assert_parsed_expression_process_to(expression, approximation, SystemForApproximation, complexFormat, angleUnit, ReplaceAllSymbolsWithDefinitionsOrUndefined, [](Expression e, ExpressionNode::ReductionContext reductionContext) { + e = e.simplify(reductionContext); + return e.approximate(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + }, numberOfDigits); +} + void assert_layout_serialize_to(Poincare::Layout layout, const char * serialization) { constexpr int bufferSize = 255; char buffer[bufferSize]; @@ -122,3 +142,5 @@ void assert_expression_layouts_as(Poincare::Expression expression, Poincare::Lay template void assert_expression_approximates_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); template void assert_expression_approximates_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); +template void assert_expression_simplifies_approximates_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); +template void assert_expression_simplifies_approximates_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); diff --git a/poincare/test/helper.h b/poincare/test/helper.h index cdac8d201..5e7f5942e 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -43,6 +43,9 @@ void assert_parsed_expression_simplify_to(const char * expression, const char * template void assert_expression_approximates_to(const char * expression, const char * approximation, Poincare::Preferences::AngleUnit angleUnit = Degree, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, int numberOfSignificantDigits = -1); +void assert_expression_simplifies_and_approximates_to(const char * expression, const char * approximation, Poincare::Preferences::AngleUnit angleUnit = Degree, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, int numberOfSignificantDigits = -1); +template +void assert_expression_simplifies_approximates_to(const char * expression, const char * approximation, Poincare::Preferences::AngleUnit angleUnit = Degree, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, int numberOfSignificantDigits = -1); // Layout serializing diff --git a/poincare/test/parsing.cpp b/poincare/test/parsing.cpp index bc673d76b..328d50661 100644 --- a/poincare/test/parsing.cpp +++ b/poincare/test/parsing.cpp @@ -385,7 +385,7 @@ QUIZ_CASE(poincare_parsing_identifiers) { assert_parsed_expression_is("ln(1)", NaperianLogarithm::Builder(BasedInteger::Builder(1))); assert_parsed_expression_is("log(1)", CommonLogarithm::Builder(BasedInteger::Builder(1))); assert_parsed_expression_is("log(1,2)", Logarithm::Builder(BasedInteger::Builder(1),BasedInteger::Builder(2))); - assert_parsed_expression_is("log_{2}(1)", Logarithm::Builder(BasedInteger::Builder(1),BasedInteger::Builder(2))); + assert_parsed_expression_is("log{2}(1)", Logarithm::Builder(BasedInteger::Builder(1),BasedInteger::Builder(2))); assert_parsed_expression_is("permute(2,1)", PermuteCoefficient::Builder(BasedInteger::Builder(2),BasedInteger::Builder(1))); assert_parsed_expression_is("prediction95(1,2)", PredictionInterval::Builder(BasedInteger::Builder(1),BasedInteger::Builder(2))); assert_parsed_expression_is("prediction(1,2)", SimplePredictionInterval::Builder(BasedInteger::Builder(1),BasedInteger::Builder(2))); diff --git a/poincare/test/simplification.cpp b/poincare/test/simplification.cpp index 8808bda1e..cd5bd1c04 100644 --- a/poincare/test/simplification.cpp +++ b/poincare/test/simplification.cpp @@ -1025,10 +1025,10 @@ QUIZ_CASE(poincare_simplification_unit_convert) { assert_parsed_expression_simplify_to("1→u(n+1)", Undefined::Name()); assert_parsed_expression_simplify_to("1→v(n)", Undefined::Name()); assert_parsed_expression_simplify_to("1→v(n+1)", Undefined::Name()); - assert_parsed_expression_simplify_to("1→u_{n}", Undefined::Name()); - assert_parsed_expression_simplify_to("1→u_{n+1}", Undefined::Name()); - assert_parsed_expression_simplify_to("1→v_{n}", Undefined::Name()); - assert_parsed_expression_simplify_to("1→v_{n+1}", Undefined::Name()); + assert_parsed_expression_simplify_to("1→u{n}", Undefined::Name()); + assert_parsed_expression_simplify_to("1→u{n+1}", Undefined::Name()); + assert_parsed_expression_simplify_to("1→v{n}", Undefined::Name()); + assert_parsed_expression_simplify_to("1→v{n+1}", Undefined::Name()); assert_parsed_expression_simplify_to("1→inf", Undefined::Name()); assert_parsed_expression_simplify_to("1→undef", Undefined::Name()); assert_parsed_expression_simplify_to("1→π", Undefined::Name()); @@ -1231,8 +1231,8 @@ QUIZ_CASE(poincare_simplification_reduction_target) { assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User); // Apply rule x^(2/3) --> root(x,3)^2 for ReductionTarget = System - assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForApproximation); - assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForAnalysis); + assert_parsed_expression_simplify_to("x^(2/3)", "x^\u00122/3\u0013", SystemForApproximation); + assert_parsed_expression_simplify_to("x^(2/3)", "x^\u00122/3\u0013", SystemForAnalysis); assert_parsed_expression_simplify_to("x^(2/3)", "x^\u00122/3\u0013", User); assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForApproximation); assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForAnalysis);