Files
Upsilon/poincare/src/complex_cartesian.cpp
Hugo Saint-Vignes 73b5b0775a [poincare] Avoid nullptr context on non-Numbers sign and nullStatus methods
Change-Id: I3361344c1e0b50cdbe999d6ea50025022a51f603
2020-11-16 17:12:42 +01:00

399 lines
17 KiB
C++

#include <poincare/complex_cartesian.h>
#include <poincare/addition.h>
#include <poincare/arc_tangent.h>
#include <poincare/binomial_coefficient.h>
#include <poincare/cosine.h>
#include <poincare/constant.h>
#include <poincare/division.h>
#include <poincare/naperian_logarithm.h>
#include <poincare/rational.h>
#include <poincare/square_root.h>
#include <poincare/sine.h>
#include <poincare/sign_function.h>
#include <poincare/subtraction.h>
#include <poincare/power.h>
#include <poincare/undefined.h>
#include <assert.h>
#include <cmath>
#include <utility>
namespace Poincare {
ExpressionNode::NullStatus ComplexCartesianNode::nullStatus(Context * context) const {
ExpressionNode::NullStatus realNullStatus = childAtIndex(0)->nullStatus(context);
ExpressionNode::NullStatus imagNullStatus = childAtIndex(1)->nullStatus(context);
if (realNullStatus == ExpressionNode::NullStatus::NonNull || imagNullStatus == ExpressionNode::NullStatus::NonNull) {
return ExpressionNode::NullStatus::NonNull;
}
return (realNullStatus == ExpressionNode::NullStatus::Null && imagNullStatus == ExpressionNode::NullStatus::Null) ? ExpressionNode::NullStatus::Null : ExpressionNode::NullStatus::Unknown;
}
Expression ComplexCartesianNode::shallowReduce(ReductionContext reductionContext) {
return ComplexCartesian(this).shallowReduce(reductionContext);
}
Expression ComplexCartesianNode::shallowBeautify(ReductionContext * reductionContext) {
return ComplexCartesian(this).shallowBeautify(reductionContext);
}
template<typename T>
Complex<T> ComplexCartesianNode::templatedApproximate(ApproximationContext approximationContext) const {
Evaluation<T> realEvaluation = childAtIndex(0)->approximate(T(), approximationContext);
Evaluation<T> imagEvalution = childAtIndex(1)->approximate(T(), approximationContext);
assert(realEvaluation.type() == EvaluationNode<T>::Type::Complex);
assert(imagEvalution.type() == EvaluationNode<T>::Type::Complex);
std::complex<T> a = static_cast<Complex<T> &>(realEvaluation).stdComplex();
std::complex<T> b = static_cast<Complex<T> &>(imagEvalution).stdComplex();
if ((a.imag() != (T)0.0 && !std::isnan(a.imag())) || (b.imag() != (T)0.0 && !std::isnan(b.imag()))) {
/* a and b are supposed to be real (if they are not undefined). However,
* due to double precision limit, the approximation of the real part or the
* imaginary part can lead to complex values.
* For instance, let the real part be
* sqrt(2*sqrt(5E23+1)-1E12*sqrt(2)) ~ 1.1892E-6. Due to std::sqrt(2.0)
* unprecision, 2*sqrt(5E23+1)-1E12*sqrt(2) < 0 which leads to
* sqrt(2*sqrt(5E23+1)-1E12*sqrt(2)) being a complex number.
* In this case, we return an undefined complex because the approximation
* is very likely to be false. */
return Complex<T>::Undefined();
}
assert(a.imag() == 0.0 || std::isnan(a.imag()));
assert(b.imag() == 0.0 || std::isnan(b.imag()));
return Complex<T>::Builder(a.real(), b.real());
}
Expression ComplexCartesian::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
{
Expression e = Expression::defaultShallowReduce();
e = e.defaultHandleUnitsInChildren();
if (e.isUndefined()) {
return e;
}
}
if (imag().nullStatus(reductionContext.context()) == ExpressionNode::NullStatus::Null) {
Expression r = real();
replaceWithInPlace(r);
return r;
}
return *this;
}
Expression ComplexCartesian::shallowBeautify(ExpressionNode::ReductionContext * reductionContext) {
Expression a = real();
Expression b = imag();
Expression oppositeA = a.makePositiveAnyNegativeNumeralFactor(*reductionContext);
Expression oppositeB = b.makePositiveAnyNegativeNumeralFactor(*reductionContext);
a = oppositeA.isUninitialized() ? a : oppositeA;
b = oppositeB.isUninitialized() ? b : oppositeB;
Expression e = Expression::CreateComplexExpression(a, b, Preferences::ComplexFormat::Cartesian,
a.isUndefined() || b.isUndefined(),
Expression::IsZero(a), Expression::IsOne(a), Expression::IsZero(b), Expression::IsOne(b),
!oppositeA.isUninitialized(),
!oppositeB.isUninitialized()
);
replaceWithInPlace(e);
return e;
}
void ComplexCartesian::factorAndArgumentOfFunction(Expression e, ExpressionNode::Type searchedType, Expression * factor, Expression * argument, ExpressionNode::ReductionContext reductionContext) {
if (e.type() == searchedType) {
*factor = Rational::Builder(1);
*argument = e.childAtIndex(0);
return;
}
if (e.type() == ExpressionNode::Type::Multiplication) {
for (int i = 0; i < e.numberOfChildren(); i++) {
if (e.childAtIndex(i).type() == searchedType) {
*argument = e.childAtIndex(i).childAtIndex(0);
*factor = e.clone();
static_cast<Multiplication *>(factor)->removeChildAtIndexInPlace(i);
*factor = factor->shallowReduce(reductionContext);
Expression positiveFactor = factor->makePositiveAnyNegativeNumeralFactor(reductionContext);
*factor = positiveFactor.isUninitialized() ? *factor : positiveFactor;
return;
}
}
}
}
Expression ComplexCartesian::squareNorm(ExpressionNode::ReductionContext reductionContext) {
Expression a = real();
Expression b = imag();
Expression aFactor, bFactor, aArgument, bArgument;
factorAndArgumentOfFunction(a, ExpressionNode::Type::Cosine, &aFactor, &aArgument, reductionContext);
factorAndArgumentOfFunction(b, ExpressionNode::Type::Sine, &bFactor, &bArgument, reductionContext);
if (!aFactor.isUninitialized() && !aArgument.isUninitialized() && !bFactor.isUninitialized() && !bArgument.isUninitialized() && aFactor.isIdenticalTo(bFactor) && aArgument.isIdenticalTo(bArgument)) {
Power result = Power::Builder(aFactor, Rational::Builder(2));
aFactor.shallowReduce(reductionContext);
return std::move(result);
}
Expression a2 = Power::Builder(a, Rational::Builder(2));
Expression b2 = Power::Builder(b, Rational::Builder(2));
Addition add = Addition::Builder(a2, b2);
a2.shallowReduce(reductionContext);
b2.shallowReduce(reductionContext);
return std::move(add);
}
Expression ComplexCartesian::norm(ExpressionNode::ReductionContext reductionContext) {
Expression a;
// Special case for pure real or pure imaginary cartesian
if (imag().nullStatus(reductionContext.context()) == ExpressionNode::NullStatus::Null) {
a = real();
} else if (real().nullStatus(reductionContext.context()) == ExpressionNode::NullStatus::Null) {
a = imag();
}
if (!a.isUninitialized()) {
// norm = sign(a) * a
Expression signa = SignFunction::Builder(a.clone());
Expression norm = Multiplication::Builder(a, signa);
signa.shallowReduce(reductionContext);
return norm;
}
Expression n2 = squareNorm(reductionContext);
Expression n = SquareRoot::Builder(n2);
n2.shallowReduce(reductionContext);
return n;
}
Expression ComplexCartesian::argument(ExpressionNode::ReductionContext reductionContext) {
Expression a = real();
Expression b = imag();
if (b.nullStatus(reductionContext.context()) != ExpressionNode::NullStatus::Null) {
// TODO: Handle ExpressionNode::NullStatus::Unknown
// if b != 0, argument = sign(b) * π/2 - atan(a/b)
// First, compute atan(a/b) or (π/180)*atan(a/b)
Expression divab = Division::Builder(a, b.clone());
Expression arcTangent = ArcTangent::Builder(divab);
divab.shallowReduce(reductionContext);
if (reductionContext.angleUnit() != Preferences::AngleUnit::Radian) {
Expression temp = arcTangent.angleUnitToRadian(reductionContext.angleUnit());
arcTangent.shallowReduce(reductionContext);
arcTangent = temp;
}
// Then, compute sign(b) * π/2 - atan(a/b)
Expression signb = SignFunction::Builder(b);
Expression signbPi2 = Multiplication::Builder(Rational::Builder(1,2), signb, Constant::Builder(UCodePointGreekSmallLetterPi));
signb.shallowReduce(reductionContext);
Expression sub = Subtraction::Builder(signbPi2, arcTangent);
signbPi2.shallowReduce(reductionContext);
arcTangent.shallowReduce(reductionContext);
return sub;
} else {
// if b == 0, argument = (1-sign(a))*π/2
Expression signa = SignFunction::Builder(a).shallowReduce(reductionContext);
Subtraction sub = Subtraction::Builder(Rational::Builder(1), signa);
signa.shallowReduce(reductionContext);
Multiplication mul = Multiplication::Builder(Rational::Builder(1,2), Constant::Builder(UCodePointGreekSmallLetterPi), sub);
sub.shallowReduce(reductionContext);
return std::move(mul);
}
}
ComplexCartesian ComplexCartesian::inverse(ExpressionNode::ReductionContext reductionContext) {
Expression a = real();
Expression b = imag();
// 1/(a+ib) = a/(a^2+b^2)+i*(-b/(a^2+b^2))
Expression denominatorReal = clone().convert<ComplexCartesian>().squareNorm(reductionContext);
Expression denominatorImag = denominatorReal.clone();
Expression denominatorRealInv = Power::Builder(denominatorReal, Rational::Builder(-1));
denominatorReal.shallowReduce(reductionContext);
Expression denominatorImagInv = Power::Builder(denominatorImag, Rational::Builder(-1));
denominatorImag.shallowReduce(reductionContext);
Multiplication A = Multiplication::Builder(a, denominatorRealInv);
denominatorRealInv.shallowReduce(reductionContext);
Expression numeratorImag = Multiplication::Builder(Rational::Builder(-1), b);
Multiplication B = Multiplication::Builder(numeratorImag, denominatorImagInv);
numeratorImag.shallowReduce(reductionContext);
denominatorImagInv.shallowReduce(reductionContext);
ComplexCartesian result = ComplexCartesian::Builder(A,B);
A.shallowReduce(reductionContext);
B.shallowReduce(reductionContext);
return result.interruptComputationIfManyNodes();
}
Multiplication ComplexCartesian::squareRootHelper(Expression e, ExpressionNode::ReductionContext reductionContext) {
//(1/2)*sqrt(2*e)
Multiplication doubleE = Multiplication::Builder(Rational::Builder(2), e);
e.shallowReduce(reductionContext);
Expression sqrt = SquareRoot::Builder(doubleE);
doubleE.shallowReduce(reductionContext);
Multiplication result = Multiplication::Builder(Rational::Builder(1,2), sqrt);
sqrt.shallowReduce(reductionContext);
return result;
}
ComplexCartesian ComplexCartesian::squareRoot(ExpressionNode::ReductionContext reductionContext) {
Expression a = real();
Expression b = imag();
// A: (1/2)*sqrt(2*(sqrt(a^2+b^2)+a))
// B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))*sign(b)
Expression normA = clone().convert<ComplexCartesian>().norm(reductionContext);
Expression normB = normA.clone();
// A = (1/2)*sqrt(2*(sqrt(a^2+b^2)+a))
Addition normAdda = Addition::Builder(normA, a.clone());
normA.shallowReduce(reductionContext);
Multiplication A = squareRootHelper(normAdda, reductionContext);
// B = B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))
Subtraction normSuba = Subtraction::Builder(normB, a);
normB.shallowReduce(reductionContext);
Multiplication B = squareRootHelper(normSuba, reductionContext);
// B = B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))*sign(b)
Expression signb = SignFunction::Builder(b);
B.addChildAtIndexInPlace(signb, B.numberOfChildren(), B.numberOfChildren());
signb.shallowReduce(reductionContext);
ComplexCartesian result = ComplexCartesian::Builder(A, B);
A.shallowReduce(reductionContext);
B.shallowReduce(reductionContext);
return result.interruptComputationIfManyNodes();
}
ComplexCartesian ComplexCartesian::powerInteger(int n, ExpressionNode::ReductionContext reductionContext) {
Expression a = real();
Expression b = imag();
assert(n > 0);
assert(b.nullStatus(reductionContext.context()) != ExpressionNode::NullStatus::Null);
// Special case: a == 0 (otherwise, we are going to introduce undefined expressions - a^0 = NAN)
// (b*i)^n = b^n*i^n with i^n == i, -i, 1 or -1
if (a.nullStatus(reductionContext.context()) == ExpressionNode::NullStatus::Null) {
ComplexCartesian result;
Expression bpow = Power::Builder(b, Rational::Builder(n));
if (n/2%2 == 1) {
Expression temp = Multiplication::Builder(Rational::Builder(-1), bpow);
bpow.shallowReduce(reductionContext);
bpow = temp;
}
if (n%2 == 0) {
result = ComplexCartesian::Builder(bpow, Rational::Builder(0));
} else {
result = ComplexCartesian::Builder(Rational::Builder(0), bpow);
}
bpow.shallowReduce(reductionContext);
return result;
}
// (a+ib) = a^n+i*b*a^(n-1)+(-1)*b^2*a^(n-2)+(-i)*b^3*a^(n-3)+b^3*a^(n-4)+...
// Real part: A = a^n+(-1)*b^2*a^(n-2)+...
// Imaginary part: B = b*a^(n-1)
Addition A = Addition::Builder();
Addition B = Addition::Builder();
ComplexCartesian result = ComplexCartesian::Builder(A, B);
for (int i = 0; i <= n; i++) {
BinomialCoefficient binom = BinomialCoefficient::Builder(Rational::Builder(n), Rational::Builder(i));
Expression aclone = i == n ? a : a.clone();
Expression bclone = i == n ? b : b.clone();
Power apow = Power::Builder(aclone, Rational::Builder(n-i));
Power bpow = Power::Builder(bclone, Rational::Builder(i));
Multiplication m = Multiplication::Builder(binom, apow, bpow);
binom.shallowReduce(reductionContext.context());
apow.shallowReduce(reductionContext);
bpow.shallowReduce(reductionContext);
if (i/2%2 == 1) {
m.addChildAtIndexInPlace(Rational::Builder(-1), 0, m.numberOfChildren());
}
if (i%2 == 0) {
A.addChildAtIndexInPlace(m, A.numberOfChildren(), A.numberOfChildren());
} else {
B.addChildAtIndexInPlace(m, B.numberOfChildren(), B.numberOfChildren());
}
m.shallowReduce(reductionContext);
result = result.interruptComputationIfManyNodes();
if (result.real().isUndefined()) {
return result;
}
}
A.shallowReduce(reductionContext);
B.shallowReduce(reductionContext);
return result;
}
ComplexCartesian ComplexCartesian::multiply(ComplexCartesian & other, ExpressionNode::ReductionContext reductionContext) {
Expression a = real();
Expression b = imag();
Expression c = other.real();
Expression d = other.imag();
// (a+ib) * (c+id) = (ac-bd)+i*(ad+bc)
// Compute ac-bd
Expression ac = Multiplication::Builder(a.clone(), c.clone());
Expression bd = Multiplication::Builder(b.clone(), d.clone());
Subtraction A = Subtraction::Builder(ac, bd);
ac.shallowReduce(reductionContext);
bd.shallowReduce(reductionContext);
// Compute ad+bc
Expression ad = Multiplication::Builder(a, d);
Expression bc = Multiplication::Builder(b, c);
Addition B = Addition::Builder(ad, bc);
ad.shallowReduce(reductionContext);
bc.shallowReduce(reductionContext);
ComplexCartesian result = ComplexCartesian::Builder(A, B);
A.shallowReduce(reductionContext);
B.shallowReduce(reductionContext);
return result.interruptComputationIfManyNodes();
}
Expression ComplexCartesian::powerHelper(Expression norm, Expression trigo, ExpressionNode::ReductionContext reductionContext) {
Multiplication m = Multiplication::Builder(norm, trigo);
norm.shallowReduce(reductionContext);
trigo.shallowReduce(reductionContext);
return std::move(m);
}
ComplexCartesian ComplexCartesian::power(ComplexCartesian & other, ExpressionNode::ReductionContext reductionContext) {
Expression r = clone().convert<ComplexCartesian>().norm(reductionContext);
Expression rclone = r.clone();
Expression th = argument(reductionContext);
Expression thclone = th.clone();
Expression c = other.real();
Expression d = other.imag();
// R = r^c*e^(-th*d)
Expression rpowc = Power::Builder(rclone, c.clone());
rclone.shallowReduce(reductionContext);
Expression thmuld = Multiplication::Builder(Rational::Builder(-1), thclone, d.clone());
thclone.shallowReduce(reductionContext);
Expression exp = Power::Builder(Constant::Builder(UCodePointScriptSmallE), thmuld);
thmuld.shallowReduce(reductionContext);
Multiplication norm = Multiplication::Builder(rpowc, exp);
rpowc.shallowReduce(reductionContext);
exp.shallowReduce(reductionContext);
// TH = d*ln(r)+c*th
Expression lnr = NaperianLogarithm::Builder(r);
r.shallowReduce(reductionContext);
Multiplication dlnr = Multiplication::Builder(d, lnr);
lnr.shallowReduce(reductionContext);
Multiplication thc = Multiplication::Builder(th, c);
th.shallowReduce(reductionContext);
Expression argument = Addition::Builder(thc, dlnr);
thc.shallowReduce(reductionContext);
dlnr.shallowReduce(reductionContext);
if (reductionContext.angleUnit() != Preferences::AngleUnit::Radian) {
Expression temp = argument.radianToAngleUnit(reductionContext.angleUnit());
argument.shallowReduce(reductionContext);
argument = temp;
}
// Result = (norm*cos(argument), norm*sin(argument))
Expression normClone = norm.clone();
Expression argClone = argument.clone();
Expression cos = Cosine::Builder(argClone);
argClone.shallowReduce(reductionContext);
Expression normcosarg = powerHelper(normClone, cos, reductionContext);
Expression sin = Sine::Builder(argument);
argument.shallowReduce(reductionContext);
Expression normsinarg = powerHelper(norm, sin, reductionContext);
ComplexCartesian result = ComplexCartesian::Builder(normcosarg, normsinarg);
normcosarg.shallowReduce(reductionContext);
normsinarg.shallowReduce(reductionContext);
return result.interruptComputationIfManyNodes();
}
ComplexCartesian ComplexCartesian::interruptComputationIfManyNodes() {
if (numberOfDescendants(true) > k_maxNumberOfNodesBeforeInterrupting) {
Expression::SetInterruption(true);
return ComplexCartesian::Builder(Undefined::Builder(), Undefined::Builder());
}
return *this;
}
}