Merge branch 'version-11' into f7

This commit is contained in:
Léa Saviot
2019-06-06 10:10:50 +02:00
61 changed files with 412 additions and 177 deletions

View File

@@ -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, _"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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();
}

View File

@@ -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),

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 \

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -1,4 +1,6 @@
#include "exponential_model.h"
#include "../store.h"
#include "../linear_model_helper.h"
#include <math.h>
#include <assert.h>
#include <poincare/code_point_layout.h>
@@ -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];

View File

@@ -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; }

View File

@@ -1,4 +1,5 @@
#include "store.h"
#include "linear_model_helper.h"
#include "apps/apps_container.h"
#include <poincare/preferences.h>
#include <assert.h>
@@ -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) {

View File

@@ -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};

View File

@@ -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"

View File

@@ -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 "

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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."

View File

@@ -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."

View File

@@ -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:

View File

@@ -1,5 +1,4 @@
#include <escher/layout_field.h>
#include <apps/i18n.h>
#include <escher/clipboard.h>
#include <escher/text_field.h>
#include <poincare/expression.h>
@@ -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);

View File

@@ -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(),

View File

@@ -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(),

View File

@@ -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))

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -36,6 +36,7 @@ typedef struct {
} image_t;
#ifdef GENERATE_PNG
#define PNG_SKIP_SETJMP_CHECK
#include <png.h>
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(

View File

@@ -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];
};

View File

@@ -159,6 +159,7 @@ tests += $(addprefix poincare/test/,\
infinity.cpp\
integer.cpp\
layouts.cpp\
layout_cursor.cpp\
logarithm.cpp\
matrix.cpp\
multiplication.cpp\

View File

@@ -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

View File

@@ -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

View File

@@ -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<HorizontalLayoutNode *>(this)->childAtIndex(0)->isEmpty(); }

View File

@@ -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<typename T> T lagrangeGaussQuadrature(T a, T b, Context Context & context, Preferences::AngleUnit angleUnit context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
#else

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -39,7 +39,7 @@ Complex<T> ArcCosineNode::computeOnComplex(const std::complex<T> 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
}

View File

@@ -39,7 +39,7 @@ Complex<T> ArcSineNode::computeOnComplex(const std::complex<T> 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
}

View File

@@ -35,7 +35,7 @@ Complex<T> ArcTangentNode::computeOnComplex(const std::complex<T> 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
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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.*/

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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<Rational>().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);

View File

@@ -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];

View File

@@ -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

View File

@@ -0,0 +1,74 @@
#include <quiz.h>
#include <poincare_layouts.h>
#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));
}

View File

@@ -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));

View File

@@ -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);
}