From 51cdf68b8cc2e36629fc1a817d09271fcbc183bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Wed, 8 Nov 2017 14:25:12 +0100 Subject: [PATCH] [poincare] Clean Multiplication::shallowReduce/shallowBeautify Change-Id: Ib1fa7ea7f7ffa911b5499b149d67608d9887ce37 --- poincare/include/poincare/expression.h | 3 - poincare/include/poincare/multiplication.h | 1 - poincare/include/poincare/power.h | 2 +- poincare/src/multiplication.cpp | 289 +++++++++------------ poincare/src/power.cpp | 81 ++++-- 5 files changed, 178 insertions(+), 198 deletions(-) diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 02290f64c..6d2f86f06 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -207,9 +207,6 @@ private: virtual Expression * cloneDenominator(Context & context, AngleUnit angleUnit) const { return nullptr; } - virtual Expression * resolveSquareRootAtDenominator(Context & context, AngleUnit angleUnit) { - return this; - } static bool TermIsARationalSquareRootOrRational(const Expression * e); static const Rational * RadicandInExpression(const Expression * e); static const Rational * RationalFactorInExpression(const Expression * e); diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index 56bb05b88..567abfe5c 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -32,7 +32,6 @@ private: int writeTextInBuffer(char * buffer, int bufferSize) const override; /* Simplification */ Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; - Expression * resolveSquareRootAtDenominator(Context & context, AngleUnit angleUnit) override; void factorizeBase(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit); void factorizeExponent(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit); Expression * distributeOnOperandAtIndex(int index, Context & context, AngleUnit angleUnit); diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index da4877895..41de8c4aa 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -38,7 +38,7 @@ private: Expression * cloneDenominator(Context & context, AngleUnit angleUnit) const override; Expression * simplifyPowerMultiplication(Multiplication * m, Expression * r, Context & context, AngleUnit angleUnit); Expression * simplifyRationalRationalPower(Expression * result, Rational * a, Rational * b, Context & context, AngleUnit angleUnit); - Expression * resolveSquareRootAtDenominator(Context & context, AngleUnit angleUnit) override; + Expression * removeSquareRootsFromDenominator(Context & context, AngleUnit angleUnit); static Expression * CreateSimplifiedIntegerRationalPower(Integer i, Rational * r, bool isDenominator); /* Evaluation */ template static Evaluation * computeOnComplexAndMatrix(const Complex * c, Evaluation * n); diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 56cca9920..d19d3ed0e 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -106,7 +106,7 @@ bool Multiplication::HaveSameNonRationalFactors(const Expression * e1, const Exp Expression * Multiplication::shallowReduce(Context& context, AngleUnit angleUnit) { /* Step 1: Multiplication is associative, so let's start by merging children - * which also are additions themselves. */ + * which also are multiplications themselves. */ int i = 0; int initialNumberOfOperands = numberOfOperands(); while (i < initialNumberOfOperands) { @@ -117,36 +117,50 @@ Expression * Multiplication::shallowReduce(Context& context, AngleUnit angleUnit } i++; } + /* Step 2: If any of the operand is zero, the multiplication result is zero */ for (int i = 0; i < numberOfOperands(); i++) { - Expression * o = editableOperand(i); + const Expression * o = operand(i); if (o->type() == Type::Rational && static_cast(o)->isZero()) { return replaceWith(new Rational(0), true); } } + // Step 3: Sort the operands sortOperands(SimplificationOrder); - /* Step 4: Factorize like terms before expanding multiplication of addition. - * This step enables to reduce expressions like (x+y)^(-1)*(x+y)(a+b) for - * example. Thanks to the simplification order, those are next to each other - * at this point. */ + /* Step 4: Gather like terms. For example, turn pi^2*pi^3 into pi^5. Thanks to + * the simplification order, such terms are guaranteed to be next to each + * other. */ i = 0; while (i < numberOfOperands()-1) { - if (operand(i)->type() == Type::Rational && operand(i+1)->type() == Type::Rational) { - Rational a = Rational::Multiplication(*(static_cast(operand(i))), *(static_cast(operand(i+1)))); - replaceOperand(operand(i), new Rational(a), true); - removeOperand(operand(i+1), true); + Expression * oi = editableOperand(i); + Expression * oi1 = editableOperand(i+1); + if (oi->type() == Type::Rational && oi1->type() == Type::Rational) { + Rational a = Rational::Multiplication(*(static_cast(oi)), *(static_cast(oi1))); + replaceOperand(oi, new Rational(a), true); + removeOperand(oi1, true); continue; - } else if (TermsHaveIdenticalBase(operand(i), operand(i+1)) && (!TermHasRationalBase(operand(i)) || (!TermHasIntegerExponent(operand(i)) && !TermHasIntegerExponent(operand(i+1))))) { - factorizeBase(editableOperand(i), editableOperand(i+1), context, angleUnit); - continue; - } else if (TermsHaveIdenticalExponent(operand(i), operand(i+1)) && TermHasRationalBase(operand(i)) && TermHasRationalBase(operand(i+1))) { - factorizeExponent(editableOperand(i), editableOperand(i+1), context, angleUnit); + } else if (TermsHaveIdenticalBase(oi, oi1)) { + bool shouldFactorizeBase = true; + if (TermHasRationalBase(oi)) { + /* Combining powers of a given rational isn't straightforward. Indeed, + * there are two cases we want to deal with: + * - 2*2^(1/2) or 2*2^pi, we want to keep as-is + * - 2^(1/2)*2^(3/2) we want to combine. */ + shouldFactorizeBase = !TermHasIntegerExponent(oi) && !TermHasIntegerExponent(oi1); + } + if (shouldFactorizeBase) { + factorizeBase(oi, oi1, context, angleUnit); + continue; + } + } else if (TermHasRationalBase(oi) && TermHasRationalBase(oi1) && TermsHaveIdenticalExponent(oi, oi1)) { + factorizeExponent(oi, oi1, context, angleUnit); continue; } i++; } + /* Step 5: Let's remove ones if there's any. It's important to do this after * having factorized because factorization can lead to new ones. For example * pi^(-1)*pi. We don't remove the last one if it's the only operand left @@ -160,9 +174,13 @@ Expression * Multiplication::shallowReduce(Context& context, AngleUnit angleUnit } i++; } - /* Step 6: we distribute multiplication on addition node. We do not want to - * do this step if the parent is a multiplication to avoid missing - * factorization like (x+y)^(-1)*((a+b)*(x+y) */ + + /* Step 6: Expand multiplication over addition operands if any. For example, + * turn (a+b)*c into a*c + b*c. We do not want to do this step right now if + * the parent is a multiplication to avoid missing factorization such as + * (x+y)^(-1)*((a+b)*(x+y)). + * Note: This step must be done after Step 4, otherwise we wouldn't be able to + * reduce expressions such as (x+y)^(-1)*(x+y)(a+b). */ if (parent()->type() != Type::Multiplication) { for (int i=0; itype() == Type::Addition) { @@ -170,101 +188,48 @@ Expression * Multiplication::shallowReduce(Context& context, AngleUnit angleUnit } } } - /* Step 7: Let's remove the multiplication altogether if it has a single - * operand. */ + + // Step 7: Let's remove the multiplication altogether if it has one operand Expression * result = squashUnaryHierarchy(); - /* Step 8: We look for square root and sum of square root (two terms maximum - * so far) to move them at numerator. */ - if (true) { - result = result->resolveSquareRootAtDenominator(context, angleUnit); - } + return result; } -Expression * Multiplication::resolveSquareRootAtDenominator(Context & context, AngleUnit angleUnit) { - for (int index = 0; index < numberOfOperands(); index++) { - Expression * o = editableOperand(index); - if (o->type() == Type::Power && o->operand(0)->type() == Type::Rational && o->operand(1)->type() == Type::Rational && static_cast(o->operand(1))->isMinusHalf()) { - Integer p = static_cast(o->operand(0))->numerator(); - Integer q = static_cast(o->operand(0))->denominator(); - Power * sqrt = new Power(new Rational(Integer::Multiplication(p, q)), new Rational(1, 2), false); - replaceOperand(o, new Rational(Integer(1), p), true); - Expression * newExpression = shallowReduce(context, angleUnit); - if (newExpression->type() == Type::Multiplication) { - static_cast(newExpression)->addOperand(sqrt); - } else { - newExpression = newExpression->replaceWith(new Multiplication(newExpression->clone(), sqrt, false), true); - } - sqrt->shallowReduce(context, angleUnit); - return newExpression; - } else if (o->type() == Type::Power && o->operand(1)->type() == Type::Rational && static_cast(o->operand(1))->isMinusOne() && o->operand(0)->type() == Type::Addition && o->operand(0)->numberOfOperands() == 2 && TermIsARationalSquareRootOrRational(o->operand(0)->operand(0)) && TermIsARationalSquareRootOrRational(o->operand(0)->operand(1))) { - const Rational * f1 = RationalFactorInExpression(o->operand(0)->operand(0)); - const Rational * f2 = RationalFactorInExpression(o->operand(0)->operand(1)); - const Rational * r1 = RadicandInExpression(o->operand(0)->operand(0)); - const Rational * r2 = RadicandInExpression(o->operand(0)->operand(1)); - Integer n1 = f1 != nullptr ? f1->numerator() : Integer(1); - Integer d1 = f1 != nullptr ? f1->denominator() : Integer(1); - Integer p1 = r1 != nullptr ? r1->numerator() : Integer(1); - Integer q1 = r1 != nullptr ? r1->denominator() : Integer(1); - Integer n2 = f2 != nullptr ? f2->numerator() : Integer(1); - Integer d2 = f2 != nullptr ? f2->denominator() : Integer(1); - Integer p2 = r2 != nullptr ? r2->numerator() : Integer(1); - Integer q2 = r2 != nullptr ? r2->denominator() : Integer(1); - // Compute n1^2*d2^2*p1*q2-n2^2*d1^2*p2*q1 - Integer denominator = Integer::Subtraction( - Integer::Multiplication( - Integer::Multiplication( - Integer::Power(n1, Integer(2)), - Integer::Power(d2, Integer(2))), - Integer::Multiplication(p1, q2)), - Integer::Multiplication( - Integer::Multiplication( - Integer::Power(n2, Integer(2)), - Integer::Power(d1, Integer(2))), - Integer::Multiplication(p2, q1))); - Power * sqrt1 = new Power(new Rational(Integer::Multiplication(p1, q1)), new Rational(1, 2), false); - Power * sqrt2 = new Power(new Rational(Integer::Multiplication(p2, q2)), new Rational(1, 2), false); - Integer factor1 = Integer::Multiplication( - Integer::Multiplication(n1, d1), - Integer::Multiplication(Integer::Power(d2, Integer(2)), q2)); - Multiplication * m1 = new Multiplication(new Rational(factor1), sqrt1, false); - Integer factor2 = Integer::Multiplication( - Integer::Multiplication(n2, d2), - Integer::Multiplication(Integer::Power(d1, Integer(2)), q1)); - Multiplication * m2 = new Multiplication(new Rational(factor2), sqrt2, false); - const Expression * subOperands[2] = {m1, m2}; - if (denominator.isNegative()) { - denominator.setNegative(false); - const Expression * temp = subOperands[0]; - subOperands[0] = subOperands[1]; - subOperands[1] = temp; - } - Subtraction * s = new Subtraction(subOperands, false); - replaceOperand(o, s, true); - s->deepReduce(context, angleUnit); - addOperand(new Rational(Integer(1), denominator)); - return shallowReduce(context, angleUnit); - } - } - return this; -} - void Multiplication::factorizeBase(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit) { + /* This function factorizes two operands which have a common base. For example + * if this is Multiplication(pi^2, pi^3), then pi^2 and pi^3 could be merged + * and this turned into Multiplication(pi^5). */ + assert(e1->parent() == this && e2->parent() == this); + assert(TermsHaveIdenticalBase(e1, e2)); + + // Step 1: Find the new exponent Expression * s = new Addition(CreateExponent(e1), CreateExponent(e2), false); + + // Step 2: Get rid of one of the operands removeOperand(e2, true); + + // Step 3: Use the new exponent + Power * p = nullptr; if (e1->type() == Type::Power) { + // If e1 is a power, replace the initial exponent with the new one e1->replaceOperand(e1->operand(1), s, true); - s->shallowReduce(context, angleUnit); - e1->shallowReduce(context, angleUnit); + p = static_cast(e1); } else { - Power * p = new Power(e1, s, false); - s->shallowReduce(context, angleUnit); + // Otherwise, create a new Power node + p = new Power(e1, s, false); replaceOperand(e1, p, false); - p->shallowReduce(context, angleUnit); } + + // Step 4: Reduce the new power + s->shallowReduce(context, angleUnit); // pi^2*pi^3 -> pi^(2+3) -> pi^5 + p->shallowReduce(context, angleUnit); // pi^2*pi^-2 -> pi^0 -> 1 } void Multiplication::factorizeExponent(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit) { + /* This function factorizes operands which share a common exponent. For + * example, it turns Multiplication(2^x,3^x) into Multiplication(6^x). */ + assert(e1->parent() == this && e2->parent() == this); + const Expression * base1 = e1->operand(0)->clone(); const Expression * base2 = e2->operand(0); // TODO: remove cast, everything is a hierarchy @@ -272,32 +237,39 @@ void Multiplication::factorizeExponent(Expression * e1, Expression * e2, Context Expression * m = new Multiplication(base1, base2, false); removeOperand(e2, true); e1->replaceOperand(e1->operand(0), m, true); - m->shallowReduce(context, angleUnit); - e1->shallowReduce(context, angleUnit); + + m->shallowReduce(context, angleUnit); // 2^x*3^x -> (2*3)^x -> 6^x + e1->shallowReduce(context, angleUnit); // 2^x*(1/2)^x -> (2*1/2)^x -> 1 } Expression * Multiplication::distributeOnOperandAtIndex(int i, Context & context, AngleUnit angleUnit) { + // This function turns a*(b+c) into a*b + a*c + // We avoid deleting and creating a new addition Addition * a = static_cast(editableOperand(i)); for (int j = 0; j < a->numberOfOperands(); j++) { Expression * termJ = a->editableOperand(j); replaceOperand(operand(i), termJ->clone(), false); Expression * m = clone(); a->replaceOperand(termJ, m, true); - m->shallowReduce(context, angleUnit); + m->shallowReduce(context, angleUnit); // pi^(-1)*(pi + x) -> pi^(-1)*pi + pi^(-1)*x -> 1 + pi^(-1)*x } replaceWith(a, true); - return a->shallowReduce(context, angleUnit); + return a->shallowReduce(context, angleUnit); // Order terms, put under a common denominator if needed } const Expression * Multiplication::CreateExponent(Expression * e) { - Expression * n = e->type() == Type::Power ? e->operand(1)->clone() : new Rational(1); - return n; + return e->type() == Type::Power ? e->operand(1)->clone() : new Rational(1); +} + +static inline const Expression * Base(const Expression * e) { + if (e->type() == Expression::Type::Power) { + return e->operand(0); + } + return e; } bool Multiplication::TermsHaveIdenticalBase(const Expression * e1, const Expression * e2) { - const Expression * f1 = e1->type() == Type::Power ? e1->operand(0) : e1; - const Expression * f2 = e2->type() == Type::Power ? e2->operand(0) : e2; - return f1->isIdenticalTo(f2); + return Base(e1)->isIdenticalTo(Base(e2)); } bool Multiplication::TermsHaveIdenticalExponent(const Expression * e1, const Expression * e2) { @@ -307,8 +279,7 @@ bool Multiplication::TermsHaveIdenticalExponent(const Expression * e1, const Exp } bool Multiplication::TermHasRationalBase(const Expression * e) { - bool hasRationalBase = e->type() == Type::Power ? e->operand(0)->type() == Type::Rational : e->type() == Type::Rational; - return hasRationalBase; + return Base(e)->type() == Type::Rational; } bool Multiplication::TermHasIntegerExponent(const Expression * e) { @@ -317,15 +288,19 @@ bool Multiplication::TermHasIntegerExponent(const Expression * e) { } if (e->operand(1)->type() == Type::Rational) { const Rational * r = static_cast(e->operand(1)); - if (r->denominator().isOne()) { - return true; - } + return r->denominator().isOne(); } return false; } Expression * Multiplication::shallowBeautify(Context & context, AngleUnit angleUnit) { - // -1*A -> -A or (-n)*A -> -n*A + /* Beautifying a Multiplication consists in several possible operations: + * - Add Opposite ((-3)*x -> -(3*x), useful when printing fractions) + * - Adding parenthesis if needed (a*(b+c) is not a*b+c) + * - Creating a Division if there's either a term with a power of -1 (a.b^(-1) + * shall become a/b) or a non-integer rational term (3/2*a -> (3*a)/2). */ + + // Step 1: Turn -n*A into -(n*A) if (operand(0)->type() == Type::Rational && operand(0)->sign() == Sign::Negative) { if (static_cast(operand(0))->isMinusOne()) { removeOperand(editableOperand(0), true); @@ -338,63 +313,47 @@ Expression * Multiplication::shallowBeautify(Context & context, AngleUnit angleU o->editableOperand(0)->shallowBeautify(context, angleUnit); return o; } - // Merge negative power: a*b^-1*c^(-Pi)*d = a*(b*c^Pi)^-1 + + /* 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 * e = mergeNegativePower(context, angleUnit); if (e->type() == Type::Power) { return e->shallowBeautify(context, angleUnit); } assert(e == this); - // Add parenthesis: *(+(a,b), c) -> *((+(a,b)), c - for (int index = 0; index < numberOfOperands(); index++) { - // Add parenthesis to addition - (a+b)*c - if (operand(index)->type() == Type::Addition ) { - const Expression * o[1] = {operand(index)}; - Parenthesis * p = new Parenthesis(o, true); - replaceOperand(operand(index), p, true); - } - } - for (int index = 0; index < numberOfOperands(); index++) { - // a*b^(-1)*... -> a*.../b - if (operand(index)->type() == Type::Power && operand(index)->operand(1)->type() == Type::Rational && static_cast(operand(index)->operand(1))->isMinusOne()) { - Power * p = static_cast(editableOperand(index)); - Expression * denominatorOperand = p->editableOperand(0); - p->detachOperand(denominatorOperand); - removeOperand(p, true); - Expression * numeratorOperand = clone(); - Division * d = new Division(numeratorOperand, denominatorOperand, false); - /* We want 1/3*Pi*(ln(2))^-1 -> Pi/(3ln(2)) and not ((1/3)Pi)/ln(2)*/ - if (numeratorOperand->operand(0)->type() == Type::Rational) { - Rational * r = static_cast(numeratorOperand->editableOperand(0)); - if (!r->denominator().isOne()) { - if (denominatorOperand->type() == Type::Multiplication) { - static_cast(denominatorOperand)->addOperand(new Rational(r->denominator())); - static_cast(denominatorOperand)->sortOperands(SimplificationOrder); - } else { - Multiplication * m = new Multiplication(new Rational(r->denominator()), denominatorOperand->clone(), false); - denominatorOperand->replaceWith(m, true); - } - } - if (!r->numerator().isMinusOne() || numeratorOperand->numberOfOperands() == 1) { - numeratorOperand->replaceOperand(r, new Rational(r->numerator()), true); - numeratorOperand = numeratorOperand->shallowReduce(context, angleUnit); - } else { - ((Multiplication *)numeratorOperand)->removeOperand(r, true); - numeratorOperand = numeratorOperand->shallowReduce(context, angleUnit); - Opposite * o = new Opposite(numeratorOperand, true); - numeratorOperand = numeratorOperand->replaceWith(o, true); - } - } else { - numeratorOperand = numeratorOperand->shallowReduce(context, angleUnit); - } - // Delete parenthesis unnecessary on numerator - if (numeratorOperand->type() == Type::Parenthesis) { - numeratorOperand->replaceWith(numeratorOperand->editableOperand(0), true); - } - replaceWith(d, true); - return d->shallowBeautify(context, angleUnit); + // Step 3: Add Parenthesis if needed + for (int i = 0; i < numberOfOperands(); i++) { + const Expression * o = operand(i); + if (o->type() == Type::Addition ) { + Parenthesis * p = new Parenthesis(o, false); + replaceOperand(o, p, false); } } + + // Step 4: Create a Division if needed + for (int i = 0; i < numberOfOperands(); i++) { + if (!(operand(i)->type() == Type::Power && operand(i)->operand(1)->type() == Type::Rational && static_cast(operand(i)->operand(1))->isMinusOne())) { + continue; + } + + // Let's remove the denominator-to-be from this + Power * p = static_cast(editableOperand(i)); + Expression * denominatorOperand = p->editableOperand(0); + p->detachOperand(denominatorOperand); + removeOperand(p, true); + + Expression * numeratorOperand = shallowReduce(context, angleUnit); + // Delete parenthesis unnecessary on numerator + if (numeratorOperand->type() == Type::Parenthesis) { + numeratorOperand = numeratorOperand->replaceWith(numeratorOperand->editableOperand(0), true); + } + Expression * originalParent = numeratorOperand->parent(); + Division * d = new Division(numeratorOperand, denominatorOperand, false); + originalParent->replaceOperand(numeratorOperand, d, false); + return d->shallowBeautify(context, angleUnit); + } + return this; } @@ -408,10 +367,10 @@ Expression * Multiplication::cloneDenominator(Context & context, AngleUnit angle result = static_cast(e)->cloneDenominator(context, angleUnit); } else { assert(e->type() == Type::Multiplication); - for (int index = 0; index < e->numberOfOperands(); index++) { + for (int i = 0; i < e->numberOfOperands(); i++) { // a*b^(-1)*... -> a*.../b - if (e->operand(index)->type() == Type::Power && e->operand(index)->operand(1)->type() == Type::Rational && static_cast(e->operand(index)->operand(1))->isMinusOne()) { - Power * p = static_cast(e->editableOperand(index)); + if (e->operand(i)->type() == Type::Power && e->operand(i)->operand(1)->type() == Type::Rational && static_cast(e->operand(i)->operand(1))->isMinusOne()) { + Power * p = static_cast(e->editableOperand(i)); result = p->editableOperand(0); p->detachOperand((result)); } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 999e49d3b..2bf4c5435 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -136,6 +136,14 @@ int Power::simplificationOrderGreaterType(const Expression * e) const { } Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { + + /* Step 0: We look for square root and sum of square roots (two terms maximum + * so far) at the denominator and move them to the numerator. */ + Expression * r = removeSquareRootsFromDenominator(context, angleUnit); + if (r) { + return r; + } + if (operand(1)->type() == Type::Rational) { const Rational * b = static_cast(operand(1)); // x^0 @@ -219,9 +227,6 @@ Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { return m->shallowReduce(context, angleUnit); } } - if (true) { - return resolveSquareRootAtDenominator(context, angleUnit); - } return this; } @@ -359,28 +364,42 @@ Expression * Power::cloneDenominator(Context & context, AngleUnit angleUnit) con return nullptr; } -Expression * Power::resolveSquareRootAtDenominator(Context & context, AngleUnit angleUnit) { +Expression * Power::removeSquareRootsFromDenominator(Context & context, AngleUnit angleUnit) { + Expression * result = nullptr; + if (operand(0)->type() == Type::Rational && operand(1)->type() == Type::Rational && static_cast(operand(1))->isMinusHalf()) { - Integer p = static_cast(operand(0))->numerator(); - Integer q = static_cast(operand(0))->denominator(); - Power * sqrt = new Power(new Rational(Integer::Multiplication(p, q)), new Rational(1, 2), false); - Expression * newExpression = new Multiplication(new Rational(Integer(1), p), sqrt, false); - sqrt->shallowReduce(context, angleUnit); - return replaceWith(newExpression, true); + /* We're considering a term of the form 1/sqrt(p/q), with p and q integers. + * We'll turn those into sqrt(p*q)/p. */ + Integer p = static_cast(operand(0))->numerator(); + Integer q = static_cast(operand(0))->denominator(); + Power * sqrt = new Power(new Rational(Integer::Multiplication(p, q)), new Rational(1, 2), false); + result = new Multiplication(new Rational(Integer(1), p), sqrt, false); + sqrt->shallowReduce(context, angleUnit); } else if (operand(1)->type() == Type::Rational && static_cast(operand(1))->isMinusOne() && operand(0)->type() == Type::Addition && operand(0)->numberOfOperands() == 2 && TermIsARationalSquareRootOrRational(operand(0)->operand(0)) && TermIsARationalSquareRootOrRational(operand(0)->operand(1))) { + /* We're considering a term of the form + * + * 1/(n1/d1*sqrt(p1/q1) + n2/d2*sqrt(p2/q2)) + * + * and we want to turn it into + * + * n1*q2*d1*d2^2*sqrt(p1*q1) - n2*q1*d2*d1^2*sqrt(p2*q2) + * ------------------------------------------------------- + * n1^2*d2^2*p1*q2 - n2^2*d1^2*p2*q1 + */ const Rational * f1 = RationalFactorInExpression(operand(0)->operand(0)); const Rational * f2 = RationalFactorInExpression(operand(0)->operand(1)); const Rational * r1 = RadicandInExpression(operand(0)->operand(0)); const Rational * r2 = RadicandInExpression(operand(0)->operand(1)); - Integer n1 = f1 != nullptr ? f1->numerator() : Integer(1); - Integer d1 = f1 != nullptr ? f1->denominator() : Integer(1); - Integer p1 = r1 != nullptr ? r1->numerator() : Integer(1); - Integer q1 = r1 != nullptr ? r1->denominator() : Integer(1); - Integer n2 = f2 != nullptr ? f2->numerator() : Integer(1); - Integer d2 = f2 != nullptr ? f2->denominator() : Integer(1); - Integer p2 = r2 != nullptr ? r2->numerator() : Integer(1); - Integer q2 = r2 != nullptr ? r2->denominator() : Integer(1); - // Compute n1^2*d2^2*p1*q2-n2^2*d1^2*p2*q1 + Integer n1 = (f1 ? f1->numerator() : Integer(1)); + Integer d1 = (f1 ? f1->denominator() : Integer(1)); + Integer p1 = (r1 ? r1->numerator() : Integer(1)); + Integer q1 = (r1 ? r1->denominator() : Integer(1)); + Integer n2 = (f2 ? f2->numerator() : Integer(1)); + Integer d2 = (f2 ? f2->denominator() : Integer(1)); + Integer p2 = (r2 ? r2->numerator() : Integer(1)); + Integer q2 = (r2 ? r2->denominator() : Integer(1)); + + // Compute the denominator = n1^2*d2^2*p1*q2 - n2^2*d1^2*p2*q1 Integer denominator = Integer::Subtraction( Integer::Multiplication( Integer::Multiplication( @@ -392,6 +411,8 @@ Expression * Power::resolveSquareRootAtDenominator(Context & context, AngleUnit Integer::Power(n2, Integer(2)), Integer::Power(d1, Integer(2))), Integer::Multiplication(p2, q1))); + + // Compute the numerator Power * sqrt1 = new Power(new Rational(Integer::Multiplication(p1, q1)), new Rational(1, 2), false); Power * sqrt2 = new Power(new Rational(Integer::Multiplication(p2, q2)), new Rational(1, 2), false); Integer factor1 = Integer::Multiplication( @@ -402,19 +423,23 @@ Expression * Power::resolveSquareRootAtDenominator(Context & context, AngleUnit Integer::Multiplication(n2, d2), Integer::Multiplication(Integer::Power(d1, Integer(2)), q1)); Multiplication * m2 = new Multiplication(new Rational(factor2), sqrt2, false); - const Expression * subOperands[2] = {m1, m2}; + Subtraction * numerator = nullptr; if (denominator.isNegative()) { + numerator = new Subtraction(m2, m1, false); denominator.setNegative(false); - const Expression * temp = subOperands[0]; - subOperands[0] = subOperands[1]; - subOperands[1] = temp; + } else { + numerator = new Subtraction(m1, m2, false); } - Subtraction * s = new Subtraction(subOperands, false); - Expression * newExpression = new Multiplication(s, new Rational(Integer(1), denominator), false); - s->deepReduce(context, angleUnit); - return replaceWith(newExpression, true)->shallowReduce(context, angleUnit); + + result = new Multiplication(numerator, new Rational(Integer(1), denominator), false); + numerator->deepReduce(context, angleUnit); } - return this; + + if (result) { + replaceWith(result, true); + result = result->shallowReduce(context, angleUnit); + } + return result; } }