Merge remote-tracking branch 'upstream/master' into omega-dev

This commit is contained in:
Quentin Guidée
2019-11-26 13:17:49 +01:00
30 changed files with 197 additions and 95 deletions

View File

@@ -18,14 +18,14 @@ void Model::tidy() {
Poincare::Expression Model::simplifiedExpression(double * modelCoefficients, Poincare::Context * context) {
Expression e = expression(modelCoefficients);
if (!e.isUninitialized()) {
PoincareHelpers::Simplify(&e, context);
PoincareHelpers::Simplify(&e, context, ExpressionNode::ReductionTarget::SystemForApproximation);
}
return e;
}
double Model::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) {
Expression yExpression = Number::DecimalNumber(y);
PoincareHelpers::Simplify(&yExpression, context);
PoincareHelpers::Simplify(&yExpression, context, ExpressionNode::ReductionTarget::SystemForApproximation);
Expression modelExpression = simplifiedExpression(modelCoefficients, context);
double result = PoincareHelpers::NextIntersection(modelExpression, "x", xMin, step, xMax, context, yExpression).x1();
return result;

View File

@@ -24,6 +24,8 @@ ValuesController::ValuesController(Responder * parentResponder, InputEventHandle
StackViewController * stack = ((StackViewController *)valuesController->stackController());
IntervalParameterController * controller = valuesController->intervalParameterController();
controller->setInterval(valuesController->intervalAtColumn(valuesController->selectedColumn()));
/* No need to change Nstart/Nend messages because they are the only messages
* used and we set them in ValuesController::ValuesController(...) */
stack->push(controller);
return true;
}, this), k_font)
@@ -32,6 +34,7 @@ ValuesController::ValuesController(Responder * parentResponder, InputEventHandle
m_sequenceTitleCells[i].setOrientation(Shared::FunctionTitleCell::Orientation::HorizontalIndicator);
}
setupSelectableTableViewAndCells(inputEventHandlerDelegate);
setDefaultStartEndMessages();
}
// TableViewDataSource
@@ -64,7 +67,7 @@ I18n::Message ValuesController::emptyMessage() {
}
// ValuesController
void ValuesController::setStartEndMessages(Shared::IntervalParameterController * controller, int column) {
void ValuesController::setDefaultStartEndMessages() {
m_intervalParameterController.setStartEndMessages(I18n::Message::NStart, I18n::Message::NEnd);
}

View File

@@ -33,7 +33,11 @@ private:
constexpr static int k_maxNumberOfDisplayableCells = k_maxNumberOfDisplayableSequences * k_maxNumberOfDisplayableRows;
// ValuesController
void setStartEndMessages(Shared::IntervalParameterController * controller, int column) override;
void setStartEndMessages(Shared::IntervalParameterController * controller, int column) override {
setDefaultStartEndMessages();
}
void setDefaultStartEndMessages();
I18n::Message valuesParameterMessageAtColumn(int columnIndex) const override;
int maxNumberOfCells() override { return k_maxNumberOfDisplayableCells; }
int maxNumberOfFunctions() override { return k_maxNumberOfDisplayableSequences; }

View File

@@ -47,7 +47,7 @@ Expression ExpressionModel::expressionReduced(const Storage::Record * record, Po
m_expression = Undefined::Builder();
} else {
m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record));
PoincareHelpers::Simplify(&m_expression, context);
PoincareHelpers::Simplify(&m_expression, context, ExpressionNode::ReductionTarget::SystemForApproximation);
// simplify might return an uninitialized Expression if interrupted
if (m_expression.isUninitialized()) {
m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record));

View File

@@ -62,10 +62,10 @@ inline Poincare::Expression ParseAndSimplify(const char * text, Poincare::Contex
return Poincare::Expression::ParseAndSimplify(text, context, complexFormat, preferences->angleUnit(), symbolicComputation);
}
inline void Simplify(Poincare::Expression * e, Poincare::Context * context, bool symbolicComputation = true) {
inline void Simplify(Poincare::Expression * e, Poincare::Context * context, Poincare::ExpressionNode::ReductionTarget target, bool symbolicComputation = true) {
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), *e, context);
*e = e->simplify(context, complexFormat, preferences->angleUnit(), symbolicComputation);
*e = e->simplify(context, complexFormat, preferences->angleUnit(), target, symbolicComputation);
}
inline void ParseAndSimplifyAndApproximate(const char * text, Poincare::Expression * simplifiedExpression, Poincare::Expression * approximateExpression, Poincare::Context * context, bool symbolicComputation = true) {

View File

@@ -243,10 +243,23 @@ void ValuesController::didChangeCell(int column, int row) {
return;
}
// Find the abscissa column corresponding to column
int abscissaColumn = 0;
int nbOfColumns = numberOfColumnsForAbscissaColumn(abscissaColumn);
while (column >= nbOfColumns) {
abscissaColumn = nbOfColumns;
nbOfColumns += numberOfColumnsForAbscissaColumn(abscissaColumn);
}
// Update the memoization of rows linked to the changed cell
int nbOfMemoizedColumns = numberOfMemoizedColumn();
for (int i = column+1; i < column+numberOfColumnsForAbscissaColumn(column); i++) {
int nbOfColumnsForAbscissa = numberOfColumnsForAbscissaColumn(abscissaColumn);
for (int i = abscissaColumn+1; i < abscissaColumn+nbOfColumnsForAbscissa; i++) {
int memoizedI = valuesColumnForAbsoluteColumn(i) - m_firstMemoizedColumn;
if (memoizedI < 0 || memoizedI >= nbOfMemoizedColumns) {
// The changed column is out of the memoized table
continue;
}
fillMemoizedBuffer(i, row, nbOfMemoizedColumns*memoizedRow+memoizedI);
}
}

View File

@@ -276,7 +276,7 @@ EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exact
assert(degree == 2);
// Compute delta = b*b-4ac
Expression delta = Subtraction::Builder(Power::Builder(coefficients[1].clone(), Rational::Builder(2)), Multiplication::Builder(Rational::Builder(4), coefficients[0].clone(), coefficients[2].clone()));
delta = delta.simplify(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit());
delta = delta.simplify(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit(), ExpressionNode::ReductionTarget::SystemForApproximation);
if (delta.isUninitialized()) {
delta = Poincare::Undefined::Builder();
}

View File

@@ -128,6 +128,11 @@ QUIZ_CASE(equation_solve) {
const char * solutions11[] = {"\u0012\u0012\u0012π^\u00122\u0013-2π+8√\u00125\u0013+9\u0013-π+1\u0013/\u00124\u0013\u0013", "\u0012\u0012-√\u0012π^\u00122\u0013-2π+8√\u00125\u0013+9\u0013-π+1\u0013/\u00124\u0013\u0013", "π^\u00122\u0013-2π+8√\u00125\u0013+9"}; // (√(π^2-2π+8√(5)+9)-π+1)/4, (-√(π^2-2π+8×√(5)+9)-π+1)/4, π^2-2π+8√(5)+9
assert_equation_system_exact_solve_to(equations11, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions11, 3);
// (x-3)^2
const char * equations21[] = {"(x-3)^2=0", 0};
const char * solutions21[] = {"3", "0"};
assert_equation_system_exact_solve_to(equations21, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions21, 2);
// TODO
// x^3 - 4x^2 + 6x - 24 = 0
//const char * equations10[] = {"2×x^2-4×x+4=3", 0};
@@ -205,6 +210,11 @@ QUIZ_CASE(equation_solve_complex_format) {
const char * equations4[] = {"x+√(-1)×√(-1)=0", 0};
assert_equation_system_exact_solve_to(equations4, EquationStore::Error::EquationUnreal, EquationStore::Type::LinearSystem, (const char **)variablesx, nullptr, 0);
// root(-8,3)*x+3 = 0 --> 3/2 in R
const char * equations5[] = {"root(-8,3)*x+3=0", 0};
const char * solutions5[] = {"\u0012\u00123\u0013/\u00122\u0013\u0013"};
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5, 1);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
// x+𝐢 = 0 --> x = -𝐢
assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1);
@@ -224,6 +234,9 @@ QUIZ_CASE(equation_solve_complex_format) {
const char * solutions4[] = {"1"};
assert_equation_system_exact_solve_to(equations4, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions4, 1);
const char * solutions5Cartesain[] = {"-\u0012\u00123\u0013/\u00124\u0013\u0013+\u0012\u00123√\u00123\u0013\u0013/\u00124\u0013\u0013𝐢"}; //-3/4+(3√3/4)*𝐢
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5Cartesain, 1);
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Polar);
// x+𝐢 = 0 --> x = e^(-π/2×i)
const char * solutions0Polar[] = {"^\u0012-\u0012\u0012π\u0013/\u00122\u0013\u0013𝐢\u0013"}; // ^(-(π/2)𝐢)
@@ -240,6 +253,9 @@ QUIZ_CASE(equation_solve_complex_format) {
const char * solutions3Polar[] = {"^\u0012-\u0012\u0012\u0013/\u00124\u0013\u0013𝐢\u0013", "^\u0012\u0012\u0012π\u0013/\u00124\u0013\u0013𝐢\u0013", "4^\u0012\u0012\u0012π\u0013/\u00122\u0013\u0013𝐢\u0013"}; // ^(-(3×π/4)𝐢)"‰, "^((π/4)𝐢)", "4^((π/2)𝐢)
assert_equation_system_exact_solve_to(equations3, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions3Polar, 3);
const char * solutions5Polar[] = {"\u0012\u00123\u0013/\u00122\u0013\u0013^\u0012\u0012\u0012\u0013/\u00123\u0013\u0013𝐢\u0013"}; //3/2^\u0012\u00122π\u0012/3\u0013𝐢"};
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5Polar, 1);
}
QUIZ_CASE(equation_and_symbolic_computation) {

View File

@@ -12,7 +12,10 @@ $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ex
@echo " using an USB cable and press at the same time the 6 key and the RESET"
@echo " button on the back of your device."
$(Q) until $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11" > /dev/null 2>&1; do sleep 2;done
$(Q) $(PYTHON) build/device/dfu.py -u $(word 2,$^)
$(Q) sleep 2
$(eval DFU_SLAVE := $(shell $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11"))
$(Q) if [[ "$(DFU_SLAVE)" == *"0483:df11"* ]]; \
then \
$(PYTHON) build/device/dfu.py -u $(word 2,$^); \
sleep 2; \
fi
$(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^)

View File

@@ -345,11 +345,14 @@ Storage::Record::ErrorStatus Storage::setBaseNameWithExtensionOfRecord(Record re
return notifyFullnessToDelegate();
}
overrideSizeAtPosition(p, newRecordSize);
overrideBaseNameWithExtensionAtPosition(p+sizeof(record_size_t), baseName, extension);
char * fullNamePosition = p + sizeof(record_size_t);
overrideBaseNameWithExtensionAtPosition(fullNamePosition, baseName, extension);
// Recompute the CRC32
record = Record(fullNamePosition);
notifyChangeToDelegate(record);
m_lastRecordRetrieved = record;
m_lastRecordRetrievedPointer = p;
return Record::ErrorStatus::None;
return Record::ErrorStatus::None;
}
return Record::ErrorStatus::RecordDoesNotExist;
}

View File

@@ -5,7 +5,6 @@
namespace UTF8Helper {
static inline int minInt(int x, int y) { return x < y ? x : y; }
static inline size_t minSizeT(size_t x, size_t y) { return x < y ? x : y; }
int CountOccurrences(const char * s, CodePoint c) {
@@ -110,7 +109,11 @@ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePo
// Remove CodePoint c
while (codePoint != UCodePointNull && bufferIndex < dstSize) {
if (codePoint != c) {
int copySize = minInt(nextPointer - currentPointer, dstSize - bufferIndex);
int copySize = nextPointer - currentPointer;
if (copySize > dstSize - 1 - bufferIndex) {
// Copying the current code point to the buffer would overflow the buffer
break;
}
memcpy(dst + bufferIndex, currentPointer, copySize);
bufferIndex+= copySize;
}
@@ -118,7 +121,7 @@ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePo
codePoint = decoder.nextCodePoint();
nextPointer = decoder.stringPosition();
}
*(dst + minInt(bufferIndex, dstSize - 1)) = 0;
*(dst + bufferIndex) = 0;
}
void RemoveCodePoint(char * buffer, CodePoint c, const char * * pointerToUpdate, const char * stoppingPosition) {

View File

@@ -87,6 +87,21 @@ QUIZ_CASE(ion_utf8_copy_and_remove_code_point) {
c = UCodePointLatinLetterSmallCapitalE;
result = "124";
assert_copy_and_remove_code_point_gives(buffer, bufferSize, s, c, result);
// The buffer size is to small to hold s
s = "1234ᴇ";
c = '5';
result = "1234"; // "1234ᴇ" size is 7
assert_copy_and_remove_code_point_gives(buffer, 6, s, c, result);
assert_copy_and_remove_code_point_gives(buffer, 7, s, c, result);
result = "1234ᴇ";
assert_copy_and_remove_code_point_gives(buffer, 8, s, c, result);
s = "1234ᴇ";
c = '4';
result = "123ᴇ";
assert_copy_and_remove_code_point_gives(buffer, 7, s, c, result);
}
void assert_remove_code_point_gives(char * buffer, CodePoint c, const char * * indexToUpdate, const char * stoppingPosition, const char * indexToUpdateResult, const char * result) {

View File

@@ -221,11 +221,11 @@ public:
* (For instance, in Polar mode, they return an expression of the form
* r*e^(i*th) reduced and approximated.) */
static Expression ParseAndSimplify(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
Expression simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
Expression simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool symbolicComputation = true);
static void ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
void simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
Expression reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
Expression reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target = ExpressionNode::ReductionTarget::SystemForApproximation);
Expression mapOnMatrixFirstChild(ExpressionNode::ReductionContext reductionContext);
static Expression ExpressionWithoutSymbols(Expression expressionWithSymbols, Context * context);
@@ -342,7 +342,7 @@ protected:
* Warning: this must be called on reduced expressions
*/
Expression makePositiveAnyNegativeNumeralFactor(ExpressionNode::ReductionContext reductionContext);
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { return node()->denominator(context, complexFormat, angleUnit); }
Expression denominator(ExpressionNode::ReductionContext reductionContext) const { return node()->denominator(reductionContext); }
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext) { return node()->shallowReduce(reductionContext); }
Expression shallowBeautify(ExpressionNode::ReductionContext reductionContext) { return node()->shallowBeautify(reductionContext); }
Expression deepBeautify(ExpressionNode::ReductionContext reductionContext);

View File

@@ -110,7 +110,15 @@ public:
/* Properties */
enum class ReductionTarget {
System = 0,
/* Minimal reduction: this at least reduces rationals operations as
* "1-0.3-0.7 --> 0" */
SystemForApproximation = 0,
/* Expansion of Newton multinome to be able to identify polynoms */
SystemForAnalysis,
/* Additional features as:
* - factorizing on a common denominator
* - turning complex expression into the form a+ib
* - identifying tangent in cos/sin polynoms ... */
User
};
enum class Sign {
@@ -196,7 +204,7 @@ public:
/*!*/ virtual Expression shallowReduce(ReductionContext reductionContext);
/*!*/ virtual Expression shallowBeautify(ReductionContext reductionContext);
/* Return a clone of the denominator part of the expression */
/*!*/ virtual Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
/*!*/ virtual Expression denominator(ExpressionNode::ReductionContext reductionContext) const;
/* LayoutShape is used to check if the multiplication sign can be omitted between two expressions. It depends on the "layout syle" of the on the right of the left expression */
enum class LayoutShape {
Decimal,

View File

@@ -47,7 +47,7 @@ private:
// Simplification
Expression shallowReduce(ReductionContext reductionContext) override;
Expression shallowBeautify(ReductionContext reductionContext) override;
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override;
Expression denominator(ExpressionNode::ReductionContext reductionContext) const override;
// Approximation
template<typename T> static MatrixComplex<T> computeOnMatrixAndComplex(const MatrixComplex<T> m, const std::complex<T> c, Preferences::ComplexFormat complexFormat) {
@@ -82,7 +82,7 @@ public:
Expression setSign(ExpressionNode::Sign s, ExpressionNode::ReductionContext reductionContext);
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
Expression shallowBeautify(ExpressionNode::ReductionContext reductionContext);
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
Expression denominator(ExpressionNode::ReductionContext reductionContext) const;
void sortChildrenInPlace(NAryExpressionNode::ExpressionOrder order, Context * context, bool canBeInterrupted) {
NAryExpression::sortChildrenInPlace(order, context, false, canBeInterrupted);
}
@@ -94,8 +94,8 @@ private:
void mergeInChildByFactorizingBase(int i, Expression e, ExpressionNode::ReductionContext reductionContext);
void factorizeExponent(int i, int j, ExpressionNode::ReductionContext reductionContext);
Expression distributeOnOperandAtIndex(int index, ExpressionNode::ReductionContext reductionContext);
void addMissingFactors(Expression factor, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
void factorizeSineAndCosine(int i, int j, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
void addMissingFactors(Expression factor, ExpressionNode::ReductionContext reductionContext);
void factorizeSineAndCosine(int i, int j, ExpressionNode::ReductionContext reductionContext);
static bool HaveSameNonNumeralFactors(const Expression & e1, const Expression & e2);
static bool TermsHaveIdenticalBase(const Expression & e1, const Expression & e2);
static bool TermsHaveIdenticalExponent(const Expression & e1, const Expression & e2);
@@ -104,7 +104,7 @@ private:
static const Expression CreateExponent(Expression e);
/* Warning: mergeNegativePower doesnot always return a multiplication:
* *(b^-1,c^-1) -> (bc)^-1 */
Expression mergeNegativePower(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
Expression mergeNegativePower(ExpressionNode::ReductionContext reductionContext);
static inline const Expression Base(const Expression e);
};

View File

@@ -52,7 +52,7 @@ private:
LayoutShape rightLayoutShape() const override { return LayoutShape::RightOfPower; }
int simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override;
int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override;
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override;
Expression denominator(ReductionContext reductionContext) const override;
// Evaluation
template<typename T> static MatrixComplex<T> computeOnComplexAndMatrix(const std::complex<T> c, const MatrixComplex<T> n, Preferences::ComplexFormat complexFormat);
template<typename T> static MatrixComplex<T> computeOnMatrixAndComplex(const MatrixComplex<T> m, const std::complex<T> d, Preferences::ComplexFormat complexFormat);
@@ -82,7 +82,7 @@ private:
constexpr static int k_maxNumberOfTermsInExpandedMultinome = 25;
// Simplification
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
Expression denominator(ExpressionNode::ReductionContext reductionContext) const;
Expression simplifyPowerPower(ExpressionNode::ReductionContext reductionContext);
Expression simplifyPowerMultiplication(ExpressionNode::ReductionContext reductionContext);

View File

@@ -57,7 +57,7 @@ private:
Expression shallowBeautify(ReductionContext reductionContext) override;
LayoutShape leftLayoutShape() const override { assert(!m_negative); return isInteger() ? LayoutShape::Integer : LayoutShape::Fraction; };
Expression setSign(Sign s, ReductionContext reductionContext) override;
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override;
Expression denominator(ReductionContext reductionContext) const override;
bool m_negative;
uint8_t m_numberOfDigitsNumerator;
uint8_t m_numberOfDigitsDenominator;

View File

@@ -354,7 +354,7 @@ Expression Addition::factorizeOnCommonDenominator(ExpressionNode::ReductionConte
Multiplication commonDenominator = Multiplication::Builder();
for (int i = 0; i < numberOfChildren(); i++) {
Expression childI = childAtIndex(i);
Expression currentDenominator = childI.denominator(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
Expression currentDenominator = childI.denominator(reductionContext);
if (!currentDenominator.isUninitialized()) {
if (currentDenominator.recursivelyMatches(Expression::IsRandom, reductionContext.context(), true)) {
// Remove "random" factors
@@ -364,7 +364,7 @@ Expression Addition::factorizeOnCommonDenominator(ExpressionNode::ReductionConte
continue;
}
// Make commonDenominator = LeastCommonMultiple(commonDenominator, denominator);
commonDenominator.addMissingFactors(currentDenominator, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
commonDenominator.addMissingFactors(currentDenominator, reductionContext);
}
}
if (commonDenominator.numberOfChildren() == 0) {

View File

@@ -46,7 +46,11 @@ Evaluation<T> EqualNode::templatedApproximate(Context * context, Preferences::Co
Expression Equal::standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
Expression sub = Subtraction::Builder(childAtIndex(0).clone(), childAtIndex(1).clone());
return sub.reduce(context, complexFormat, angleUnit);
/* When reducing the equation, we specify the reduction target to be
* SystemForAnalysis. This enables to expand Newton multinom to be able to
* detect polynom correctly ("(x+2)^2" in this form won't be detected
* unless expanded). */
return sub.reduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForAnalysis);
}
Expression Equal::shallowReduce() {

View File

@@ -519,7 +519,7 @@ Expression Expression::ParseAndSimplify(const char * text, Context * context, Pr
if (exp.isUninitialized()) {
return Undefined::Builder();
}
exp = exp.simplify(context, complexFormat, angleUnit, symbolicSimplification);
exp = exp.simplify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, symbolicSimplification);
/* simplify might have been interrupted, in which case the resulting
* expression is uninitialized, so we need to check that. */
if (exp.isUninitialized()) {
@@ -547,9 +547,9 @@ void Expression::ParseAndSimplifyAndApproximate(const char * text, Expression *
}
}
Expression Expression::simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
Expression Expression::simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool symbolicComputation) {
sSimplificationHasBeenInterrupted = false;
ExpressionNode::ReductionContext c = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, symbolicComputation);
ExpressionNode::ReductionContext c = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, target, symbolicComputation);
Expression e = deepReduce(c);
if (!sSimplificationHasBeenInterrupted) {
e = e.deepBeautify(c);
@@ -618,7 +618,7 @@ void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expre
Expression e = clone().deepReduce(userReductionContext);
if (sSimplificationHasBeenInterrupted) {
sSimplificationHasBeenInterrupted = false;
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, symbolicComputation);
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation, symbolicComputation);
e = deepReduce(systemReductionContext);
}
*simplifiedExpression = Expression();
@@ -730,9 +730,9 @@ Expression Expression::angleUnitToRadian(Preferences::AngleUnit angleUnit) {
return *this;
}
Expression Expression::reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
Expression Expression::reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
sSimplificationHasBeenInterrupted = false;
return deepReduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, true));
return deepReduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, target, true));
}
Expression Expression::deepReduce(ExpressionNode::ReductionContext reductionContext) {
@@ -856,8 +856,13 @@ Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Pre
Expression norm;
Expression exp;
if (!isOneRa || isZeroTb) {
assert(!isNegativeRa); // norm cannot be negative
norm = ra;
/* Norm cannot be negative but can be preceded by a negative sign (for
* instance "-log(0.3)") which would lead to isNegativeRa = True. */
if (isNegativeRa) {
norm = Opposite::Builder(ra);
} else {
norm = ra;
}
}
if (!isZeroRa && !isZeroTb) {
Expression arg;

View File

@@ -129,7 +129,7 @@ void ExpressionNode::setChildrenInPlace(Expression other) {
Expression(this).defaultSetChildrenInPlace(other);
}
Expression ExpressionNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
Expression ExpressionNode::denominator(ReductionContext reductionContext) const {
return Expression();
}

View File

@@ -115,7 +115,7 @@ void Matrix::addChildrenAsRowInPlace(TreeHandle t, int i) {
int Matrix::rank(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool inPlace) {
Matrix m = inPlace ? *this : clone().convert<Matrix>();
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System);
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation);
m = m.rowCanonize(systemReductionContext, nullptr);
int rank = m.numberOfRows();
int i = rank-1;

View File

@@ -219,8 +219,8 @@ Expression MultiplicationNode::shallowBeautify(ReductionContext reductionContext
return Multiplication(this).shallowBeautify(reductionContext);
}
Expression MultiplicationNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
return Multiplication(this).denominator(context, complexFormat, angleUnit);
Expression MultiplicationNode::denominator(ReductionContext reductionContext) const {
return Multiplication(this).denominator(reductionContext);
}
/* Multiplication */
@@ -306,7 +306,7 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
/* Step 2: Merge negative powers: a*b^(-1)*c^(-pi)*d = a*(b*c^pi)^(-1)
* This also turns 2/3*a into 2*a*3^(-1) */
Expression thisExp = mergeNegativePower(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
Expression thisExp = mergeNegativePower(reductionContext);
if (thisExp.type() == ExpressionNode::Type::Power) {
return thisExp.shallowBeautify(reductionContext);
}
@@ -339,13 +339,13 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
return thisExp;
}
Expression Multiplication::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
Expression Multiplication::denominator(ExpressionNode::ReductionContext reductionContext) const {
// Merge negative power: a*b^-1*c^(-Pi)*d = a*(b*c^Pi)^-1
// WARNING: we do not want to change the expression but to create a new one.
Multiplication thisClone = clone().convert<Multiplication>();
Expression e = thisClone.mergeNegativePower(context, complexFormat, angleUnit);
Expression e = thisClone.mergeNegativePower(reductionContext);
if (e.type() == ExpressionNode::Type::Power) {
return e.denominator(context, complexFormat, angleUnit);
return e.denominator(reductionContext);
} else {
assert(e.type() == ExpressionNode::Type::Multiplication);
for (int i = 0; i < e.numberOfChildren(); i++) {
@@ -511,7 +511,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext
for (int j = i+1; j < numberOfChildren(); j++) {
Expression o2 = childAtIndex(j);
if (Base(o2).type() == ExpressionNode::Type::Cosine && TermHasNumeralExponent(o2) && Base(o2).childAtIndex(0).isIdenticalTo(x)) {
factorizeSineAndCosine(i, j, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
factorizeSineAndCosine(i, j, reductionContext);
break;
}
}
@@ -738,10 +738,10 @@ Expression Multiplication::distributeOnOperandAtIndex(int i, ExpressionNode::Red
return a.shallowReduce(reductionContext); // Order terms, put under a common denominator if needed
}
void Multiplication::addMissingFactors(Expression factor, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
void Multiplication::addMissingFactors(Expression factor, ExpressionNode::ReductionContext reductionContext) {
if (factor.type() == ExpressionNode::Type::Multiplication) {
for (int j = 0; j < factor.numberOfChildren(); j++) {
addMissingFactors(factor.childAtIndex(j), context, complexFormat, angleUnit);
addMissingFactors(factor.childAtIndex(j), reductionContext);
}
return;
}
@@ -762,7 +762,6 @@ void Multiplication::addMissingFactors(Expression factor, Context * context, Pre
if (factor.type() != ExpressionNode::Type::Rational) {
/* If factor is not a rational, we merge it with the child of identical
* base if any. Otherwise, we add it as an new child. */
ExpressionNode::ReductionContext reductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User);
for (int i = 0; i < numberOfChildren(); i++) {
if (TermsHaveIdenticalBase(childAtIndex(i), factor)) {
Expression sub = Subtraction::Builder(CreateExponent(childAtIndex(i)), CreateExponent(factor)).deepReduce(reductionContext);
@@ -783,10 +782,10 @@ void Multiplication::addMissingFactors(Expression factor, Context * context, Pre
}
}
addChildAtIndexInPlace(factor.clone(), 0, numberOfChildren());
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true);
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true);
}
void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
void Multiplication::factorizeSineAndCosine(int i, int j, ExpressionNode::ReductionContext reductionContext) {
/* This function turn sin(x)^p * cos(x)^q into either:
* - tan(x)^p*cos(x)^(p+q) if |p|<|q|
* - tan(x)^(-q)*sin(x)^(p+q) otherwise */
@@ -802,7 +801,6 @@ void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Pre
Number absP = p.clone().convert<Number>().setSign(ExpressionNode::Sign::Positive);
Number absQ = q.clone().convert<Number>().setSign(ExpressionNode::Sign::Positive);
Expression tan = Tangent::Builder(x.clone());
ExpressionNode::ReductionContext userReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User);
if (Number::NaturalOrder(absP, absQ) < 0) {
// Replace sin(x) by tan(x) or sin(x)^p by tan(x)^p
if (p.isRationalOne()) {
@@ -810,19 +808,19 @@ void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Pre
} else {
replaceChildAtIndexInPlace(i, Power::Builder(tan, p));
}
childAtIndex(i).shallowReduce(userReductionContext);
childAtIndex(i).shallowReduce(reductionContext);
// Replace cos(x)^q by cos(x)^(p+q)
replaceChildAtIndexInPlace(j, Power::Builder(Base(childAtIndex(j)), sumPQ));
childAtIndex(j).shallowReduce(userReductionContext);
childAtIndex(j).shallowReduce(reductionContext);
} else {
// Replace cos(x)^q by tan(x)^(-q)
Expression newPower = Power::Builder(tan, Number::Multiplication(q, Rational::Builder(-1)));
newPower.childAtIndex(1).shallowReduce(userReductionContext);
newPower.childAtIndex(1).shallowReduce(reductionContext);
replaceChildAtIndexInPlace(j, newPower);
newPower.shallowReduce(userReductionContext);
newPower.shallowReduce(reductionContext);
// Replace sin(x)^p by sin(x)^(p+q)
replaceChildAtIndexInPlace(i, Power::Builder(Base(childAtIndex(i)), sumPQ));
childAtIndex(i).shallowReduce(userReductionContext);
childAtIndex(i).shallowReduce(reductionContext);
}
}
@@ -876,7 +874,7 @@ bool Multiplication::TermHasNumeralExponent(const Expression & e) {
return false;
}
Expression Multiplication::mergeNegativePower(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
Expression Multiplication::mergeNegativePower(ExpressionNode::ReductionContext reductionContext) {
/* mergeNegativePower groups all factors that are power of form a^(-b) together
* for instance, a^(-1)*b^(-c)*c = c*(a*b^c)^(-1) */
Multiplication m = Multiplication::Builder();
@@ -895,7 +893,7 @@ Expression Multiplication::mergeNegativePower(Context * context, Preferences::Co
while (i < numberOfChildren()) {
if (childAtIndex(i).type() == ExpressionNode::Type::Power) {
Expression p = childAtIndex(i);
Expression positivePIndex = p.childAtIndex(1).makePositiveAnyNegativeNumeralFactor( ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User));
Expression positivePIndex = p.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(reductionContext);
if (!positivePIndex.isUninitialized()) {
// Remove a^(-b) from the Multiplication
removeChildAtIndexInPlace(i);
@@ -913,10 +911,10 @@ Expression Multiplication::mergeNegativePower(Context * context, Preferences::Co
if (m.numberOfChildren() == 0) {
return *this;
}
m.sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true);
m.sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true);
Power p = Power::Builder(m.squashUnaryHierarchyInPlace(), Rational::Builder(-1));
addChildAtIndexInPlace(p, 0, numberOfChildren());
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true);
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true);
return squashUnaryHierarchyInPlace();
}

View File

@@ -211,8 +211,8 @@ int PowerNode::simplificationOrderSameType(const ExpressionNode * e, bool ascend
return SimplificationOrder(childAtIndex(1), e->childAtIndex(1), ascending, canBeInterrupted);
}
Expression PowerNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
return Power(this).denominator(context, complexFormat, angleUnit);
Expression PowerNode::denominator(ReductionContext reductionContext) const {
return Power(this).denominator(reductionContext);
}
// Evaluation
@@ -743,8 +743,12 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
}
/* Step 13: (a0+a1+...am)^n with n integer
* -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome) */
* -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome)
* We don't apply this rule when the target is the SystemForApproximation.
* Indeed, developing the multinome is likely to increase the numbers of
* operations and lead to precision loss. */
if (!letPowerAtRoot
&& reductionContext.target() != ExpressionNode::ReductionTarget::SystemForApproximation
&& indexType == ExpressionNode::Type::Rational
&& !static_cast<Rational &>(index).signedIntegerNumerator().isZero()
&& static_cast<Rational &>(index).isInteger()
@@ -845,7 +849,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionContext) {
// Step 1: X^-y -> 1/(X->shallowBeautify)^y
Expression p = denominator(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
Expression p = denominator(reductionContext);
// If the denominator is initialized, the index of the power is of form -y
if (!p.isUninitialized()) {
Division d = Division::Builder(Rational::Builder(1), p);
@@ -867,12 +871,12 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont
return result;
}
/* Optional Step 3: if the ReductionTarget is the System, turn a^(p/q) into
* (root(a, q))^p
/* Optional Step 3: if the ReductionTarget is the SystemForApproximation or
* SystemForAnalysis, turn a^(p/q) into (root(a, q))^p
* Indeed, root(a, q) can have a real root which is not the principale angle
* but that we want to return in real complex format. This special case is
* handled in NthRoot approximation but not in Power approximation. */
if (reductionContext.target() == ExpressionNode::ReductionTarget::System && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
if (reductionContext.target() != ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
Integer p = childAtIndex(1).convert<Rational>().signedIntegerNumerator();
Integer q = childAtIndex(1).convert<Rational>().integerDenominator();
Expression nthRoot = q.isOne() ? childAtIndex(0) : NthRoot::Builder(childAtIndex(0), Rational::Builder(q));
@@ -887,11 +891,11 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont
// Private
// Simplification
Expression Power::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
Expression Power::denominator(ExpressionNode::ReductionContext reductionContext) const {
// Clone the power
Expression clone = Power::Builder(childAtIndex(0).clone(), childAtIndex(1).clone());
// If the power is of form x^(-y), denominator should be x^y
Expression positiveIndex = clone.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User));
Expression positiveIndex = clone.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(reductionContext);
if (!positiveIndex.isUninitialized()) {
// if y was -1, clone is now x^1, denominator is then only x
// we cannot shallowReduce the clone as it is not attached to its parent yet

View File

@@ -145,7 +145,7 @@ Expression RationalNode::shallowBeautify(ReductionContext reductionContext) {
return Rational(this).shallowBeautify();
}
Expression RationalNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
Expression RationalNode::denominator(ReductionContext reductionContext) const {
return Rational(this).denominator();
}

View File

@@ -311,7 +311,7 @@ QUIZ_CASE(poincare_preperties_get_variables) {
void assert_reduced_expression_has_polynomial_coefficient(const char * expression, const char * symbolName, const char ** coefficients, Preferences::ComplexFormat complexFormat = Cartesian, Preferences::AngleUnit angleUnit = Radian) {
Shared::GlobalContext globalContext;
Expression e = parse_expression(expression, false);
e = e.reduce(&globalContext, complexFormat, angleUnit);
e = e.reduce(&globalContext, complexFormat, angleUnit, SystemForAnalysis);
Expression coefficientBuffer[Poincare::Expression::k_maxNumberOfPolynomialCoefficients];
int d = e.getPolynomialReducedCoefficients(symbolName, coefficientBuffer, &globalContext, complexFormat, Radian);
for (int i = 0; i <= d; i++) {

View File

@@ -54,11 +54,11 @@ Poincare::Expression parse_expression(const char * expression, bool addParenthes
return result;
}
void assert_simplify(const char * expression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) {
void assert_simplify(const char * expression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, ExpressionNode::ReductionTarget target) {
Shared::GlobalContext globalContext;
Expression e = parse_expression(expression, false);
quiz_assert_print_if_failure(!e.isUninitialized(), expression);
e = e.simplify(&globalContext, complexFormat, angleUnit);
e = e.simplify(&globalContext, complexFormat, angleUnit, target);
quiz_assert_print_if_failure(!(e.isUninitialized()), expression);
}
@@ -68,7 +68,7 @@ void assert_parsed_expression_simplify_to(const char * expression, const char *
if (target == ExpressionNode::ReductionTarget::User) {
copy.simplifyAndApproximate(&copy, nullptr, context, complexFormat, angleUnit, symbolicComputation);
} else {
copy = copy.simplify(context, complexFormat, angleUnit, symbolicComputation);
copy = copy.simplify(context, complexFormat, angleUnit, target, symbolicComputation);
}
if (copy.isUninitialized()) {
return e;
@@ -81,7 +81,7 @@ template<typename T>
void assert_expression_approximates_to(const char * expression, const char * approximation, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) {
int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits;
numberOfDigits = numberOfSignificantDigits > 0 ? numberOfSignificantDigits : numberOfDigits;
assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::System, complexFormat, angleUnit, false, [](Expression e, Context * context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::SystemForApproximation, complexFormat, angleUnit, false, [](Expression e, Context * context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
return e.approximate<T>(context, complexFormat, angleUnit);
}, numberOfDigits);
}

View File

@@ -5,7 +5,8 @@ const char * MaxIntegerString(); // (2^32)^k_maxNumberOfDigits-1
const char * OverflowedIntegerString(); // (2^32)^k_maxNumberOfDigits
const char * BigOverflowedIntegerString(); // OverflowedIntegerString with a 2 on first digit
constexpr Poincare::ExpressionNode::ReductionTarget System = Poincare::ExpressionNode::ReductionTarget::System;
constexpr Poincare::ExpressionNode::ReductionTarget SystemForApproximation = Poincare::ExpressionNode::ReductionTarget::SystemForApproximation;
constexpr Poincare::ExpressionNode::ReductionTarget SystemForAnalysis = Poincare::ExpressionNode::ReductionTarget::SystemForAnalysis;
constexpr Poincare::ExpressionNode::ReductionTarget User = Poincare::ExpressionNode::ReductionTarget::User;
constexpr Poincare::Preferences::AngleUnit Degree = Poincare::Preferences::AngleUnit::Degree;
constexpr Poincare::Preferences::AngleUnit Radian = Poincare::Preferences::AngleUnit::Radian;
@@ -30,7 +31,7 @@ Poincare::Expression parse_expression(const char * expression, bool addParenthes
// Simplification
void assert_simplify(const char * expression, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian);
void assert_simplify(const char * expression, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, Poincare::ExpressionNode::ReductionTarget target = User);
void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::ExpressionNode::ReductionTarget target = User, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, bool symbolicComputation = true);

View File

@@ -108,6 +108,9 @@ QUIZ_CASE(assert_print_floats) {
assert_float_prints_to(10000000000000000000000000000.0, "1ᴇ28", DecimalMode, 14);
assert_float_prints_to(10000000000000000000000000000.0, "10ᴇ27", EngineeringMode, 14);
// This used to crash on web platform
assert_float_prints_to(1000.0, "1000", DecimalMode, 7);
assert_float_prints_to(1000000.0f, "1ᴇ6", ScientificMode, 7);
assert_float_prints_to(1000000.0f, "1000000", DecimalMode, 7);
assert_float_prints_to(1000000.0f, "1ᴇ6", EngineeringMode, 7);

View File

@@ -941,6 +941,7 @@ QUIZ_CASE(poincare_simplification_complex_format) {
assert_parsed_expression_simplify_to("[[1,√(-1)]]", "[[1,^\u0012π/2×𝐢\u0013]]", User, Radian, Polar);
assert_parsed_expression_simplify_to("atan(2)", "atan(2)", User, Radian, Polar);
assert_parsed_expression_simplify_to("atan(-2)", "atan(2)×^\u0012π×𝐢\u0013", User, Radian, Polar);
assert_parsed_expression_simplify_to("cos(42π)", "-cos(42×π)×^\x12π×𝐢\x13", User, Degree, Polar);
// User defined variable
assert_parsed_expression_simplify_to("a", "a", User, Radian, Polar);
@@ -959,36 +960,54 @@ QUIZ_CASE(poincare_simplification_complex_format) {
}
QUIZ_CASE(poincare_simplification_reduction_target) {
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", System);
// Factorize on the same denominator only for ReductionTarget = User
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", SystemForAnalysis);
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", SystemForApproximation);
assert_parsed_expression_simplify_to("1/π+1/x", "\u0012x+π\u0013/\u0012π×x\u0013", User);
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", System);
// Display in the form a+ib only for ReductionTarget = User
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", SystemForAnalysis);
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", SystemForApproximation);
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/2-1/2×𝐢", User);
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", System);
// Replace sin/cos-->tan for ReductionTarget = User
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", SystemForAnalysis);
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", SystemForApproximation);
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "tan(x)/cos(x)", User);
assert_parsed_expression_simplify_to("x^0", "x^0", System);
// Apply rule x^0 --> 1 for ReductionTarget = User (because this is not always true)
assert_parsed_expression_simplify_to("x^0", "x^0", SystemForAnalysis);
assert_parsed_expression_simplify_to("x^0", "x^0", SystemForApproximation);
assert_parsed_expression_simplify_to("x^0", "1", User);
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", SystemForApproximation);
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User);
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", System);
// Apply rule x^(2/3) --> root(x,3)^2 for ReductionTarget = System
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForApproximation);
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForAnalysis);
assert_parsed_expression_simplify_to("x^(2/3)", "x^\u00122/3\u0013", User);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System);
assert_parsed_expression_simplify_to("x^2", "x^2", System);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForApproximation);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForAnalysis);
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", User);
assert_parsed_expression_simplify_to("x^2", "x^2", SystemForApproximation);
assert_parsed_expression_simplify_to("x^2", "x^2", User);
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "1/\u0012√(3)+√(2)\u0013", System);
// Remove square root at denominator for ReductionTarget = User
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "1/\u0012√(3)+√(2)\u0013", SystemForApproximation);
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "√(3)-√(2)", User);
assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", System);
// Always reduce sign for ReductionTarget = User
assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", SystemForApproximation);
assert_parsed_expression_simplify_to("sign(abs(x))", "1", User);
assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", System);
// Apply rule atan(1/x)-> (π×sign(x)-2×atan(x))/2 for ReductionTarget = User (as it is not always true)
assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", SystemForApproximation);
assert_parsed_expression_simplify_to("atan(1/x)", "\u0012π×sign(x)-2×atan(x)\u0013/2", User);
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", System);
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User);
// Expand multinome when ReductionTarget is not SystemForApproximation as it increases precision loss
assert_parsed_expression_simplify_to("(2+x)^2", "(x+2)^2", SystemForApproximation);
assert_parsed_expression_simplify_to("(2+x)^2", "x^2+4×x+4", SystemForAnalysis);
assert_parsed_expression_simplify_to("(2+x)^2", "x^2+4×x+4", User);
}
QUIZ_CASE(poincare_simplification_mix) {