[poincare] Rational: make a method 'isInteger'

This commit is contained in:
Émilie Feral
2019-07-26 16:53:01 +02:00
parent c08d7a4733
commit d985872951
20 changed files with 52 additions and 45 deletions

View File

@@ -16,6 +16,7 @@ public:
Integer denominator() const;
bool isNegative() const { return m_negative; }
void setNegative(bool negative) { m_negative = negative; }
bool isInteger() const { return denominator().isOne(); }
// TreeNode
size_t size() const override;
@@ -43,11 +44,11 @@ public:
// Basic test
bool isZero() const { return unsignedNumerator().isZero(); }
bool isOne() const { return signedNumerator().isOne() && denominator().isOne(); }
bool isMinusOne() const { return signedNumerator().isMinusOne() && denominator().isOne(); }
bool isOne() const { return signedNumerator().isOne() && isInteger(); }
bool isMinusOne() const { return signedNumerator().isMinusOne() && isInteger(); }
bool isHalf() const { return signedNumerator().isOne() && denominator().isTwo(); }
bool isMinusHalf() const { return signedNumerator().isMinusOne() && denominator().isTwo(); }
bool isTen() const { return signedNumerator().isTen() && denominator().isOne(); }
bool isTen() const { return signedNumerator().isTen() && isInteger(); }
static int NaturalOrder(const RationalNode * i, const RationalNode * j);
private:
@@ -92,6 +93,7 @@ public:
bool isMinusHalf() const { return node()->isMinusHalf(); }
bool isTen() const { return node()->isTen(); }
bool numeratorOrDenominatorIsInfinity() const;
bool isInteger() const { return node()->isInteger(); }
// Arithmetic
/* Warning: when using this function, always assert that the result does not

View File

@@ -70,13 +70,13 @@ Expression BinomialCoefficient::shallowReduce(Context * context) {
if (c0.type() == ExpressionNode::Type::Rational) {
Rational r0 = static_cast<Rational&>(c0);
if (!r0.integerDenominator().isOne() || r0.isNegative()) {
if (!r0.isInteger() || r0.isNegative()) {
return replaceWithUndefinedInPlace();
}
}
if (c1.type() == ExpressionNode::Type::Rational) {
Rational r1 = static_cast<Rational&>(c1);
if (!r1.integerDenominator().isOne() || r1.isNegative()) {
if (!r1.isInteger() || r1.isNegative()) {
return replaceWithUndefinedInPlace();
}
}

View File

@@ -72,7 +72,7 @@ Expression ConfidenceInterval::shallowReduce(ExpressionNode::ReductionContext re
}
if (c1.type() == ExpressionNode::Type::Rational) {
Rational r1 = static_cast<Rational&>(c1);
if (!r1.integerDenominator().isOne() || r1.signedIntegerNumerator().isNegative()) {
if (!r1.isInteger() || r1.signedIntegerNumerator().isNegative()) {
return replaceWithUndefinedInPlace();
}
}

View File

@@ -32,7 +32,7 @@ bool DivisionNode::childNeedsSystemParenthesesAtSerialization(const TreeNode * c
if (static_cast<const ExpressionNode *>(child)->isNumber() && Number(static_cast<const NumberNode *>(child)).sign() == Sign::Negative) {
return true;
}
if (static_cast<const ExpressionNode *>(child)->type() == Type::Rational && !static_cast<const RationalNode *>(child)->denominator().isOne()) {
if (static_cast<const ExpressionNode *>(child)->type() == Type::Rational && !static_cast<const RationalNode *>(child)->isInteger()) {
return true;
}
Type types[] = {Type::Subtraction, Type::Opposite, Type::MultiplicationExplicite, Type::Division, Type::Addition};

View File

@@ -50,13 +50,13 @@ Expression DivisionQuotient::shallowReduce(Context * context) {
}
if (c0.type() == ExpressionNode::Type::Rational) {
Rational r0 = static_cast<Rational &>(c0);
if (!r0.integerDenominator().isOne()) {
if (!r0.isInteger()) {
return replaceWithUndefinedInPlace();
}
}
if (c1.type() == ExpressionNode::Type::Rational) {
Rational r1 = static_cast<Rational &>(c1);
if (!r1.integerDenominator().isOne()) {
if (!r1.isInteger()) {
return replaceWithUndefinedInPlace();
}
}

View File

@@ -51,13 +51,13 @@ Expression DivisionRemainder::shallowReduce(Context * context) {
}
if (c0.type() == ExpressionNode::Type::Rational) {
Rational r0 = static_cast<Rational &>(c0);
if (!r0.integerDenominator().isOne()) {
if (!r0.isInteger()) {
return replaceWithUndefinedInPlace();
}
}
if (c1.type() == ExpressionNode::Type::Rational) {
Rational r1 = static_cast<Rational &>(c1);
if (!r1.integerDenominator().isOne()) {
if (!r1.isInteger()) {
return replaceWithUndefinedInPlace();
}
}

View File

@@ -87,7 +87,7 @@ Expression Factor::shallowBeautify(ExpressionNode::ReductionContext reductionCon
return replaceWithUndefinedInPlace();
}
Expression result = numeratorDecomp.squashUnaryHierarchyInPlace();
if (!r.integerDenominator().isOne()) {
if (!r.isInteger()) {
MultiplicationExplicite denominatorDecomp = createMultiplicationOfIntegerPrimeDecomposition(r.integerDenominator(), reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
if (denominatorDecomp.numberOfChildren() == 0) {
return replaceWithUndefinedInPlace();

View File

@@ -35,7 +35,7 @@ bool FactorialNode::childNeedsSystemParenthesesAtSerialization(const TreeNode *
* --- ! ---> [2/3]!
* 3
*/
if (static_cast<const ExpressionNode *>(child)->type() == Type::Rational && !static_cast<const RationalNode *>(child)->denominator().isOne()) {
if (static_cast<const ExpressionNode *>(child)->type() == Type::Rational && !static_cast<const RationalNode *>(child)->isInteger()) {
return true;
}
Type types[] = {Type::Division, Type::Power};
@@ -100,7 +100,7 @@ Expression Factorial::shallowReduce(ExpressionNode::ReductionContext reductionCo
}
if (c.type() == ExpressionNode::Type::Rational) {
Rational r = c.convert<Rational>();
if (!r.integerDenominator().isOne() || r.sign() == ExpressionNode::Sign::Negative) {
if (!r.isInteger() || r.sign() == ExpressionNode::Sign::Negative) {
return replaceWithUndefinedInPlace();
}
if (Integer(k_maxOperandValue).isLowerThan(r.unsignedIntegerNumerator())) {

View File

@@ -64,13 +64,13 @@ Expression GreatCommonDivisor::shallowReduce(Context * context) {
}
if (c0.type() == ExpressionNode::Type::Rational) {
Rational r0 = static_cast<Rational &>(c0);
if (!r0.integerDenominator().isOne()) {
if (!r0.isInteger()) {
return replaceWithUndefinedInPlace();
}
}
if (c1.type() == ExpressionNode::Type::Rational) {
Rational r1 = static_cast<Rational&>(c1);
if (!r1.integerDenominator().isOne()) {
if (!r1.isInteger()) {
return replaceWithUndefinedInPlace();
}
}

View File

@@ -68,13 +68,13 @@ Expression LeastCommonMultiple::shallowReduce(Context * context) {
}
if (c0.type() == ExpressionNode::Type::Rational) {
Rational r0 = static_cast<Rational &>(c0);
if (!r0.integerDenominator().isOne()) {
if (!r0.isInteger()) {
return replaceWithUndefinedInPlace();
}
}
if (c1.type() == ExpressionNode::Type::Rational) {
Rational r1 = static_cast<Rational &>(c1);
if (!r1.integerDenominator().isOne()) {
if (!r1.isInteger()) {
return replaceWithUndefinedInPlace();
}
}

View File

@@ -196,7 +196,7 @@ Expression Logarithm::shallowReduce(ExpressionNode::ReductionContext reductionCo
Rational r = static_cast<Rational &>(c);
Addition a = Addition::Builder();
// if the log base is Integer: log_b(r) = c + log_b(r') with r = b^c*r'
if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert<Rational>().integerDenominator().isOne()) {
if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert<Rational>().isInteger()) {
Integer b = childAtIndex(1).convert<Rational>().signedIntegerNumerator();
Integer newNumerator = simplifyLogarithmIntegerBaseInteger(r.signedIntegerNumerator(), b, a, false);
Integer newDenomitor = simplifyLogarithmIntegerBaseInteger(r.integerDenominator(), b, a, true);

View File

@@ -537,8 +537,8 @@ void MultiplicationExplicite::addMissingFactors(Expression factor, Context * con
* child, we replace it by its LCM with factor ; otherwise, we simply add
* factor as a child. */
if (numberOfChildren() > 0 && childAtIndex(0).type() == ExpressionNode::Type::Rational && factor.type() == ExpressionNode::Type::Rational) {
assert(static_cast<Rational &>(factor).integerDenominator().isOne());
assert(childAtIndex(0).convert<Rational>().integerDenominator().isOne());
assert(static_cast<Rational &>(factor).isInteger());
assert(childAtIndex(0).convert<Rational>().isInteger());
Integer lcm = Arithmetic::LCM(static_cast<Rational &>(factor).unsignedIntegerNumerator(), childAtIndex(0).convert<Rational>().unsignedIntegerNumerator());
if (lcm.isOverflow()) {
addChildAtIndexInPlace(Rational::Builder(static_cast<Rational &>(factor).unsignedIntegerNumerator()), 1, numberOfChildren());
@@ -669,7 +669,7 @@ Expression MultiplicationExplicite::mergeNegativePower(Context * context, Prefer
* for instance, a^(-1)*b^(-c)*c = c*(a*b^c)^(-1) */
MultiplicationExplicite m = MultiplicationExplicite::Builder();
// Special case for rational p/q: if q != 1, q should be at denominator
if (childAtIndex(0).type() == ExpressionNode::Type::Rational && !childAtIndex(0).convert<Rational>().integerDenominator().isOne()) {
if (childAtIndex(0).type() == ExpressionNode::Type::Rational && !childAtIndex(0).convert<Rational>().isInteger()) {
Rational r = childAtIndex(0).convert<Rational>();
m.addChildAtIndexInPlace(Rational::Builder(r.integerDenominator()), 0, m.numberOfChildren());
if (r.signedIntegerNumerator().isOne()) {

View File

@@ -26,7 +26,7 @@ bool MultiplicationImpliciteNode::childNeedsSystemParenthesesAtSerialization(con
* ---i --> [2/3]i
* 3
*/
if (static_cast<const ExpressionNode *>(child)->type() == Type::Rational && !static_cast<const RationalNode *>(child)->denominator().isOne()) {
if (static_cast<const ExpressionNode *>(child)->type() == Type::Rational && !static_cast<const RationalNode *>(child)->isInteger()) {
return true;
}
// 2^{3}i --> [2^3]i

View File

@@ -110,7 +110,7 @@ Number Number::Power(const Number & i, const Number & j) {
return BinaryOperation(i, j,
// Special case for Rational^Rational: we escape to Float if the index is not an Integer
[](const Rational & i, const Rational & j) {
if (!j.integerDenominator().isOne()) {
if (!j.isInteger()) {
// We return an overflown result to reach the escape case Float+Float
return Rational::Builder(Integer::Overflow(false));
}

View File

@@ -64,13 +64,13 @@ Expression PermuteCoefficient::shallowReduce(Context * context) {
}
if (c0.type() == ExpressionNode::Type::Rational) {
Rational r0 = static_cast<Rational &>(c0);
if (!r0.integerDenominator().isOne() || r0.sign() == ExpressionNode::Sign::Negative) {
if (!r0.isInteger() || r0.sign() == ExpressionNode::Sign::Negative) {
return replaceWithUndefinedInPlace();
}
}
if (c1.type() == ExpressionNode::Type::Rational) {
Rational r1 = static_cast<Rational &>(c1);
if (!r1.integerDenominator().isOne() || r1.sign() == ExpressionNode::Sign::Negative) {
if (!r1.isInteger() || r1.sign() == ExpressionNode::Sign::Negative) {
return replaceWithUndefinedInPlace();
}
}

View File

@@ -39,7 +39,7 @@ ExpressionNode::Sign PowerNode::sign(Context * context) const {
}
if (childAtIndex(0)->sign(context) == Sign::Negative && childAtIndex(1)->type() == ExpressionNode::Type::Rational) {
RationalNode * r = static_cast<RationalNode *>(childAtIndex(1));
if (r->denominator().isOne()) {
if (r->isInteger()) {
assert(!Integer::Division(r->signedNumerator(), Integer(2)).remainder.isOverflow());
if (Integer::Division(r->signedNumerator(), Integer(2)).remainder.isZero()) {
return Sign::Positive;
@@ -67,7 +67,7 @@ int PowerNode::polynomialDegree(Context * context, const char * symbolName) cons
}
if (childAtIndex(1)->type() == ExpressionNode::Type::Rational) {
RationalNode * r = static_cast<RationalNode *>(childAtIndex(1));
if (!r->denominator().isOne() || Number(r).sign() == Sign::Negative) {
if (!r->isInteger() || Number(r).sign() == Sign::Negative) {
return -1;
}
Integer numeratorInt = r->signedNumerator();
@@ -93,7 +93,7 @@ bool PowerNode::isReal(Context * context) const {
if (base->isReal(context) &&
index->isReal(context) &&
(base->sign(context) == Sign::Positive ||
(index->type() == ExpressionNode::Type::Rational && static_cast<RationalNode *>(index)->denominator().isOne()))) {
(index->type() == ExpressionNode::Type::Rational && static_cast<RationalNode *>(index)->isInteger()))) {
return true;
}
return false;
@@ -105,7 +105,7 @@ bool PowerNode::childNeedsUserParentheses(const Expression & child) const {
* ^(2/3, 4) --> (2/3)^{4}
*/
if ((child.isNumber() && static_cast<const Number &>(child).sign() == Sign::Negative)
|| (child.type() == Type::Rational && !static_cast<const Rational &>(child).integerDenominator().isOne())) {
|| (child.type() == Type::Rational && !static_cast<const Rational &>(child).isInteger())) {
return true;
}
// ^(2+3,4) --> (2+3)^{4}
@@ -166,7 +166,7 @@ bool PowerNode::childNeedsSystemParenthesesAtSerialization(const TreeNode * chil
if (static_cast<const ExpressionNode *>(child)->isNumber() && Number(static_cast<const NumberNode *>(child)).sign() == Sign::Negative) {
return true;
}
if (static_cast<const ExpressionNode *>(child)->type() == Type::Rational && !static_cast<const RationalNode *>(child)->denominator().isOne()) {
if (static_cast<const ExpressionNode *>(child)->type() == Type::Rational && !static_cast<const RationalNode *>(child)->isInteger()) {
return true;
}
Type types[] = {Type::Power, Type::Subtraction, Type::Opposite, Type::MultiplicationExplicite, Type::MultiplicationImplicite, Type::Division, Type::Addition};
@@ -271,7 +271,7 @@ int Power::getPolynomialCoefficients(Context * context, const char * symbolName,
&& childAtIndex(1).type() == ExpressionNode::Type::Rational)
{
Rational r = childAtIndex(1).convert<Rational>();
if (!r.integerDenominator().isOne() || r.sign() == ExpressionNode::Sign::Negative) {
if (!r.isInteger() || r.sign() == ExpressionNode::Sign::Negative) {
return -1;
}
Integer num = r.unsignedIntegerNumerator();
@@ -309,7 +309,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
ExpressionNode::Type baseType = base.type();
ExpressionNode::Type indexType = index.type();
if (SortedIsMatrix(base, reductionContext.context())) {
if (indexType != ExpressionNode::Type::Rational || !static_cast<Rational &>(index).integerDenominator().isOne()) {
if (indexType != ExpressionNode::Type::Rational || !static_cast<Rational &>(index).isInteger()) {
return replaceWithUndefinedInPlace();
}
if (baseType != ExpressionNode::Type::Matrix) {
@@ -449,7 +449,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
} else if (r.isMinusHalf()) {
// (x+iy)^(-1/2)
result = complexBase.squareRoot(reductionContext).inverse(reductionContext);
} else if (r.integerDenominator().isOne() && r.unsignedIntegerNumerator().isLowerThan(Integer(10))) {
} else if (r.isInteger() && r.unsignedIntegerNumerator().isLowerThan(Integer(10))) {
if (r.sign() == ExpressionNode::Sign::Positive) {
// (x+iy)^n, n integer positive n < 10
result = complexBase.powerInteger(r.unsignedIntegerNumerator().extractedInt(), reductionContext);
@@ -627,7 +627,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
Power powerBase = static_cast<Power &>(base);
// Check if a > 0 or c is Integer
if (powerBase.childAtIndex(0).sign(reductionContext.context()) == ExpressionNode::Sign::Positive // a > 0
|| (indexType == ExpressionNode::Type::Rational && static_cast<Rational &>(index).integerDenominator().isOne())) // c integr
|| (indexType == ExpressionNode::Type::Rational && static_cast<Rational &>(index).isInteger())) // c integr
{
/* If the complexFormat is real, we check that the inner power is defined
* before applying the rule (a^b)^c -> a^(b*c). Otherwise, we return
@@ -646,7 +646,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
if (!letPowerAtRoot && baseType == ExpressionNode::Type::MultiplicationExplicite) {
MultiplicationExplicite multiplicationBase = static_cast<MultiplicationExplicite &>(base);
// Case 1: (a*b*c*...)^n = a^n*b^n*c^n*... if n integer
if (indexType == ExpressionNode::Type::Rational && static_cast<Rational &>(index).integerDenominator().isOne()) {
if (indexType == ExpressionNode::Type::Rational && static_cast<Rational &>(index).isInteger()) {
return simplifyPowerMultiplication(reductionContext);
}
// Case 2: (a*b*...)^r -> |a|^r*(sign(a)*b*...)^r if a not -1
@@ -695,7 +695,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
Expression additionIndexChild0 = additionIndex.childAtIndex(0);
if (additionIndexChild0.type() == ExpressionNode::Type::Rational) {
const Rational rationalIndex = static_cast<Rational &>(additionIndexChild0);
if (rationalIndex.unsignedIntegerNumerator().isOne() && !rationalIndex.integerDenominator().isOne()) {
if (rationalIndex.unsignedIntegerNumerator().isOne() && !rationalIndex.isInteger()) {
/* Do not reduce a^(1/q+c+...) to avoid potential infinite loop:
* a^(1/q+c+...) --> a^(1/q)*a^(c+...) --> a^(1/q+c+...)*/
/* TODO: do something more sensible here:
@@ -731,7 +731,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
if (!letPowerAtRoot
&& indexType == ExpressionNode::Type::Rational
&& !static_cast<Rational &>(index).signedIntegerNumerator().isZero()
&& static_cast<Rational &>(index).integerDenominator().isOne()
&& static_cast<Rational &>(index).isInteger()
&& baseType == ExpressionNode::Type::Addition)
{
// Exponent n
@@ -793,7 +793,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
/* We could use the Newton formula instead which is quicker but not immediate
* to implement in the general case (Newton multinome). */
// (a+b)^n with n integer -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Newton)
if (!letPowerAtRoot && childAtIndex(1)->type() == ExpressionNode::Type::Rational && static_cast<const Rational *>(childAtIndex(1))->denominator().isOne() && childAtIndex(0)->type() == ExpressionNode::Type::Addition && childAtIndex(0)->numberOfChildren() == 2) {
if (!letPowerAtRoot && childAtIndex(1)->type() == ExpressionNode::Type::Rational && static_cast<const Rational *>(childAtIndex(1))->isInteger() && childAtIndex(0)->type() == ExpressionNode::Type::Addition && childAtIndex(0)->numberOfChildren() == 2) {
Rational * nr = static_cast<Rational *>(childAtIndex(1));
Integer n = nr->numerator();
n.setNegative(false);
@@ -920,7 +920,7 @@ Expression Power::simplifyRationalRationalPower(ExpressionNode::ReductionContext
// this is a^b with a, b rationals
Rational a = childAtIndex(0).convert<Rational>();
Rational b = childAtIndex(1).convert<Rational>();
if (b.integerDenominator().isOne()) {
if (b.isInteger()) {
Rational r = Rational::IntegerPower(a, b.signedIntegerNumerator());
if (r.numeratorOrDenominatorIsInfinity()) {
return Power::Builder(a, b);

View File

@@ -67,7 +67,7 @@ Expression PredictionInterval::shallowReduce(ExpressionNode::ReductionContext re
}
if (c1.type() == ExpressionNode::Type::Rational) {
Rational r1 = static_cast<Rational &>(c1);
if (!r1.integerDenominator().isOne() || r1.sign() == ExpressionNode::Sign::Negative) {
if (!r1.isInteger() || r1.sign() == ExpressionNode::Sign::Negative) {
return replaceWithUndefinedInPlace();
}
}
@@ -76,7 +76,7 @@ Expression PredictionInterval::shallowReduce(ExpressionNode::ReductionContext re
}
Rational r0 = static_cast<Rational &>(c0);
Rational r1 = static_cast<Rational &>(c1);
if (!r1.integerDenominator().isOne() || r1.sign() == ExpressionNode::Sign::Negative || r0.sign() == ExpressionNode::Sign::Negative || Integer::NaturalOrder(r0.unsignedIntegerNumerator(), r0.integerDenominator()) > 0) {
if (!r1.isInteger() || r1.sign() == ExpressionNode::Sign::Negative || r0.sign() == ExpressionNode::Sign::Negative || Integer::NaturalOrder(r0.unsignedIntegerNumerator(), r0.integerDenominator()) > 0) {
return replaceWithUndefinedInPlace();
}
/* [r0-1.96*sqrt(r0*(1-r0)/r1), r0+1.96*sqrt(r0*(1-r0)/r1)]*/

View File

@@ -72,7 +72,7 @@ int RationalNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo
}
buffer[bufferSize-1] = 0;
int numberOfChar = signedNumerator().serialize(buffer, bufferSize);
if (denominator().isOne()) {
if (isInteger()) {
return numberOfChar;
}
if (numberOfChar >= bufferSize-1) {
@@ -97,7 +97,7 @@ Expression RationalNode::setSign(Sign s, ReductionContext reductionContext) {
Layout RationalNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
Layout numeratorLayout = signedNumerator().createLayout();
if (denominator().isOne()) {
if (isInteger()) {
return numeratorLayout;
}
Layout denominatorLayout = denominator().createLayout();

View File

@@ -56,7 +56,7 @@ Expression Round::shallowReduce(ExpressionNode::ReductionContext reductionContex
if (childAtIndex(0).type() == ExpressionNode::Type::Rational && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
Rational r1 = childAtIndex(0).convert<Rational>();
Rational r2 = childAtIndex(1).convert<Rational>();
if (!r2.integerDenominator().isOne()) {
if (!r2.isInteger()) {
return replaceWithUndefinedInPlace();
}
const Rational ten = Rational::Builder(10);

View File

@@ -60,6 +60,11 @@ QUIZ_CASE(poincare_rational_properties) {
quiz_assert(!Rational::Builder(3,2).isMinusHalf());
quiz_assert(Rational::Builder(10).isTen());
quiz_assert(!Rational::Builder(-1).isTen());
quiz_assert(Rational::Builder(-1).isInteger());
quiz_assert(Rational::Builder(9).isInteger());
quiz_assert(Rational::Builder(9,3).isInteger());
quiz_assert(Rational::Builder(-9,3).isInteger());
quiz_assert(!Rational::Builder(9,10).isInteger());
}
static inline void assert_add_to(const Rational i, const Rational j, const Rational k) {