Files
Upsilon/poincare/include/poincare/expression_node.h
2022-12-05 20:07:45 -08:00

356 lines
12 KiB
C++

#ifndef POINCARE_EXPRESSION_NODE_H
#define POINCARE_EXPRESSION_NODE_H
#include <poincare/tree_node.h>
#include <poincare/evaluation.h>
#include <poincare/layout.h>
#include <poincare/context.h>
#include <stdint.h>
namespace Poincare {
/* Methods preceded by '*!*' interfere with the expression pool, which can make
* 'this' outdated. They should only be called in a wrapper on Expression. */
class SymbolAbstract;
class Symbol;
class ComplexCartesian;
class ExpressionNode : public TreeNode {
friend class AdditionNode;
friend class DivisionNode;
friend class NAryExpressionNode;
friend class NAryInfixExpressionNode;
friend class PowerNode;
friend class SymbolNode;
public:
// The types order is important here.
enum class Type : uint8_t {
Uninitialized = 0,
Undefined = 1,
Unreal,
Rational,
BasedInteger,
Decimal,
Double,
Float,
Infinity,
/* When merging number nodes together, we do a linear scan which stops at
* the first non-number child. */
Multiplication,
Power,
Addition,
Factorial,
Division,
Constant,
Symbol,
Store,
UnitConvert,
Equal,
Sine,
Cosine,
Tangent,
AbsoluteValue,
And,
ArcCosine,
ArcSine,
ArcTangent,
BitClear,
BitFlip,
BitGet,
BitsClear,
BitsClearExplicit,
BitSet,
BinomCDF,
BinomialCoefficient,
BinomPDF,
Ceiling,
ComplexArgument,
Conjugate,
Derivative,
Determinant,
DivisionQuotient,
DivisionRemainder,
Factor,
Floor,
FracPart,
Function,
GreatCommonDivisor,
HyperbolicArcCosine,
HyperbolicArcSine,
HyperbolicArcTangent,
HyperbolicCosine,
HyperbolicSine,
HyperbolicTangent,
ImaginaryPart,
Integral,
InvBinom,
InvNorm,
KMat,
LeastCommonMultiple,
Logarithm,
MatrixTrace,
NaperianLogarithm,
NormCDF,
NormCDF2,
NormPDF,
Not,
NotExplicit,
NthRoot,
Opposite,
Or,
Parenthesis,
PermuteCoefficient,
Product,
Random,
Randint,
RealPart,
RotateLeft,
RotateLeftExplicit,
RotateRight,
RotateRightExplicit,
Round,
Sequence,
ShiftArithmeticRight,
ShiftArithmeticRightExplicit,
ShiftLogicLeft,
ShiftLogicLeftExplicit,
ShiftLogicRight,
ShiftLogicRightExplicit,
SignFunction,
SquareRoot,
Subtraction,
Sum,
TwosComplement,
VectorDot,
VectorNorm,
Xor,
/* When sorting the children of an expression, we assert that the following
* nodes are at the end of the list : */
// - Units
Unit,
// - Complexes
ComplexCartesian,
// - Any kind of matrices :
ConfidenceInterval,
MatrixDimension,
MatrixIdentity,
MatrixInverse,
MatrixTranspose,
MatrixRowEchelonForm,
MatrixReducedRowEchelonForm,
PredictionInterval,
VectorCross,
Matrix,
EmptyExpression
};
/* Poor man's RTTI */
virtual Type type() const = 0;
/* Properties */
enum class ReductionTarget {
/* 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 SymbolicComputation {
ReplaceAllSymbolsWithDefinitionsOrUndefined = 0,
ReplaceAllDefinedSymbolsWithDefinition = 1,
ReplaceDefinedFunctionsWithDefinitions = 2,
ReplaceAllSymbolsWithUndefined = 3, // Used in UnitConvert::shallowReduce
DoNotReplaceAnySymbol = 4
};
enum class UnitConversion {
None = 0,
Default,
InternationalSystem
};
enum class Sign {
Negative = -1,
Unknown = 0,
Positive = 1
};
enum class NullStatus {
Unknown = -1,
NonNull = 0,
Null = 1,
};
enum class IntegerStatus {
Unknown = -1,
Integer = 1,
};
class ComputationContext {
public:
ComputationContext(
Context * context,
Preferences::ComplexFormat complexFormat,
Preferences::AngleUnit angleUnit) :
m_context(context),
m_complexFormat(complexFormat),
m_angleUnit(angleUnit)
{}
Context * context() { return m_context; }
void setContext(Context * context) { m_context = context; }
Preferences::ComplexFormat complexFormat() const { return m_complexFormat; }
Preferences::AngleUnit angleUnit() const { return m_angleUnit; }
private:
Context * m_context;
Preferences::ComplexFormat m_complexFormat;
Preferences::AngleUnit m_angleUnit;
};
class ReductionContext : public ComputationContext {
public:
ReductionContext(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ReductionTarget target, SymbolicComputation symbolicComputation = SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, UnitConversion unitConversion = UnitConversion::Default) :
ComputationContext(context, complexFormat, angleUnit),
m_unitFormat(unitFormat),
m_target(target),
m_symbolicComputation(symbolicComputation),
m_unitConversion(unitConversion)
{}
static ReductionContext NonInvasiveReductionContext(ReductionContext reductionContext) {
return ReductionContext(
reductionContext.context(),
reductionContext.complexFormat(),
reductionContext.angleUnit(),
reductionContext.unitFormat(),
reductionContext.target(),
SymbolicComputation::DoNotReplaceAnySymbol,
UnitConversion::None
);
}
Preferences::UnitFormat unitFormat() const { return m_unitFormat; }
ReductionTarget target() const { return m_target; }
SymbolicComputation symbolicComputation() const { return m_symbolicComputation; }
UnitConversion unitConversion() const { return m_unitConversion; }
private:
Preferences::UnitFormat m_unitFormat;
ReductionTarget m_target;
SymbolicComputation m_symbolicComputation;
UnitConversion m_unitConversion;
};
class ApproximationContext : public ComputationContext {
public:
ApproximationContext(
Context * context,
Preferences::ComplexFormat complexFormat,
Preferences::AngleUnit angleUnit,
bool withinReduce = false) :
ComputationContext(context, complexFormat, angleUnit),
m_withinReduce(withinReduce)
{}
ApproximationContext(ReductionContext reductionContext, bool withinReduce) :
ApproximationContext(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit(), withinReduce) {}
bool withinReduce() const { return m_withinReduce; }
private:
bool m_withinReduce;
};
virtual Sign sign(Context * context) const { return Sign::Unknown; }
virtual NullStatus nullStatus(Context * context) const { return NullStatus::Unknown; }
virtual IntegerStatus integerStatus(Context * context) const { return IntegerStatus::Unknown; }
virtual bool isNumber() const { return false; }
virtual bool isRandom() const { return false; }
virtual bool isParameteredExpression() const { return false; }
/* childAtIndexNeedsUserParentheses checks if parentheses are required by mathematical rules:
* +(2,-1) --> 2+(-1)
* *(+(2,1),3) --> (2+1)*3
*/
virtual bool childAtIndexNeedsUserParentheses(const Expression & child, int childIndex) const { return false; }
/*!*/ virtual Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression);
/*!*/ virtual Expression setSign(Sign s, ReductionContext reductionContext);
virtual int polynomialDegree(Context * context, const char * symbolName) const;
/*!*/ virtual int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const;
/*!*/ virtual Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount);
typedef bool (*isVariableTest)(const char * c, Poincare::Context * context);
virtual int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable, int nextVariableIndex) const;
bool isOfType(Type * types, int length) const;
virtual Expression removeUnit(Expression * unit); // Only reduced nodes should answer
/* Simplification */
/* SimplificationOrder returns:
* 1 if e1 > e2
* -1 if e1 < e2
* 0 if e1 == e
* The order groups like terms together to avoid quadratic complexity when
* factorizing addition or multiplication. For example, it groups terms with
* same bases together (ie Pi, Pi^3) and with same non-rational factors
* together (ie Pi, 2*Pi).
* Because SimplificationOrder is a recursive call, we sometimes enable its
* interruption to avoid freezing in the simplification process. */
static int SimplificationOrder(const ExpressionNode * e1, const ExpressionNode * e2, bool ascending, bool canBeInterrupted = false, bool ignoreParentheses = false);
/* In the simplification order, most expressions are compared by only
* comparing their types. However hierarchical expressions of same type would
* compare their operands and thus need to reimplement
* simplificationOrderSameType. Besides, operations that can be simplified
* (ie +, *, ^, !) have specific rules to group like terms together and thus
* reimplement simplificationOrderGreaterType. */
virtual int simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted, bool ignoreParentheses) const { return ascending ? -1 : 1; }
virtual int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted, bool ignoreParentheses) const;
/* Layout Helper */
virtual Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const = 0;
/* Evaluation Helper */
typedef float SinglePrecision;
typedef double DoublePrecision;
constexpr static int k_maxNumberOfSteps = 10000;
virtual Evaluation<float> approximate(SinglePrecision p, ApproximationContext approximationContext) const = 0;
virtual Evaluation<double> approximate(DoublePrecision p, ApproximationContext approximationContext) const = 0;
/* Simplification */
/*!*/ virtual void deepReduceChildren(ReductionContext reductionContext);
/*!*/ virtual void deepBeautifyChildren(ReductionContext reductionContext);
/*!*/ virtual Expression shallowReduce(ReductionContext reductionContext);
/* TODO: shallowBeautify takes a pointer to the reduction context, unlike
* other methods. The pointer is needed to allow UnitConvert to modify the
* context and prevent unit modifications (in Expression::deepBeautify, after
* calling UnitConvert::shallowBeautify).
* We should uniformize this behaviour and use pointers in other methods using
* the reduction context. */
/*!*/ virtual Expression shallowBeautify(ReductionContext * reductionContext);
/*!*/ virtual bool derivate(ReductionContext, Expression symbol, Expression symbolValue);
virtual Expression unaryFunctionDifferential(ReductionContext reductionContext);
/* Return a clone of the denominator part of the expression */
/*!*/ virtual Expression denominator(ExpressionNode::ReductionContext reductionContext) const;
/* LayoutShape is used to check if the multiplication sign can be omitted between two expressions. It depends on the "layout style" of the on the right of the left expression */
enum class LayoutShape {
Decimal,
Integer,
BinaryHexadecimal,
OneLetter,
MoreLetters,
BoundaryPunctuation, // ( [ ∫
Root,
NthRoot,
Fraction,
RightOfPower
};
virtual LayoutShape leftLayoutShape() const = 0;
virtual LayoutShape rightLayoutShape() const { return leftLayoutShape(); }
/* Hierarchy */
ExpressionNode * childAtIndex(int i) const override { return static_cast<ExpressionNode *>(TreeNode::childAtIndex(i)); }
virtual void setChildrenInPlace(Expression other);
protected:
/* Hierarchy */
ExpressionNode * parent() const override { return static_cast<ExpressionNode *>(TreeNode::parent()); }
Direct<ExpressionNode> children() const { return Direct<ExpressionNode>(this); }
};
}
#endif