mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
Merge branch 'version-11' into f7
This commit is contained in:
@@ -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, _"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 \
|
||||
|
||||
17
apps/regression/linear_model_helper.cpp
Normal file
17
apps/regression/linear_model_helper.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
15
apps/regression/linear_model_helper.h
Normal file
15
apps/regression/linear_model_helper.h
Normal 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
|
||||
@@ -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];
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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))
|
||||
|
||||
BIN
kandinsky/fonts/LargeFont.ttf
Normal file
BIN
kandinsky/fonts/LargeFont.ttf
Normal file
Binary file not shown.
Binary file not shown.
BIN
kandinsky/fonts/SmallFont.ttf
Normal file
BIN
kandinsky/fonts/SmallFont.ttf
Normal file
Binary file not shown.
Binary file not shown.
@@ -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(
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
@@ -159,6 +159,7 @@ tests += $(addprefix poincare/test/,\
|
||||
infinity.cpp\
|
||||
integer.cpp\
|
||||
layouts.cpp\
|
||||
layout_cursor.cpp\
|
||||
logarithm.cpp\
|
||||
matrix.cpp\
|
||||
multiplication.cpp\
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.*/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
74
poincare/test/layout_cursor.cpp
Normal file
74
poincare/test/layout_cursor.cpp
Normal 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));
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user