diff --git a/apps/code/base.de.i18n b/apps/code/base.de.i18n index 155cd5ccd..5879c5745 100644 --- a/apps/code/base.de.i18n +++ b/apps/code/base.de.i18n @@ -1,8 +1,8 @@ Console = "Interaktive Konsole" -AddScript = "Skript hinzufuegen" +AddScript = "Skript hinzufügen" ScriptOptions = "Skriptoptionen" -ExecuteScript = "Skript ausfuehren" +ExecuteScript = "Skript ausführen" AutoImportScript = "Automatischer Import in Konsole" -DeleteScript = "Skript loeschen" +DeleteScript = "Skript löschen" FunctionsAndVariables = "Funktionen und Variablen" AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _" diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index e5a0be097..fa6a0999d 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -1,28 +1,28 @@ PythonPound = "Kommentar" PythonPercent = "Modulo" -Python1J = "Imaginaeres i" +Python1J = "Imaginäres i" PythonLF = "Zeilenvorschub" PythonTab = "Tabulator" PythonAmpersand = "Bitweise und" PythonSymbolExp = "Bitweise exklusiv oder" PythonVerticalBar = "Bitweise oder" -PythonImag = "Imaginaerteil von z" +PythonImag = "Imaginärteil von z" PythonReal = "Realteil von z" -PythonSingleQuote = "Einfaches Anfuehrungszeichen" -PythonAbs = "Absolute/r Wert/Groesse" +PythonSingleQuote = "Einfaches Anführungszeichen" +PythonAbs = "Absolute/r Wert/Größe" PythonAcos = "Arkuskosinus" PythonAcosh = "Hyperbelkosinus" PythonAsin = "Arkussinus" PythonAsinh = "Hyperbelsinus" PythonAtan = "Arkustangens" -PythonAtan2 = "Gib arctan(y/x)" +PythonAtan2 = "Gib atan(y/x)" PythonAtanh = "Hyperbeltangens" -PythonBin = "Ganzzahl nach binaer konvertieren" +PythonBin = "Ganzzahl nach binär konvertieren" PythonCeil = "Aufrundung" PythonChoice = "Zufallszahl aus der Liste" -PythonCmathFunction = "cmath-Modul-Funktionspraefix" +PythonCmathFunction = "cmath-Modul-Funktionspräfix" PythonColor = "Definiere eine RGB-Farbe" -PythonComplex = "a+ib zurueckgeben" +PythonComplex = "a+ib zurückgeben" PythonCopySign = "Return x with the sign of y" PythonCos = "Kosinus" PythonCosh = "Hyperbolic cosine" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 4fd635083..312851e59 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -15,7 +15,7 @@ PythonAcosh = "Arc hyperbolic cosine" PythonAsin = "Arc sine" PythonAsinh = "Arc hyperbolic sine" PythonAtan = "Arc tangent" -PythonAtan2 = "Return arctan(y/x)" +PythonAtan2 = "Return atan(y/x)" PythonAtanh = "Arc hyperbolic tangent" PythonBin = "Convert integer to binary" PythonCeil = "Ceiling" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index 4fd635083..312851e59 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -15,7 +15,7 @@ PythonAcosh = "Arc hyperbolic cosine" PythonAsin = "Arc sine" PythonAsinh = "Arc hyperbolic sine" PythonAtan = "Arc tangent" -PythonAtan2 = "Return arctan(y/x)" +PythonAtan2 = "Return atan(y/x)" PythonAtanh = "Arc hyperbolic tangent" PythonBin = "Convert integer to binary" PythonCeil = "Ceiling" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index cdc0899ad..89d307345 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -15,7 +15,7 @@ PythonAcosh = "Arc cosinus hyperbolique" PythonAsin = "Arc sinus" PythonAsinh = "Arc sinus hyperbolique" PythonAtan = "Arc tangente" -PythonAtan2 = "Calcul de arctan(y/x)" +PythonAtan2 = "Calcul de atan(y/x)" PythonAtanh = "Arc tangente hyperbolique" PythonBin = "Conversion d'un entier en binaire" PythonCeil = "Plafond" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index fd30d2b90..e77aaf28b 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -15,7 +15,7 @@ PythonAcosh = "Arc hyperbolic cosine" PythonAsin = "Arc sine" PythonAsinh = "Arc hyperbolic sine" PythonAtan = "Arc tangent" -PythonAtan2 = "Return arctan(y/x)" +PythonAtan2 = "Return atan(y/x)" PythonAtanh = "Arc hyperbolic tangent" PythonBin = "Convert integer to binary" PythonCeil = "Ceiling" diff --git a/apps/code/editor_controller.cpp b/apps/code/editor_controller.cpp index 854793fc9..00b4f562a 100644 --- a/apps/code/editor_controller.cpp +++ b/apps/code/editor_controller.cpp @@ -70,9 +70,19 @@ bool EditorController::textAreaDidReceiveEvent(TextArea * textArea, Ion::Events: /* If the cursor is on the left of the text of a line, backspace one * indentation space at a time. */ const char * text = textArea->text(); - const char * firstNonSpace = UTF8Helper::NotCodePointSearch(text, ' ', true, textArea->cursorLocation()); + const char * cursorLocation = textArea->cursorLocation(); + const char * firstNonSpace = UTF8Helper::NotCodePointSearch(text, ' ', true, cursorLocation); assert(firstNonSpace >= text); - if (UTF8Helper::CodePointIs(firstNonSpace, '\n') && ((text - firstNonSpace)/UTF8Decoder::CharSizeOfCodePoint(' ')) >= k_indentationSpacesNumber) { + bool cursorIsPrecededOnTheLineBySpacesOnly = false; + size_t numberOfSpaces = cursorLocation - firstNonSpace; + if (UTF8Helper::CodePointIs(firstNonSpace, '\n')) { + cursorIsPrecededOnTheLineBySpacesOnly = true; + numberOfSpaces -= UTF8Decoder::CharSizeOfCodePoint('\n'); + } else if (firstNonSpace == text) { + cursorIsPrecededOnTheLineBySpacesOnly = true; + } + numberOfSpaces = numberOfSpaces / UTF8Decoder::CharSizeOfCodePoint(' '); + if (cursorIsPrecededOnTheLineBySpacesOnly && numberOfSpaces >= k_indentationSpacesNumber) { for (int i = 0; i < k_indentationSpacesNumber; i++) { textArea->removeCodePoint(); } diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 01dfc7cdd..fa14d93dc 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -95,7 +95,7 @@ const ToolboxMessageTree MathModuleChildren[] = { const ToolboxMessageTree KandinskyModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportKandinsky, I18n::Message::PythonImportKandinsky, false), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromKandinsky, I18n::Message::PythonImportFromKandinsky, false), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKandinskyFunction, I18n::Message::PythonKandinskyFunction, false), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandKandinskyFunction, I18n::Message::PythonKandinskyFunction, false, I18n::Message::PythonCommandKandinskyFunctionWithoutArg), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetPixel, I18n::Message::PythonGetPixel), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSetPixel, I18n::Message::PythonSetPixel), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor), diff --git a/apps/graph/base.de.i18n b/apps/graph/base.de.i18n index e5ec488fa..47586b05a 100644 --- a/apps/graph/base.de.i18n +++ b/apps/graph/base.de.i18n @@ -1,8 +1,8 @@ FunctionApp = "Funktionen" FunctionAppCapital = "FUNKTIONEN" FunctionTab = "Funktionen" -AddFunction = "Funktion hinzufuegen" -DeleteFunction = "Funktion loeschen" +AddFunction = "Funktion hinzufügen" +DeleteFunction = "Funktion löschen" FunctionColor = "Farbe der Funktion" NoFunction = "Keine Funktion" NoActivatedFunction = "Keine aktive Funktion" diff --git a/apps/on_boarding/base.de.i18n b/apps/on_boarding/base.de.i18n index 0ae046cbd..bbf9040ad 100644 --- a/apps/on_boarding/base.de.i18n +++ b/apps/on_boarding/base.de.i18n @@ -1,6 +1,6 @@ -UpdateAvailable = "UPDATE VERFUEGBAR" -UpdateMessage1 = "Wichtige Verbesserungen fuer Ihren" -UpdateMessage2 = "Rechner stehen zur Verfuegung." +UpdateAvailable = "UPDATE VERFÜGBAR" +UpdateMessage1 = "Wichtige Verbesserungen für Ihren" +UpdateMessage2 = "Rechner stehen zur Verfügung." UpdateMessage3 = "Verbinden Sie sich mit Ihrem Computer auf" UpdateMessage4 = "www.numworks.com/update." BetaVersion = "BETA VERSION" @@ -10,4 +10,4 @@ BetaVersionMessage3 = "You might run into bugs or glitches." BetaVersionMessage4 = "" BetaVersionMessage5 = "Please send any feedback to" BetaVersionMessage6 = "contact@numworks.com" -Skip = "Ueberspringen" +Skip = "Überspringen" diff --git a/apps/probability/base.de.i18n b/apps/probability/base.de.i18n index 78de545c8..f0a406873 100644 --- a/apps/probability/base.de.i18n +++ b/apps/probability/base.de.i18n @@ -1,6 +1,6 @@ ProbaApp = "Wahrsch." ProbaAppCapital = "WAHRSCHEINLICHKEIT" -ChooseLaw = "Waehlen Sie eine Verteilung" +ChooseLaw = "Wählen Sie eine Verteilung" Binomial = "Binomial" Uniforme = "Uniform" Normal = "Normal" @@ -10,7 +10,7 @@ UniformLaw = "Uniformverteilung" ExponentialLaw = "Exponentialverteilung" NormalLaw = "Normalverteilung" PoissonLaw = "Poisson-Verteilung" -ChooseParameters = "Parameter auswaehlen" +ChooseParameters = "Parameter auswählen" RepetitionNumber = "n: Anzahl der Versuche" SuccessProbability = "p: Erfolgswahrscheinlichkeit" IntervalDefinition = "[a,b]: Intervall" diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 450d3a471..f31243396 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -13,6 +13,7 @@ app_src += $(addprefix apps/regression/,\ graph_options_controller.cpp \ graph_view.cpp \ initialisation_parameter_controller.cpp \ + linear_model_helper.cpp \ regression_context.cpp \ regression_controller.cpp \ store.cpp \ diff --git a/apps/regression/linear_model_helper.cpp b/apps/regression/linear_model_helper.cpp new file mode 100644 index 000000000..c0c975d2b --- /dev/null +++ b/apps/regression/linear_model_helper.cpp @@ -0,0 +1,17 @@ +#include "linear_model_helper.h" + +namespace Regression { + +namespace LinearModelHelper { + +double Slope(double covariance, double variance) { + return covariance / variance; +} + +double YIntercept(double meanOfY, double meanOfX, double slope) { + return meanOfY - slope * meanOfX; +} + +} + +} diff --git a/apps/regression/linear_model_helper.h b/apps/regression/linear_model_helper.h new file mode 100644 index 000000000..27ca1da55 --- /dev/null +++ b/apps/regression/linear_model_helper.h @@ -0,0 +1,15 @@ +#ifndef REGRESSION_LINEAR_MODEL_HELPER +#define REGRESSION_LINEAR_MODEL_HELPER + +namespace Regression { + +namespace LinearModelHelper { + +double Slope(double covariance, double variance); +double YIntercept(double meanOfY, double meanOfX, double slope); + +} + +} + +#endif diff --git a/apps/regression/model/exponential_model.cpp b/apps/regression/model/exponential_model.cpp index e29bdb047..608467a11 100644 --- a/apps/regression/model/exponential_model.cpp +++ b/apps/regression/model/exponential_model.cpp @@ -1,4 +1,6 @@ #include "exponential_model.h" +#include "../store.h" +#include "../linear_model_helper.h" #include #include #include @@ -46,6 +48,45 @@ double ExponentialModel::levelSet(double * modelCoefficients, double xMin, doubl return log(y/a)/b; } +void ExponentialModel::fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) { + /* By the change of variable z=ln(y), the equation y=a*exp(b*x) becomes + * z=c*x+d with c=b and d=ln(a). Although that change of variable does not + * preserve the regression error function, it turns an exponential regression + * problem into a linear one and we consider that the solution of the latter + * is good enough for our purpose. + * That being said, one should check that the y values are all positive. (If + * the y values are all negative, one may replace each of them by its + * opposite. In the case where y values happen to be zero or of opposite + * sign, we call the base class method as a fallback. */ + double sumOfX = 0; + double sumOfY = 0; + double sumOfXX = 0; + double sumOfXY = 0; + const int numberOfPoints = store->numberOfPairsOfSeries(series); + const int sign = store->get(series, 1, 0) > 0 ? 1 : -1; + for (int p = 0; p < numberOfPoints; p++) { + const double x = store->get(series, 0, p); + const double z = store->get(series, 1, p) * sign; + if (z <= 0) { + return Model::fit(store, series, modelCoefficients, context); + } + const double y = log(z); + sumOfX += x; + sumOfY += y; + sumOfXX += x*x; + sumOfXY += x*y; + } + const double meanOfX = sumOfX / numberOfPoints; + const double meanOfY = sumOfY / numberOfPoints; + const double meanOfXX = sumOfXX / numberOfPoints; + const double meanOfXY = sumOfXY / numberOfPoints; + const double variance = meanOfXX - meanOfX * meanOfX; + const double covariance = meanOfXY - meanOfX * meanOfY; + modelCoefficients[1] = LinearModelHelper::Slope(covariance, variance); + modelCoefficients[0] = + sign * exp(LinearModelHelper::YIntercept(meanOfY, meanOfX, modelCoefficients[1])); +} + double ExponentialModel::partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const { double a = modelCoefficients[0]; double b = modelCoefficients[1]; diff --git a/apps/regression/model/exponential_model.h b/apps/regression/model/exponential_model.h index 7d373b473..d7293ce35 100644 --- a/apps/regression/model/exponential_model.h +++ b/apps/regression/model/exponential_model.h @@ -12,6 +12,7 @@ public: I18n::Message formulaMessage() const override { return I18n::Message::ExponentialRegressionFormula; } double evaluate(double * modelCoefficients, double x) const override; double levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) override; + void fit(Store * store, int series, double * modelCoefficients, Poincare::Context * context) override; double partialDerivate(double * modelCoefficients, int derivateCoefficientIndex, double x) const override; int numberOfCoefficients() const override { return 2; } int bannerLinesCount() const override { return 2; } diff --git a/apps/regression/store.cpp b/apps/regression/store.cpp index 683849169..7174cb8d3 100644 --- a/apps/regression/store.cpp +++ b/apps/regression/store.cpp @@ -1,4 +1,5 @@ #include "store.h" +#include "linear_model_helper.h" #include "apps/apps_container.h" #include #include @@ -238,11 +239,11 @@ double Store::covariance(int series) const { } double Store::slope(int series) const { - return covariance(series)/varianceOfColumn(series, 0); + return LinearModelHelper::Slope(covariance(series), varianceOfColumn(series, 0)); } double Store::yIntercept(int series) const { - return meanOfColumn(series, 1) - slope(series)*meanOfColumn(series, 0); + return LinearModelHelper::YIntercept(meanOfColumn(series, 1), meanOfColumn(series, 0), slope(series)); } double Store::yValueForXValue(int series, double x, Poincare::Context * globalContext) { diff --git a/apps/regression/test/model.cpp b/apps/regression/test/model.cpp index aebea93df..d03f42fa0 100644 --- a/apps/regression/test/model.cpp +++ b/apps/regression/test/model.cpp @@ -76,6 +76,20 @@ QUIZ_CASE(exponential_regression) { assert_regression_is(x, y, 6, Model::Type::Exponential, coefficients); } +QUIZ_CASE(exponential_regression2) { + double x[] = {0, 1, 2, 3}; + double y[] = {3000, 3315.513, 3664.208, 4049.576}; + double coefficients[] = {3000, .1}; + assert_regression_is(x, y, 4, Model::Type::Exponential, coefficients); +} + +QUIZ_CASE(exponential_regression3) { + double x[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + double y[] = {-1, -.3678794, -.1353353, -.04978707, -.01831564, -.006737947, -.002478752, -.000911882, -.0003354626, -.0001234098, -.00004539993}; + double coefficients[] = {-1, -1}; + assert_regression_is(x, y, 11, Model::Type::Exponential, coefficients); +} + QUIZ_CASE(power_regression) { double x[] = {1.0, 50.0, 34.0, 67.0, 20.0}; double y[] = {71.860, 2775514, 979755.1, 6116830, 233832.9}; diff --git a/apps/sequence/base.de.i18n b/apps/sequence/base.de.i18n index 2899ddbc5..278615512 100644 --- a/apps/sequence/base.de.i18n +++ b/apps/sequence/base.de.i18n @@ -1,15 +1,15 @@ SequenceApp = "Folge" SequenceAppCapital = "FOLGE" SequenceTab = "Folgen" -AddSequence = "Folge hinzufuegen" -ChooseSequenceType = "Das Bildungsgesetz der Folge auswaehlen" +AddSequence = "Folge hinzufügen" +ChooseSequenceType = "Das Bildungsgesetz der Folge auswählen" SequenceType = "Bildungsgesetz der Folge" Explicit = "Explizit" SingleRecurrence = "Rekursion 1. Ordnung" DoubleRecurrence = "Rekursion 2. Ordnung" SequenceOptions = "Optionen der Folge" SequenceColor = "Farbe der Folge" -DeleteSequence = "Folge loeschen" +DeleteSequence = "Folge löschen" NoSequence = "Keine Folge" NoActivatedSequence = "Keine aktive Folge" NStart = "Startwert" diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index b0a434359..3ed41780d 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -4,14 +4,14 @@ AngleUnit = "Winkeleinheit" DisplayMode = "Zahlenformat" EditionMode = "Eingabe" EditionLinear = "Linear " -Edition2D = "Natuerlich " +Edition2D = "Natürlich " ComplexFormat = "Komplex" ExamMode = "Testmodus" ActivateExamMode = "Start Testmodus" ExamModeActive = "Testmodus: aktiv" -About = "Ueber" +About = "Über" Degres = "Grad " -Radian = "Bogenmass " +Radian = "Bogenmaß " Decimal = "Dezimal " Scientific = "Wissenschaftlich " SignificantFigures = "Signifikante Stellen " diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index e2ff6f0c2..da4a09283 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -1,10 +1,10 @@ ActivateDeactivate = "Aktivieren/Deaktivieren" ActiveExamModeMessage1 = "Alle Ihre Daten werden " -ActiveExamModeMessage2 = "geloescht, wenn Sie den " +ActiveExamModeMessage2 = "gelöscht, wenn Sie den " ActiveExamModeMessage3 = "Testmodus einschalten." Axis = "Achsen" Cancel = "Abbrechen" -ClearColumn = "Spalte loeschen" +ClearColumn = "Spalte löschen" ColumnOptions = "Optionen der Spalte" CopyColumnInList = "Die Spalte in einer Liste kopieren" DataNotSuitable = "Daten nicht geeignet" @@ -14,10 +14,10 @@ Deg = "gra" Deviation = "Varianz" DisplayValues = "Werte anzeigen" Empty = "Leer" -ExitExamMode1 = "Moechten Sie den Testmodus " +ExitExamMode1 = "Möchten Sie den Testmodus " ExitExamMode2 = "verlassen?" Exponential = "Exponentielle" -FillWithFormula = "Mit einer Formel fuellen" +FillWithFormula = "Mit einer Formel füllen" ForbiddenValue = "Verbotener Wert" FunctionColumn = "0(0) Spalte" FunctionOptions = "Funktionsoptionen" @@ -25,22 +25,22 @@ Goto = "Gehe zu" GraphTab = "Graph" HardwareTestLaunch1 = "Sie sind dabei den Hardwaretest zu" HardwareTestLaunch2 = "starten. Um ihn wieder zu verlassen," -HardwareTestLaunch3 = "muessen Sie einen Reset durchfuhren," -HardwareTestLaunch4 = "der Ihre Daten loeschen wird." +HardwareTestLaunch3 = "müssen Sie einen Reset durchfuhren," +HardwareTestLaunch4 = "der Ihre Daten löschen wird." Initialization = "Initialisierung" IntervalSet = "Intervall einstellen" Language = "Sprache" -LowBattery = "Batterie erschoepft" +LowBattery = "Batterie erschöpft" Mean = "Mittelwert" Move = " Verschieben: " NameCannotStartWithNumber = "Ein Name darf nicht mit einer Zahl beginnen" NameTaken = "Dieser Name ist bereits vergeben" NameTooLong = "Der Name ist zu lang" -Next = "Naechste" +Next = "Nächste" NoDataToPlot = "Keine Daten zum Zeichnen" -NoFunctionToDelete = "Keine Funktion zum Loeschen" -NoValueToCompute = "Keine Groesse zum Berechnen" -Ok = "Bestaetigen" +NoFunctionToDelete = "Keine Funktion zum Löschen" +NoValueToCompute = "Keine Größe zum Berechnen" +Ok = "Bestätigen" Or = " oder " Orthonormal = "Orthonormal" Plot = "Graphen zeichnen" diff --git a/apps/solver/base.de.i18n b/apps/solver/base.de.i18n index b5977d8b6..5350c81d2 100644 --- a/apps/solver/base.de.i18n +++ b/apps/solver/base.de.i18n @@ -1,25 +1,25 @@ SolverApp = "Gleichungen" SolverAppCapital = "GLEICHUNGEN" -AddEquation = "Gleichung hinzufuegen" -ResolveEquation = "Loesen der Gleichung" -ResolveSystem = "Loesen des Gleichungssystems" +AddEquation = "Gleichung hinzufügen" +ResolveEquation = "Lösen der Gleichung" +ResolveSystem = "Lösen des Gleichungssystems" UseEquationModel = "Verwenden Sie ein Gleichungsmodell" RequireEquation = "Die Eingabe muss eine Gleichung sein" UndefinedEquation = "Nicht definierte Gleichung" UnrealEquation = "Nicht reelle Gleichung" TooManyVariables = "Es gibt zu viele Unbekannte" NonLinearSystem = "Das System ist nicht linear" -Solution = "Loesung" -ApproximateSolution = "Ungefaehre Loesung" -SearchInverval = "Loesungssuche Intervall" -NoSolutionSystem = "Das System hat keine Loesung" -NoSolutionEquation = "Die Gleichung hat keine Loesung" -NoSolutionInterval = "Keine Loesung im Intervall gefunden" +Solution = "Lösung" +ApproximateSolution = "Ungefähre Lösung" +SearchInverval = "Lösungssuche Intervall" +NoSolutionSystem = "Das System hat keine Lösung" +NoSolutionEquation = "Die Gleichung hat keine Lösung" +NoSolutionInterval = "Keine Lösung im Intervall gefunden" EnterEquation = "Geben Sie eine Gleichung ein" -InfiniteNumberOfSolutions = "Es gibt unendlich viele Loesungen" -ApproximateSolutionIntervalInstruction0= "Geben Sie das Intervall fuer die Suche" -ApproximateSolutionIntervalInstruction1= "nach einer ungefaehren Loesung ein" +InfiniteNumberOfSolutions = "Es gibt unendlich viele Lösungen" +ApproximateSolutionIntervalInstruction0= "Geben Sie das Intervall für die Suche" +ApproximateSolutionIntervalInstruction1= "nach einer ungefähren Lösung ein" OnlyFirstSolutionsDisplayed0 = "Es werden nur die ersten" -OnlyFirstSolutionsDisplayed1 = "zehn Loesungen angezeigt." +OnlyFirstSolutionsDisplayed1 = "zehn Lösungen angezeigt." PolynomeHasNoRealSolution0 = "Das Polynom hat" PolynomeHasNoRealSolution1 = "keine reelle Nullstelle" diff --git a/apps/statistics/base.de.i18n b/apps/statistics/base.de.i18n index 4aae4697a..a28b5e8f5 100644 --- a/apps/statistics/base.de.i18n +++ b/apps/statistics/base.de.i18n @@ -5,12 +5,12 @@ BoxTab = "Boxplot" Values1 = "Werte V1" Values2 = "Werte V2" Values3 = "Werte V3" -Sizes1 = "Haeufigkeiten N1" -Sizes2 = "Haeufigkeiten N2" -Sizes3 = "Haeufigkeiten N3" +Sizes1 = "Häufigkeiten N1" +Sizes2 = "Häufigkeiten N2" +Sizes3 = "Häufigkeiten N3" ImportList = "Laden einer Liste" Interval = " Intervall" -Size = " Haeufigkeit" +Size = " Häufigkeit" Frequency = "Relative" HistogramSet = "Einstellung des Histogramms" RectangleWidth = "Breite der Rechtecke" diff --git a/apps/toolbox.de.i18n b/apps/toolbox.de.i18n index 0de46ea75..8b7f75086 100644 --- a/apps/toolbox.de.i18n +++ b/apps/toolbox.de.i18n @@ -19,7 +19,7 @@ Product = "Produkt" ComplexAbsoluteValue = "Betrag" Agument = "Argument" RealPart = "Realteil" -ImaginaryPart = "Imaginaerteil" +ImaginaryPart = "Imaginärteil" Conjugate = "Konjugiert" Combination = "Kombination" Permutation = "Permutation" @@ -31,7 +31,7 @@ Inverse = "Inverse" Determinant = "Determinante" Transpose = "Transponierte" Trace = "Spur" -Dimension = "Groesse" +Dimension = "Größe" Sort = "Sortieren aufsteigend" InvSort = "Sortieren absteigend" Maximum = "Maximalwert" @@ -49,7 +49,7 @@ InverseHyperbolicTangent = "Areatangens hyperbolicus" Prediction95 = "Schwankungsbereich 95%" Prediction = "Einfacher Schwankungsbereich" Confidence = "Konfidenzintervall" -RandomAndApproximation = "Zufall und Naeherung" +RandomAndApproximation = "Zufall und Näherung" RandomFloat = "Dezimalzahl in [0,1]" -RandomInteger = "Zufaellige ganze Zahl in [a,b]" +RandomInteger = "Zufällige ganze Zahl in [a,b]" PrimeFactorDecomposition = "Primfaktorzerlegung" diff --git a/apps/usb/base.de.i18n b/apps/usb/base.de.i18n index 938143630..bc6cd5950 100644 --- a/apps/usb/base.de.i18n +++ b/apps/usb/base.de.i18n @@ -1,7 +1,7 @@ USBConnected = "DER RECHNER IST ANGESCHLOSSEN" -ConnectedMessage1 = "Um Daten zu uebertragen, verbinden" +ConnectedMessage1 = "Um Daten zu übertragen, verbinden" ConnectedMessage2 = "Sie Sich von Ihrem Computer aus mit" ConnectedMessage3 = "workshop.numworks.com." -ConnectedMessage4 = "Druecken Sie die RETURN-Taste am" +ConnectedMessage4 = "Drücken Sie die RETURN-Taste am" ConnectedMessage5 = "Taschenrechner oder ziehen Sie das Kabel," ConnectedMessage6 = "um die Verbindung zu trennen." diff --git a/apps/variables.de.i18n b/apps/variables.de.i18n index 60817027e..220ab3680 100644 --- a/apps/variables.de.i18n +++ b/apps/variables.de.i18n @@ -1,5 +1,5 @@ Variables = "Variablen" -Expressions = "Ausdruecke" +Expressions = "Ausdrücke" Functions = "Funktionen" EmptyExpressionBox0 = "Sie haben keine Variable definiert." EmptyFunctionBox0 = "Sie haben keine Funktion definiert." diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 874719df8..3994e89d4 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -53,7 +53,7 @@ private: static_assert(k_maxNumberOfLayouts == TextField::maxBufferSize(), "Maximal number of layouts in a layout field should be equal to max number of char in text field"); void scrollRightOfLayout(Poincare::Layout layoutR); void scrollToBaselinedRect(KDRect rect, KDCoordinate baseline); - void insertLayoutAtCursor(Poincare::Layout layoutR, Poincare::Layout pointedLayout, bool forceCursorRightOfLayout = false); + void insertLayoutAtCursor(Poincare::Layout layoutR, Poincare::Expression correspondingExpression, bool forceCursorRightOfLayout = false); class ContentView : public View { public: diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index a25088ef0..912e070c7 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -91,6 +90,10 @@ void LayoutField::reload(KDSize previousSize) { } bool LayoutField::handleEventWithText(const char * text, bool indentation, bool forceCursorRightOfText) { + /* The text here can be: + * - the result of a key pressed, such as "," or "cos(•)" + * - the text added after a toolbox selection + * - the result of a copy-paste. */ if (text[0] == 0) { // The text is empty return true; @@ -122,31 +125,18 @@ bool LayoutField::handleEventWithText(const char * text, bool indentation, bool } else { Expression resultExpression = Expression::Parse(text); if (resultExpression.isUninitialized()) { + // The text is not parsable (for instance, ",") and is added char by char. KDSize previousLayoutSize = minimalSizeForOptimalDisplay(); m_contentView.cursor()->insertText(text); reload(previousLayoutSize); - } else { - Layout resultLayout = resultExpression.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::PrintFloat::k_numberOfStoredSignificantDigits); - if (currentNumberOfLayouts + resultLayout.numberOfDescendants(true) >= k_maxNumberOfLayouts) { - return true; - } - // Find the pointed layout. - Layout pointedLayout; - if (!forceCursorRightOfText) { - if (strcmp(text, I18n::translate(I18n::Message::RandomCommandWithArg)) == 0) { - /* Special case: if the text is "random()", the cursor should not be set - * inside the parentheses. */ - pointedLayout = resultLayout; - } else if (resultLayout.type() == LayoutNode::Type::HorizontalLayout) { - pointedLayout = resultLayout.recursivelyMatches( - [](Poincare::Layout layout) { - return layout.type() == LayoutNode::Type::LeftParenthesisLayout || layout.isEmpty();}); - } - } - /* Insert the layout. If pointedLayout is uninitialized, the cursor will - * be on the right of the inserted layout. */ - insertLayoutAtCursor(resultLayout, pointedLayout, forceCursorRightOfText); + return true; } + // The text is parsable, we create its layout an insert it. + Layout resultLayout = resultExpression.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::PrintFloat::k_numberOfStoredSignificantDigits); + if (currentNumberOfLayouts + resultLayout.numberOfDescendants(true) >= k_maxNumberOfLayouts) { + return true; + } + insertLayoutAtCursor(resultLayout, resultExpression, forceCursorRightOfText); } return true; } @@ -285,42 +275,44 @@ void LayoutField::scrollToBaselinedRect(KDRect rect, KDCoordinate baseline) { scrollToContentRect(balancedRect, true); } -void LayoutField::insertLayoutAtCursor(Layout layoutR, Layout pointedLayoutR, bool forceCursorRightOfLayout) { +void LayoutField::insertLayoutAtCursor(Layout layoutR, Poincare::Expression correspondingExpression, bool forceCursorRightOfLayout) { if (layoutR.isUninitialized()) { return; } KDSize previousSize = minimalSizeForOptimalDisplay(); + Poincare::LayoutCursor * cursor = m_contentView.cursor(); // Handle empty layouts - m_contentView.cursor()->showEmptyLayoutIfNeeded(); + cursor->showEmptyLayoutIfNeeded(); bool layoutWillBeMerged = layoutR.type() == LayoutNode::Type::HorizontalLayout; Layout lastMergedLayoutChild = layoutWillBeMerged ? layoutR.childAtIndex(layoutR.numberOfChildren()-1) : Layout(); - // Add the layout - m_contentView.cursor()->addLayoutAndMoveCursor(layoutR); + // Find the layout where the cursor will point + assert(!correspondingExpression.isUninitialized()); + Layout cursorLayout = forceCursorRightOfLayout ? layoutR : layoutR.layoutToPointWhenInserting(&correspondingExpression); + assert(!cursorLayout.isUninitialized()); - // Move the cursor if needed - if(!forceCursorRightOfLayout) { - if (!pointedLayoutR.isUninitialized() && (!layoutWillBeMerged || pointedLayoutR != layoutR)) { - // Make sure the layout was inserted (its parent is not uninitialized) - m_contentView.cursor()->setLayout(pointedLayoutR); - m_contentView.cursor()->setPosition(LayoutCursor::Position::Right); - } else if (!layoutWillBeMerged) { - m_contentView.cursor()->setLayout(layoutR.layoutToPointWhenInserting()); - m_contentView.cursor()->setPosition(LayoutCursor::Position::Right); - } - } else if (!layoutWillBeMerged) { - m_contentView.cursor()->setLayout(layoutR); - m_contentView.cursor()->setPosition(LayoutCursor::Position::Right); + // Add the layout. This puts the cursor at the right of the added layout + cursor->addLayoutAndMoveCursor(layoutR); + + /* Move the cursor if needed. + * If the layout to point to has been merged, it means that only its children + * have been inserted in the layout, so we must not move the cursor to the + * parent. In this case, addLayoutAndMoveCursor made the cursor point to the + * last merged child, which is what is wanted. + * For other cases, move the cursor to the computed layout. */ + if (!(layoutWillBeMerged && cursorLayout == layoutR)) { + cursor->setLayout(cursorLayout); + cursor->setPosition(LayoutCursor::Position::Right); } // Handle matrices - m_contentView.cursor()->layoutReference().addGreySquaresToAllMatrixAncestors(); + cursor->layoutReference().addGreySquaresToAllMatrixAncestors(); // Handle empty layouts - m_contentView.cursor()->hideEmptyLayoutIfNeeded(); + cursor->hideEmptyLayoutIfNeeded(); // Reload reload(previousSize); diff --git a/ion/include/ion/keyboard/layout_B2/layout_events.h b/ion/include/ion/keyboard/layout_B2/layout_events.h index 3a62f82f9..800cbc8d0 100644 --- a/ion/include/ion/keyboard/layout_B2/layout_events.h +++ b/ion/include/ion/keyboard/layout_B2/layout_events.h @@ -24,7 +24,7 @@ static constexpr EventData s_dataForEvent[4*Event::PageSize] = { U(), U(), U(), U(), U(), U(), U(), U(), TL(), TL(), TL(), TL(), T("["), T("]"), T("{"), T("}"), T("_"), T("→"), - T("arcsin(\x11)"), T("arccos(\x11)"), T("arctan(\x11)"), T("="), T("<"), T(">"), + T("asin(\x11)"), T("acos(\x11)"), T("atan(\x11)"), T("="), T("<"), T(">"), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), diff --git a/ion/include/ion/keyboard/layout_B3/layout_events.h b/ion/include/ion/keyboard/layout_B3/layout_events.h index e531a9bce..03898f223 100644 --- a/ion/include/ion/keyboard/layout_B3/layout_events.h +++ b/ion/include/ion/keyboard/layout_B3/layout_events.h @@ -24,7 +24,7 @@ static constexpr EventData s_dataForEvent[4*Event::PageSize] = { U(), U(), U(), U(), U(), U(), U(), U(), TL(), TL(), TL(), TL(), T("["), T("]"), T("{"), T("}"), T("_"), T("→"), - T("arcsin(\x11)"), T("arccos(\x11)"), T("arctan(\x11)"), T("="), T("<"), T(">"), + T("asin(\x11)"), T("acos(\x11)"), T("atan(\x11)"), T("="), T("<"), T(">"), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), U(), diff --git a/kandinsky/Makefile b/kandinsky/Makefile index fcb8533dd..582c30977 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -16,8 +16,8 @@ src += $(addprefix kandinsky/src/,\ ) src += $(addprefix kandinsky/fonts/, \ - LargeSourcePixel.ttf \ - SmallSourcePixel.ttf \ + LargeFont.ttf \ + SmallFont.ttf \ ) tests += $(addprefix kandinsky/test/,\ @@ -26,13 +26,13 @@ tests += $(addprefix kandinsky/test/,\ rect.cpp\ ) -RASTERIZER_CFLAGS := -std=c99 `pkg-config freetype2 --cflags` -RASTERIZER_LDFLAGS := `pkg-config freetype2 --libs` +RASTERIZER_CFLAGS := -std=c99 $(shell pkg-config freetype2 --cflags) +RASTERIZER_LDFLAGS := $(shell pkg-config freetype2 --libs) -ifdef LIBPNG_PATH - small_font_files += kandinsky/src/small_font.png - large_font_files += kandinsky/src/large_font.png - RASTERIZER_CFLAGS += -I$(LIBPNG_PATH)/include -DGENERATE_PNG=1 -L$(LIBPNG_PATH)/lib -lpng +HAS_LIBPNG := $(shell pkg-config libpng --exists && echo 1) +ifeq ($(HAS_LIBPNG),1) + RASTERIZER_CFLAGS += $(shell pkg-config libpng --cflags) -DGENERATE_PNG=1 + RASTERIZER_LDFLAGS += $(shell pkg-config libpng --libs) endif $(eval $(call rule_for, \ @@ -44,16 +44,15 @@ $(eval $(call rule_for, \ RASTERIZER := $(BUILD_DIR)/kandinsky/fonts/rasterizer -$(eval $(call rule_for, \ +# Define a rasterizing recipe. Parameters : font name, size, packed_width, packed_height +define raster_font +$(call rule_for, \ RASTER, \ - kandinsky/fonts/SmallSourcePixel.cpp, \ - kandinsky/fonts/SmallSourcePixel.otf $$(RASTERIZER), \ - $$(RASTERIZER) $$< 12 12 SmallFont $$@ \ -)) + kandinsky/fonts/$(1).cpp, \ + kandinsky/fonts/$(1).ttf $$(RASTERIZER), \ + $$(RASTERIZER) $$< $(2) $(2) $(3) $(4) $(1) $$@ $(if $(HAS_LIBPNG),$$(basename $$@).png) \ +) +endef -$(eval $(call rule_for, \ - RASTER, \ - kandinsky/fonts/LargeSourcePixel.cpp, \ - kandinsky/fonts/LargeSourcePixel.otf $$(RASTERIZER), \ - $$(RASTERIZER) $$< 16 16 LargeFont $$@ \ -)) +$(eval $(call raster_font,SmallFont,12,7,14)) +$(eval $(call raster_font,LargeFont,16,10,18)) diff --git a/kandinsky/fonts/LargeFont.ttf b/kandinsky/fonts/LargeFont.ttf new file mode 100644 index 000000000..46b67f74d Binary files /dev/null and b/kandinsky/fonts/LargeFont.ttf differ diff --git a/kandinsky/fonts/LargeSourcePixel.otf b/kandinsky/fonts/LargeSourcePixel.otf deleted file mode 100644 index 1e6c616ea..000000000 Binary files a/kandinsky/fonts/LargeSourcePixel.otf and /dev/null differ diff --git a/kandinsky/fonts/SmallFont.ttf b/kandinsky/fonts/SmallFont.ttf new file mode 100644 index 000000000..059148375 Binary files /dev/null and b/kandinsky/fonts/SmallFont.ttf differ diff --git a/kandinsky/fonts/SmallSourcePixel.otf b/kandinsky/fonts/SmallSourcePixel.otf deleted file mode 100644 index 43e3c0e56..000000000 Binary files a/kandinsky/fonts/SmallSourcePixel.otf and /dev/null differ diff --git a/kandinsky/fonts/rasterizer.c b/kandinsky/fonts/rasterizer.c index 708d027ca..967c5457e 100644 --- a/kandinsky/fonts/rasterizer.c +++ b/kandinsky/fonts/rasterizer.c @@ -36,6 +36,7 @@ typedef struct { } image_t; #ifdef GENERATE_PNG +#define PNG_SKIP_SETJMP_CHECK #include void writeImageToPNGFile(image_t * image, char * filename); #endif @@ -48,9 +49,9 @@ int main(int argc, char * argv[]) { FT_Face face; image_t bitmap_image; - int expectedNumberOfArguments = 6; + int expectedNumberOfArguments = 8; #ifdef GENERATE_PNG - expectedNumberOfArguments = 7; + expectedNumberOfArguments = 9; #endif if (argc != expectedNumberOfArguments) { #ifdef GENERATE_PNG @@ -61,6 +62,8 @@ int main(int argc, char * argv[]) { fprintf(stderr, " font_file: Path of the font file to load\n"); fprintf(stderr, " glyph_width: Width of bitmap glyphs, in pixels\n"); fprintf(stderr, " glyph_height: Height of bitmap glyphs, in pixels\n"); + fprintf(stderr, " packed_glyph_width: Minimal glyph width in pixels. Pass 0 if unsure.\n"); + fprintf(stderr, " packed_glyph_height: Minimal glyph height in pixels. Pass 0 if unsure.\n"); fprintf(stderr, " font_name: name of the loaded font\n"); fprintf(stderr, " output_cpp: Name of the generated C source file\n"); #ifdef GENERATE_PNG @@ -72,10 +75,12 @@ int main(int argc, char * argv[]) { char * font_file = argv[1]; int requested_glyph_width = atoi(argv[2]); int requested_glyph_height = atoi(argv[3]); - char * font_name = argv[4]; - char * output_cpp = argv[5]; + int packed_glyph_width = atoi(argv[4]); + int packed_glyph_height = atoi(argv[5]); + char * font_name = argv[6]; + char * output_cpp = argv[7]; #ifdef GENERATE_PNG - char * output_png = argv[6]; + char * output_png = argv[8]; #endif ENSURE(!FT_Init_FreeType(&library), "Initializing library"); @@ -108,11 +113,21 @@ int main(int argc, char * argv[]) { if (belowBaseline > maxBelowBaseline) { maxBelowBaseline = belowBaseline; } + // printf("Codepoint %04x : %dx%d\n", codePoint, width, aboveBaseline+belowBaseline); } int glyph_width = maxWidth-1; + if (packed_glyph_width != 0) { + ENSURE(glyph_width == packed_glyph_width, "Expecting a packed glyph width of %d but got %d instead", packed_glyph_width, glyph_width); + } else { + printf("Computed packed_glyph_width = %d\n", glyph_width); + } int glyph_height = maxAboveBaseline+maxBelowBaseline; - //printf("Actual glyph size = %dx%d\n", glyph_width, glyph_height); + if (packed_glyph_height != 0) { + ENSURE(glyph_height == packed_glyph_height, "Expecting a packed glyph height of %d but got %d instead", packed_glyph_height, glyph_height); + } else { + printf("Computed packed_glyph_height = %d\n", glyph_height); + } int grid_size = 1; int grid_width = 20; @@ -192,6 +207,7 @@ int main(int argc, char * argv[]) { int greyscaleBitsPerPixel = 4; int sizeOfUncompressedGlyphBuffer = glyph_width * glyph_height * greyscaleBitsPerPixel/8; + ENSURE(8*sizeOfUncompressedGlyphBuffer == glyph_width * glyph_height * greyscaleBitsPerPixel, "Error: the glyph size (%dx%d@%dbpp) cannot fit in an integral number of bytes", glyph_width, glyph_height, greyscaleBitsPerPixel); uint8_t * uncompressedGlyphBuffer = (uint8_t *)malloc(sizeOfUncompressedGlyphBuffer); uint16_t glyphDataOffset[NumberOfCodePoints+1]; @@ -218,8 +234,8 @@ int main(int argc, char * argv[]) { } } } - ENSURE(accumulator == 0, "Discarded accumulator data"); - ENSURE(numberOfValuesAccumulated == 0, "Discarded accumulator data"); + ENSURE(accumulator == 0, "Discarded accumulator data (accumulator = %d)", accumulator); + ENSURE(numberOfValuesAccumulated == 0, "Discarded accumulator data (numberOfValuesAccumulated = %d)", numberOfValuesAccumulated); ENSURE(uncompressedGlyphBufferIndex == sizeOfUncompressedGlyphBuffer, "Error filling uncompressed buffer, only %d out of %d", uncompressedGlyphBufferIndex, sizeOfUncompressedGlyphBuffer); int sizeOfCompressedGlyphBuffer = LZ4_compress_HC( diff --git a/kandinsky/include/kandinsky/font.h b/kandinsky/include/kandinsky/font.h index 06d3dfe6d..10ec5ae67 100644 --- a/kandinsky/include/kandinsky/font.h +++ b/kandinsky/include/kandinsky/font.h @@ -26,6 +26,7 @@ class KDFont { private: static constexpr int k_bitsPerPixel = 4; // TODO: Should be generated by the rasterizer + static constexpr int k_maxGlyphPixelCount = 180; //TODO: Should be generated by the rasterizer static const KDFont privateLargeFont; static const KDFont privateSmallFont; public: @@ -44,7 +45,6 @@ public: uint8_t * greyscaleBuffer() { return m_greyscales; } uint8_t * secondaryGreyscaleBuffer() { return m_greyscales + k_maxGlyphPixelCount; } private: - static constexpr int k_maxGlyphPixelCount = 180; //TODO: Should be generated by the rasterizer uint8_t m_greyscales[2*k_maxGlyphPixelCount]; KDColor m_colors[k_maxGlyphPixelCount]; }; diff --git a/poincare/Makefile b/poincare/Makefile index 898de929c..8a8907671 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -159,6 +159,7 @@ tests += $(addprefix poincare/test/,\ infinity.cpp\ integer.cpp\ layouts.cpp\ + layout_cursor.cpp\ logarithm.cpp\ matrix.cpp\ multiplication.cpp\ diff --git a/poincare/include/poincare/condensed_sum_layout.h b/poincare/include/poincare/condensed_sum_layout.h index 0495cbdb1..688b78bc4 100644 --- a/poincare/include/poincare/condensed_sum_layout.h +++ b/poincare/include/poincare/condensed_sum_layout.h @@ -26,9 +26,9 @@ public: return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Sum::s_functionHelper.name()); } - LayoutNode * layoutToPointWhenInserting() override { + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override { assert(false); - return nullptr; + return this; } // TreeNode diff --git a/poincare/include/poincare/fraction_layout.h b/poincare/include/poincare/fraction_layout.h index 423198210..94eeba128 100644 --- a/poincare/include/poincare/fraction_layout.h +++ b/poincare/include/poincare/fraction_layout.h @@ -27,7 +27,7 @@ public: int leftCollapsingAbsorbingChildIndex() const override { return 0; } int rightCollapsingAbsorbingChildIndex() const override { return 1; } void didCollapseSiblings(LayoutCursor * cursor) override; - LayoutNode * layoutToPointWhenInserting() override; + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override; bool canBeOmittedMultiplicationRightFactor() const override { return false; } /* WARNING: We need to override this function, else 1/2 3/4 would be * serialized as 1/2**3/4, as the two Fraction layouts think their sibling is diff --git a/poincare/include/poincare/horizontal_layout.h b/poincare/include/poincare/horizontal_layout.h index 1333108a8..fa3fe7ec6 100644 --- a/poincare/include/poincare/horizontal_layout.h +++ b/poincare/include/poincare/horizontal_layout.h @@ -26,6 +26,7 @@ public: void moveCursorRight(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; LayoutCursor equivalentCursor(LayoutCursor * cursor) override; void deleteBeforeCursor(LayoutCursor * cursor) override; + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; bool isEmpty() const override { return m_numberOfChildren == 1 && const_cast(this)->childAtIndex(0)->isEmpty(); } diff --git a/poincare/include/poincare/integral.h b/poincare/include/poincare/integral.h index 6c176caa0..d848e05b1 100644 --- a/poincare/include/poincare/integral.h +++ b/poincare/include/poincare/integral.h @@ -41,7 +41,7 @@ private: T integral; T absoluteError; }; - constexpr static int k_maxNumberOfIterations = 10; + constexpr static int k_maxNumberOfIterations = 20; #ifdef LAGRANGE_METHOD template T lagrangeGaussQuadrature(T a, T b, Context Context & context, Preferences::AngleUnit angleUnit context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; #else diff --git a/poincare/include/poincare/integral_layout.h b/poincare/include/poincare/integral_layout.h index 1a4836c61..cbd03b1a0 100644 --- a/poincare/include/poincare/integral_layout.h +++ b/poincare/include/poincare/integral_layout.h @@ -24,7 +24,7 @@ public: void moveCursorDown(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited = false) override; void deleteBeforeCursor(LayoutCursor * cursor) override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; - LayoutNode * layoutToPointWhenInserting() override { return lowerBoundLayout(); } + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override { return lowerBoundLayout(); } CodePoint XNTCodePoint() const override { return 'x'; } // TreeNode diff --git a/poincare/include/poincare/layout.h b/poincare/include/poincare/layout.h index 8aa19ad4e..f9eaa0ad0 100644 --- a/poincare/include/poincare/layout.h +++ b/poincare/include/poincare/layout.h @@ -8,6 +8,7 @@ namespace Poincare { class LayoutCursor; +class Expression; class Layout : public TreeHandle { friend class GridLayoutNode; @@ -59,7 +60,11 @@ public: void deleteBeforeCursor(LayoutCursor * cursor) { return node()->deleteBeforeCursor(cursor); } bool removeGreySquaresFromAllMatrixAncestors() { return node()->removeGreySquaresFromAllMatrixAncestors(); } bool addGreySquaresToAllMatrixAncestors() { return node()->addGreySquaresToAllMatrixAncestors(); } - Layout layoutToPointWhenInserting() { return Layout(node()->layoutToPointWhenInserting()); } + Layout layoutToPointWhenInserting(Expression * correspondingExpression) { + // Pointer to correspondingExpr because expression.h includes layout.h + assert(correspondingExpression != nullptr); + return Layout(node()->layoutToPointWhenInserting(correspondingExpression)); + } // Cursor LayoutCursor cursor() const; diff --git a/poincare/include/poincare/layout_node.h b/poincare/include/poincare/layout_node.h index 2786ec135..3c82e2720 100644 --- a/poincare/include/poincare/layout_node.h +++ b/poincare/include/poincare/layout_node.h @@ -6,6 +6,7 @@ namespace Poincare { +class Expression; class LayoutCursor; class Layout; @@ -102,9 +103,7 @@ public: virtual void deleteBeforeCursor(LayoutCursor * cursor); // Other - virtual LayoutNode * layoutToPointWhenInserting() { - return numberOfChildren() > 0 ? childAtIndex(0) : this; - } + virtual LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression); bool removeGreySquaresFromAllMatrixAncestors() { return changeGreySquaresOfAllMatrixAncestors(false); } bool addGreySquaresToAllMatrixAncestors() { return changeGreySquaresOfAllMatrixAncestors(true); } /* A layout has text if it is not empty and it is not an horizontal layout diff --git a/poincare/include/poincare/sequence_layout.h b/poincare/include/poincare/sequence_layout.h index 9f5608adc..c301d120e 100644 --- a/poincare/include/poincare/sequence_layout.h +++ b/poincare/include/poincare/sequence_layout.h @@ -19,7 +19,7 @@ public: void moveCursorUp(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited = false) override; void moveCursorDown(LayoutCursor * cursor, bool * shouldRecomputeLayout, bool equivalentPositionVisited = false) override; void deleteBeforeCursor(LayoutCursor * cursor) override; - LayoutNode * layoutToPointWhenInserting() override { return lowerBoundLayout(); } + LayoutNode * layoutToPointWhenInserting(Expression * correspondingExpression) override { return lowerBoundLayout(); } CodePoint XNTCodePoint() const override { return 'n'; } // TreeNode diff --git a/poincare/src/arc_cosine.cpp b/poincare/src/arc_cosine.cpp index 46e7fd159..887bf0ef6 100644 --- a/poincare/src/arc_cosine.cpp +++ b/poincare/src/arc_cosine.cpp @@ -39,7 +39,7 @@ Complex ArcCosineNode::computeOnComplex(const std::complex c, Preferences: * this cut. We followed the convention chosen by the lib c++ of llvm on * ]-inf+0i, -1+0i[ (warning: acos takes the other side of the cut values on * ]-inf-0i, -1-0i[) and choose the values on ]1+0i, +inf+0i[ to comply with - * acos(-x) = Pi - acos(x) and tan(arccos(x)) = sqrt(1-x^2)/x. */ + * acos(-x) = π - acos(x) and tan(acos(x)) = sqrt(1-x^2)/x. */ if (c.imag() == 0 && c.real() > 1) { result.imag(-result.imag()); // other side of the cut } diff --git a/poincare/src/arc_sine.cpp b/poincare/src/arc_sine.cpp index 8239d3fb9..7a043d7f4 100644 --- a/poincare/src/arc_sine.cpp +++ b/poincare/src/arc_sine.cpp @@ -39,7 +39,7 @@ Complex ArcSineNode::computeOnComplex(const std::complex c, Preferences::C * this cut. We followed the convention chosen by the lib c++ of llvm on * ]-inf+0i, -1+0i[ (warning: asin takes the other side of the cut values on * ]-inf-0i, -1-0i[) and choose the values on ]1+0i, +inf+0i[ to comply with - * asin(-x) = -asin(x) and tan(arcsin(x)) = x/sqrt(1-x^2). */ + * asin(-x) = -asin(x) and tan(asin(x)) = x/sqrt(1-x^2). */ if (c.imag() == 0 && c.real() > 1) { result.imag(-result.imag()); // other side of the cut } diff --git a/poincare/src/arc_tangent.cpp b/poincare/src/arc_tangent.cpp index f98a01900..a107b1b72 100644 --- a/poincare/src/arc_tangent.cpp +++ b/poincare/src/arc_tangent.cpp @@ -35,7 +35,7 @@ Complex ArcTangentNode::computeOnComplex(const std::complex c, Preferences * on this cut. We followed the convention chosen by the lib c++ of llvm on * ]-i+0, -i*inf+0[ (warning: atan takes the other side of the cut values on * ]-i+0, -i*inf+0[) and choose the values on ]-inf*i, -i[ to comply with - * atan(-x) = -atan(x) and sin(arctan(x)) = x/sqrt(1+x^2). */ + * atan(-x) = -atan(x) and sin(atan(x)) = x/sqrt(1+x^2). */ if (c.real() == 0 && c.imag() < -1) { result.real(-result.real()); // other side of the cut } diff --git a/poincare/src/complex_cartesian.cpp b/poincare/src/complex_cartesian.cpp index f58c40d98..000156c78 100644 --- a/poincare/src/complex_cartesian.cpp +++ b/poincare/src/complex_cartesian.cpp @@ -140,8 +140,8 @@ Expression ComplexCartesian::argument(Context & context, Preferences::ComplexFor Expression a = real(); Expression b = imag(); if (!b.isRationalZero()) { - // if b != 0, argument = sign(b) * Pi/2 - arctan(a/b) - // First, compute arctan(a/b) or (Pi/180)*arctan(a/b) + // if b != 0, argument = sign(b) * π/2 - atan(a/b) + // First, compute atan(a/b) or (π/180)*atan(a/b) Expression divab = Division::Builder(a, b.clone()); Expression arcTangent = ArcTangent::Builder(divab); divab.shallowReduce(context, complexFormat, angleUnit, target); @@ -150,7 +150,7 @@ Expression ComplexCartesian::argument(Context & context, Preferences::ComplexFor arcTangent.shallowReduce(context, complexFormat, angleUnit, target); arcTangent = temp; } - // Then, compute sign(b) * Pi/2 - arctan(a/b) + // Then, compute sign(b) * π/2 - atan(a/b) Expression signb = SignFunction::Builder(b); Expression signbPi2 = Multiplication::Builder(Rational::Builder(1,2), signb, Constant::Builder(UCodePointGreekSmallLetterPi)); signb.shallowReduce(context, complexFormat, angleUnit, target); @@ -159,7 +159,7 @@ Expression ComplexCartesian::argument(Context & context, Preferences::ComplexFor arcTangent.shallowReduce(context, complexFormat, angleUnit, target); return sub; } else { - // if b == 0, argument = (1-sign(a))*Pi/2 + // if b == 0, argument = (1-sign(a))*π/2 Expression signa = SignFunction::Builder(a).shallowReduce(context, complexFormat, angleUnit, target); Subtraction sub = Subtraction::Builder(Rational::Builder(1), signa); signa.shallowReduce(context, complexFormat, angleUnit, target); diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp index 812b5cdf7..a9857b7d1 100644 --- a/poincare/src/decimal.cpp +++ b/poincare/src/decimal.cpp @@ -287,6 +287,13 @@ Decimal Decimal::Builder(const char * integralPart, int integralPartLength, cons } //TODO: set a FLAG to tell that a rounding happened? bool rounding = integralPartLength > PrintFloat::k_numberOfStoredSignificantDigits && integralPart[PrintFloat::k_numberOfStoredSignificantDigits] >= '5'; + /* At this point, the exponent has already been computed. In the very special + * case where all the significant digits of the mantissa are 9, rounding up + * must increment the exponent. For instance, rounding up 0.99...9 (whose + * exponent is -1) yields 1 (whose exponent is 0). To that end, the + * significant digits will be scanned successively to determine whether the + * exponent should be incremented. */ + bool incrementExponentAfterRoundingUp = true; // Cap the length of the integralPart integralPartLength = integralPartLength > PrintFloat::k_numberOfStoredSignificantDigits ? PrintFloat::k_numberOfStoredSignificantDigits : integralPartLength; Integer numerator(integralPart, integralPartLength, false); @@ -301,13 +308,22 @@ Decimal Decimal::Builder(const char * integralPart, int integralPartLength, cons } rounding |= integralPartLength+fractionalPartLength > PrintFloat::k_numberOfStoredSignificantDigits && fractionalPart[PrintFloat::k_numberOfStoredSignificantDigits-integralPartLength] >= '5'; fractionalPartLength = integralPartLength+fractionalPartLength > PrintFloat::k_numberOfStoredSignificantDigits ? PrintFloat::k_numberOfStoredSignificantDigits - integralPartLength : fractionalPartLength; + while (incrementExponentAfterRoundingUp && integralPartLength-- > 0) { + incrementExponentAfterRoundingUp = (*(integralPart++) == '9'); + } for (int i = 0; i < fractionalPartLength; i++) { numerator = Integer::Multiplication(numerator, base); assert(*fractionalPart >= '0' && *fractionalPart <= '9'); numerator = Integer::Addition(numerator, Integer(*fractionalPart-'0')); + incrementExponentAfterRoundingUp &= (*fractionalPart == '9'); fractionalPart++; } - numerator = rounding ? Integer::Addition(numerator, Integer(1)) : numerator; + if (rounding) { + numerator = Integer::Addition(numerator, Integer(1)); + if (incrementExponentAfterRoundingUp) { + exponent++; + } + } exponent = numerator.isZero() ? 0 : exponent; return Decimal::Builder(numerator, exponent); } diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 8f4f90711..1b3618bb0 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -446,7 +446,7 @@ void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expre /* Step 2: Approximation * We compute the approximate expression from the Cartesian form to avoid * unprecision. For example, if the result is the ComplexCartesian(a,b), - * the final expression is goind to be sqrt(a^2+b^2)*exp(i*arctan(b/a)... + * the final expression is going to be sqrt(a^2+b^2)*exp(i*atan(b/a)... * in Polar ComplexFormat. If we approximate this expression instead of * ComplexCartesian(a,b), we are going to loose precision on the resulting * complex.*/ diff --git a/poincare/src/fraction_layout.cpp b/poincare/src/fraction_layout.cpp index 8af85a37d..589ba4fcf 100644 --- a/poincare/src/fraction_layout.cpp +++ b/poincare/src/fraction_layout.cpp @@ -166,7 +166,7 @@ int FractionLayoutNode::serialize(char * buffer, int bufferSize, Preferences::Pr return numberOfChar; } -LayoutNode * FractionLayoutNode::layoutToPointWhenInserting() { +LayoutNode * FractionLayoutNode::layoutToPointWhenInserting(Expression * correspondingExpression) { if (numeratorLayout()->isEmpty()){ return numeratorLayout(); } diff --git a/poincare/src/horizontal_layout.cpp b/poincare/src/horizontal_layout.cpp index 475ab8737..260ef72da 100644 --- a/poincare/src/horizontal_layout.cpp +++ b/poincare/src/horizontal_layout.cpp @@ -164,6 +164,21 @@ void HorizontalLayoutNode::deleteBeforeCursor(LayoutCursor * cursor) { LayoutNode::deleteBeforeCursor(cursor); } +LayoutNode * HorizontalLayoutNode::layoutToPointWhenInserting(Expression * correspondingExpression) { + assert(correspondingExpression != nullptr); + if (correspondingExpression->numberOfChildren() > 0) { + Layout layoutToPointTo = Layout(this).recursivelyMatches( + [](Poincare::Layout layout) { + return layout.type() == LayoutNode::Type::LeftParenthesisLayout || layout.isEmpty(); + } + ); + if (!layoutToPointTo.isUninitialized()) { + return layoutToPointTo.node(); + } + } + return this; +} + int HorizontalLayoutNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { if (numberOfChildren() == 0) { if (bufferSize == 0) { diff --git a/poincare/src/layout_node.cpp b/poincare/src/layout_node.cpp index fa6341dda..ba236cacd 100644 --- a/poincare/src/layout_node.cpp +++ b/poincare/src/layout_node.cpp @@ -110,6 +110,11 @@ void LayoutNode::deleteBeforeCursor(LayoutCursor * cursor) { // WARNING: Do no use "this" afterwards } +LayoutNode * LayoutNode::layoutToPointWhenInserting(Expression * correspondingExpression) { + assert(correspondingExpression != nullptr); + return numberOfChildren() > 0 ? childAtIndex(0) : this; +} + bool LayoutNode::willRemoveChild(LayoutNode * l, LayoutCursor * cursor, bool force) { if (!force) { Layout(this).replaceChildWithEmpty(Layout(l), cursor); diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index 84b694c5c..c54d4b12f 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -99,14 +99,14 @@ Expression Trigonometry::shallowReduceDirectFunction(Expression & e, Context& co return lookup; } - // Step 2. Look for an expression of type "cos(arccos(x))", return x + // Step 2. Look for an expression of type "cos(acos(x))", return x if (AreInverseFunctions(e, e.childAtIndex(0))) { Expression result = e.childAtIndex(0).childAtIndex(0); e.replaceWithInPlace(result); return result; } - // Step 3. Look for an expression of type "cos(arcsin(x))" or "sin(arccos(x)), return sqrt(1-x^2) + // Step 3. Look for an expression of type "cos(asin(x))" or "sin(acos(x)), return sqrt(1-x^2) // These equalities are true on complexes if ((e.type() == ExpressionNode::Type::Cosine && e.childAtIndex(0).type() == ExpressionNode::Type::ArcSine) || (e.type() == ExpressionNode::Type::Sine && e.childAtIndex(0).type() == ExpressionNode::Type::ArcCosine)) { Expression sqrt = @@ -131,9 +131,9 @@ Expression Trigonometry::shallowReduceDirectFunction(Expression & e, Context& co return sqrt.shallowReduce(context, complexFormat, angleUnit, target); } - // Step 4. Look for an expression of type "cos(arctan(x))" or "sin(arctan(x))" - // cos(arctan(x)) --> 1/sqrt(1+x^2) - // sin(arctan(x)) --> x/sqrt(1+x^2) + // Step 4. Look for an expression of type "cos(atan(x))" or "sin(atan(x))" + // cos(atan(x)) --> 1/sqrt(1+x^2) + // sin(atan(x)) --> x/sqrt(1+x^2) // These equalities are true on complexes if ((e.type() == ExpressionNode::Type::Cosine || e.type() == ExpressionNode::Type::Sine) && e.childAtIndex(0).type() == ExpressionNode::Type::ArcTangent) { Expression x = e.childAtIndex(0).childAtIndex(0); @@ -252,7 +252,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c assert(isInverseTrigonometryFunction(e)); float pi = angleUnit == Preferences::AngleUnit::Radian ? M_PI : 180; - // Step 1. Look for an expression of type "arccos(cos(x))", return x + // Step 1. Look for an expression of type "acos(cos(x))", return x if (AreInverseFunctions(e.childAtIndex(0), e)) { float trigoOp = e.childAtIndex(0).childAtIndex(0).node()->approximate(float(), context, complexFormat, angleUnit).toScalar(); if ((e.type() == ExpressionNode::Type::ArcCosine && trigoOp >= 0.0f && trigoOp <= pi) || @@ -264,7 +264,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c } } - // Step 2. Special case for arctan(sin(x)/cos(x)) + // Step 2. Special case for atan(sin(x)/cos(x)) if (e.type() == ExpressionNode::Type::ArcTangent && ExpressionIsEquivalentToTangent(e.childAtIndex(0))) { float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).node()->approximate(float(), context, complexFormat, angleUnit).toScalar(); if (trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) { @@ -274,7 +274,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c } } - // Step 3. Look for an expression of type "arctan(1/x), return sign(x)*π/2-arctan(x) + // Step 3. Look for an expression of type "atan(1/x), return sign(x)*π/2-atan(x) if (e.type() == ExpressionNode::Type::ArcTangent && e.childAtIndex(0).type() == ExpressionNode::Type::Power && e.childAtIndex(0).childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(0).childAtIndex(1).convert().isMinusOne()) { Expression x = e.childAtIndex(0).childAtIndex(0); /* This equality is not true if x = 0. We apply it under certain conditions: @@ -310,8 +310,8 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c */ Expression p = e.parent(); bool letArcFunctionAtRoot = !p.isUninitialized() && isDirectTrigonometryFunction(p); - /* Step 5. Handle opposite argument: arccos(-x) = π-arcos(x), - * arcsin(-x) = -arcsin(x), arctan(-x)= -arctan(x) * + /* Step 5. Handle opposite argument: acos(-x) = π-acos(x), + * asin(-x) = -asin(x), atan(-x)= -atan(x) * */ if (!letArcFunctionAtRoot) { Expression positiveArg = e.childAtIndex(0).makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index ee9a2149d..fc3436671 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -193,6 +193,11 @@ void assert_parsed_expression_layout_serialize_to_self(const char * expressionLa quiz_assert(strcmp(expressionLayout, buffer) == 0); } +void assert_expression_layouts_as(Poincare::Expression expression, Poincare::Layout layout) { + Layout l = expression.createLayout(DecimalMode, PrintFloat::k_numberOfStoredSignificantDigits); + quiz_assert(l.isIdenticalTo(layout)); +} + void assert_expression_layout_serialize_to(Poincare::Layout layout, const char * serialization) { constexpr int bufferSize = 255; char buffer[bufferSize]; diff --git a/poincare/test/helper.h b/poincare/test/helper.h index 91c8bcb46..091d68d74 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -39,6 +39,7 @@ void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expre void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::ExpressionNode::ReductionTarget target = Poincare::ExpressionNode::ReductionTarget::User, Poincare::Preferences::AngleUnit angleUnit = Poincare::Preferences::AngleUnit::Radian, Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::ComplexFormat::Cartesian); void assert_parsed_expression_serialize_to(Poincare::Expression expression, const char * serializedExpression, Poincare::Preferences::PrintFloatMode mode = DecimalMode, int numberOfSignifiantDigits = 7); +void assert_expression_layouts_as(Poincare::Expression expression, Poincare::Layout layout); // Layouts diff --git a/poincare/test/layout_cursor.cpp b/poincare/test/layout_cursor.cpp new file mode 100644 index 000000000..ee883705c --- /dev/null +++ b/poincare/test/layout_cursor.cpp @@ -0,0 +1,74 @@ +#include +#include +#include "helper.h" + +using namespace Poincare; + +void assert_inserted_layout_points_to(Layout layoutToInsert, Expression * correspondingExpression, Layout layoutToPoint) { + quiz_assert(correspondingExpression != nullptr); + assert_expression_layouts_as(*correspondingExpression, layoutToInsert); + Layout layoutToPointComputed = layoutToInsert.layoutToPointWhenInserting(correspondingExpression); + quiz_assert(layoutToPointComputed.identifier() == layoutToPoint.identifier()); +} + +QUIZ_CASE(poincare_layout_cursor_computation) { + Layout l; + Expression e; + + // 1+2 + l = HorizontalLayout::Builder( + CodePointLayout::Builder('1'), + CodePointLayout::Builder('+'), + CodePointLayout::Builder('2')); + e = Addition::Builder(Rational::Builder(1), Rational::Builder(2)); + assert_inserted_layout_points_to(l, &e, l); + + // random() + HorizontalLayout hl = HorizontalLayout::Builder(); + hl.addChildAtIndex(CodePointLayout::Builder('r'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(CodePointLayout::Builder('a'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(CodePointLayout::Builder('n'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(CodePointLayout::Builder('d'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(CodePointLayout::Builder('o'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(CodePointLayout::Builder('m'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(LeftParenthesisLayout::Builder(), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(RightParenthesisLayout::Builder(), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + l = hl; + e = Random::Builder(); + assert_inserted_layout_points_to(l, &e, l); + + // cos(\x11) + hl = HorizontalLayout::Builder(); + hl.addChildAtIndex(CodePointLayout::Builder('c'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(CodePointLayout::Builder('o'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(CodePointLayout::Builder('s'), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(LeftParenthesisLayout::Builder(), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + hl.addChildAtIndex(RightParenthesisLayout::Builder(), hl.numberOfChildren(), hl.numberOfChildren(), nullptr); + l = hl; + e = Cosine::Builder(EmptyExpression::Builder()); + assert_inserted_layout_points_to(l, &e, l.childAtIndex(3)); + + // •^• + l = HorizontalLayout::Builder( + EmptyLayout::Builder(), + VerticalOffsetLayout::Builder( + EmptyLayout::Builder(), + VerticalOffsetLayoutNode::Position::Superscript) + ); + e = Power::Builder(EmptyExpression::Builder(), EmptyExpression::Builder()); + assert_inserted_layout_points_to(l, &e, l.childAtIndex(0)); + + // int(•, x, •, •) + l = IntegralLayout::Builder( + EmptyLayout::Builder(), + HorizontalLayout::Builder( + CodePointLayout::Builder('x')), + EmptyLayout::Builder(), + EmptyLayout::Builder()); + e = Integral::Builder( + EmptyExpression::Builder(), + Symbol::Builder('x'), + EmptyExpression::Builder(), + EmptyExpression::Builder()); + assert_inserted_layout_points_to(l, &e, l.childAtIndex(2)); +} diff --git a/poincare/test/number.cpp b/poincare/test/number.cpp index 8bb30e4b2..922190419 100644 --- a/poincare/test/number.cpp +++ b/poincare/test/number.cpp @@ -27,6 +27,12 @@ QUIZ_CASE(poincare_number_parser) { assert_parsed_expression_is("123456789012345.2", Decimal::Builder(Integer("12345678901235"), 14)); assert_parsed_expression_is("123456789012341.2", Decimal::Builder(Integer("12345678901234"), 14)); assert_parsed_expression_is("12.34567", Decimal::Builder(Integer("1234567"), 1)); + assert_parsed_expression_is(".999999999999990", Decimal::Builder(Integer("99999999999999"), -1)); + assert_parsed_expression_is("9.99999999999994", Decimal::Builder(Integer("99999999999999"), 0)); + assert_parsed_expression_is("99.9999999999995", Decimal::Builder(Integer("100000000000000"), 2)); + assert_parsed_expression_is("999.999999999999", Decimal::Builder(Integer("100000000000000"), 3)); + assert_parsed_expression_is("9999.99199999999", Decimal::Builder(Integer("99999920000000"), 3)); + assert_parsed_expression_is("99299.9999999999", Decimal::Builder(Integer("99300000000000"), 4)); // Infinity assert_parsed_expression_is("23ᴇ1000", Infinity::Builder(false)); diff --git a/poincare/test/trigo.cpp b/poincare/test/trigo.cpp index 50fed6eea..c7a9df4a7 100644 --- a/poincare/test/trigo.cpp +++ b/poincare/test/trigo.cpp @@ -513,22 +513,22 @@ QUIZ_CASE(poincare_trigo_simplify) { assert_parsed_expression_simplify_to("atan(√(3))", "60", User, Degree); assert_parsed_expression_simplify_to("atan(1/x)", "(π×sign(x)-2×atan(x))/2", User, Degree); - // cos(arcsin) + // cos(asin) assert_parsed_expression_simplify_to("cos(asin(x))", "√(-x^2+1)", User, Degree); assert_parsed_expression_simplify_to("cos(asin(-x))", "√(-x^2+1)", User, Degree); - // cos(arctan) + // cos(atan) assert_parsed_expression_simplify_to("cos(atan(x))", "1/√(x^2+1)", User, Degree); assert_parsed_expression_simplify_to("cos(atan(-x))", "1/√(x^2+1)", User, Degree); - // sin(arccos) + // sin(acos) assert_parsed_expression_simplify_to("sin(acos(x))", "√(-x^2+1)", User, Degree); assert_parsed_expression_simplify_to("sin(acos(-x))", "√(-x^2+1)", User, Degree); - // sin(arctan) + // sin(atan) assert_parsed_expression_simplify_to("sin(atan(x))", "x/√(x^2+1)", User, Degree); assert_parsed_expression_simplify_to("sin(atan(-x))", "-x/√(x^2+1)", User, Degree); - // tan(arccos) + // tan(acos) assert_parsed_expression_simplify_to("tan(acos(x))", "√(-x^2+1)/x", User, Degree); assert_parsed_expression_simplify_to("tan(acos(-x))", "-√(-x^2+1)/x", User, Degree); - // tan(arcsin) + // tan(asin) assert_parsed_expression_simplify_to("tan(asin(x))", "x/√(-x^2+1)", User, Degree); assert_parsed_expression_simplify_to("tan(asin(-x))", "-x/√(-x^2+1)", User, Degree); }