diff --git a/apps/regression/model/model.cpp b/apps/regression/model/model.cpp index ca91effd3..d949b1571 100644 --- a/apps/regression/model/model.cpp +++ b/apps/regression/model/model.cpp @@ -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; diff --git a/apps/shared/expression_model.cpp b/apps/shared/expression_model.cpp index 015526c11..dcdb5d347 100644 --- a/apps/shared/expression_model.cpp +++ b/apps/shared/expression_model.cpp @@ -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)); diff --git a/apps/shared/poincare_helpers.h b/apps/shared/poincare_helpers.h index b8d73f340..0f2ba050e 100644 --- a/apps/shared/poincare_helpers.h +++ b/apps/shared/poincare_helpers.h @@ -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) { diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index d083bb533..ea3112b53 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -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(); } diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index df49a70a8..b1abfac91 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -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); diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index 70a653acb..96d8ebe96 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -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 { diff --git a/poincare/src/equal.cpp b/poincare/src/equal.cpp index 611aa6b86..c51d3fb56 100644 --- a/poincare/src/equal.cpp +++ b/poincare/src/equal.cpp @@ -46,7 +46,11 @@ Evaluation 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() { diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index cce1c46aa..2b95ece7c 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -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) { diff --git a/poincare/src/matrix.cpp b/poincare/src/matrix.cpp index 6a206b345..757981199 100644 --- a/poincare/src/matrix.cpp +++ b/poincare/src/matrix.cpp @@ -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(); - 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; diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 7130d720d..6897375ff 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -744,11 +744,11 @@ 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) - * We apply this rule only when the target is the User. Indeed, developing - * the multinome is likely to increase the numbers of operations and to - * lead to precision loss. */ + * 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::User + && reductionContext.target() != ExpressionNode::ReductionTarget::SystemForApproximation && indexType == ExpressionNode::Type::Rational && !static_cast(index).signedIntegerNumerator().isZero() && static_cast(index).isInteger() @@ -871,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, + * 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().signedIntegerNumerator(); Integer q = childAtIndex(1).convert().integerDenominator(); Expression nthRoot = q.isOne() ? childAtIndex(0) : NthRoot::Builder(childAtIndex(0), Rational::Builder(q)); diff --git a/poincare/test/expression_properties.cpp b/poincare/test/expression_properties.cpp index d138e8b6a..e2f7697e9 100644 --- a/poincare/test/expression_properties.cpp +++ b/poincare/test/expression_properties.cpp @@ -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++) { diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index a35e01306..d79d8b27d 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -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(©, 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 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(context, complexFormat, angleUnit); }, numberOfDigits); } diff --git a/poincare/test/helper.h b/poincare/test/helper.h index ea742d44b..7a110e016 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -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); diff --git a/poincare/test/simplification.cpp b/poincare/test/simplification.cpp index 2699ddc57..9379274d4 100644 --- a/poincare/test/simplification.cpp +++ b/poincare/test/simplification.cpp @@ -959,36 +959,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) {