[poincare] Exact undef results with GCD and LCM

Change-Id: Iba39cd6140d8ef5b3fd24e0f53c9e28fbd57d438
This commit is contained in:
Hugo Saint-Vignes
2020-09-28 16:30:37 +02:00
committed by Émilie Feral
parent a89878a24d
commit 073893ae48
7 changed files with 27 additions and 13 deletions

View File

@@ -42,7 +42,7 @@ public:
static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("gcd", -2, &UntypedBuilderMultipleChildren<GreatCommonDivisor>);
// Expression
Expression shallowReduce(Context * context);
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
Expression shallowBeautify(Context * context);
};

View File

@@ -42,7 +42,7 @@ public:
static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("lcm", -2, &UntypedBuilderMultipleChildren<LeastCommonMultiple>);
// Expression
Expression shallowReduce(Context * context);
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
Expression shallowBeautify(Context * context);
};

View File

@@ -55,7 +55,7 @@ protected:
node()->sortChildrenInPlace(order, context, canSwapMatrices, canBeInterrupted);
}
NAryExpressionNode * node() const { return static_cast<NAryExpressionNode *>(Expression::node()); }
Expression checkChildrenAreRationalIntegers(Context * context);
Expression checkChildrenAreRationalIntegersAndUpdate(ExpressionNode::ReductionContext reductionContext);
};
}

View File

@@ -289,12 +289,13 @@ bool Expression::hasDefinedComplexApproximation(Context * context, Preferences::
}
/* We return true when both real and imaginary approximation are defined and
* imaginary part is not null. */
Expression imag = ImaginaryPart::Builder(*this);
Expression e = clone();
Expression imag = ImaginaryPart::Builder(e);
float b = imag.approximateToScalar<float>(context, complexFormat, angleUnit);
if (b == 0.0f || std::isinf(b) || std::isnan(b)) {
return false;
}
Expression real = RealPart::Builder(*this);
Expression real = RealPart::Builder(e);
float a = real.approximateToScalar<float>(context, complexFormat, angleUnit);
if (std::isinf(a) || std::isnan(a)) {
return false;

View File

@@ -16,7 +16,7 @@ int GreatCommonDivisorNode::serialize(char * buffer, int bufferSize, Preferences
}
Expression GreatCommonDivisorNode::shallowReduce(ReductionContext reductionContext) {
return GreatCommonDivisor(this).shallowReduce(reductionContext.context());
return GreatCommonDivisor(this).shallowReduce(reductionContext);
}
Expression GreatCommonDivisorNode::shallowBeautify(ReductionContext reductionContext) {
@@ -36,7 +36,7 @@ Expression GreatCommonDivisor::shallowBeautify(Context * context) {
return *this;
}
Expression GreatCommonDivisor::shallowReduce(Context * context) {
Expression GreatCommonDivisor::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
{
Expression e = Expression::defaultShallowReduce();
e = e.defaultHandleUnitsInChildren();
@@ -51,7 +51,7 @@ Expression GreatCommonDivisor::shallowReduce(Context * context) {
// Step 1: check that all children are compatible
{
Expression checkChildren = checkChildrenAreRationalIntegers(context);
Expression checkChildren = checkChildrenAreRationalIntegersAndUpdate(reductionContext);
if (!checkChildren.isUninitialized()) {
return checkChildren;
}

View File

@@ -16,7 +16,7 @@ int LeastCommonMultipleNode::serialize(char * buffer, int bufferSize, Preference
}
Expression LeastCommonMultipleNode::shallowReduce(ReductionContext reductionContext) {
return LeastCommonMultiple(this).shallowReduce(reductionContext.context());
return LeastCommonMultiple(this).shallowReduce(reductionContext);
}
Expression LeastCommonMultipleNode::shallowBeautify(ReductionContext reductionContext) {
@@ -36,7 +36,7 @@ Expression LeastCommonMultiple::shallowBeautify(Context * context) {
return *this;
}
Expression LeastCommonMultiple::shallowReduce(Context * context) {
Expression LeastCommonMultiple::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
{
Expression e = Expression::defaultShallowReduce();
e = e.defaultHandleUnitsInChildren();
@@ -51,7 +51,7 @@ Expression LeastCommonMultiple::shallowReduce(Context * context) {
// Step 1: check that all children are compatible
{
Expression checkChildren = checkChildrenAreRationalIntegers(context);
Expression checkChildren = checkChildrenAreRationalIntegersAndUpdate(reductionContext);
if (!checkChildren.isUninitialized()) {
return checkChildren;
}

View File

@@ -74,13 +74,26 @@ int NAryExpression::allChildrenAreReal(Context * context) const {
return result;
}
Expression NAryExpression::checkChildrenAreRationalIntegers(Context * context) {
Expression NAryExpression::checkChildrenAreRationalIntegersAndUpdate(ExpressionNode::ReductionContext reductionContext) {
for (int i = 0; i < numberOfChildren(); ++i) {
Expression c = childAtIndex(i);
if (c.deepIsMatrix(context)) {
if (c.deepIsMatrix(reductionContext.context())) {
return replaceWithUndefinedInPlace();
}
if (c.type() != ExpressionNode::Type::Rational) {
/* Replace expression with undefined if child can be approximated to a
* complex or finite non-integer number. Otherwise, rely on template
* approximations. hasDefinedComplexApproximation is given Cartesian
* complex format to force imaginary part approximation. */
if (!c.isReal(reductionContext.context()) && c.hasDefinedComplexApproximation(reductionContext.context(), Preferences::ComplexFormat::Cartesian, reductionContext.angleUnit())) {
return replaceWithUndefinedInPlace();
}
// If c was complex but with a null imaginary part, real part is checked.
float app = c.approximateToScalar<float>(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
if (std::isfinite(app) && app != std::round(app)) {
return replaceWithUndefinedInPlace();
}
// Note : Child could be replaced with the approximation (if finite) here.
return *this;
}
if (!static_cast<Rational &>(c).isInteger()) {