mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-20 22:30:30 +01:00
341 lines
9.3 KiB
C++
341 lines
9.3 KiB
C++
#include <poincare/symbol.h>
|
|
#include <poincare/allocation_failure_expression_node.h>
|
|
#include <poincare/context.h>
|
|
#include <poincare/rational.h>
|
|
#include <poincare/parenthesis.h>
|
|
#include <poincare/layout_helper.h>
|
|
#include <poincare/char_layout_node.h>
|
|
#include <poincare/horizontal_layout_node.h>
|
|
#include <poincare/vertical_offset_layout_node.h>
|
|
#include <ion.h>
|
|
#include <cmath>
|
|
#include <assert.h>
|
|
|
|
namespace Poincare {
|
|
|
|
SymbolNode * SymbolNode::FailedAllocationStaticNode() {
|
|
static AllocationFailureExpressionNode<SymbolNode> failure;
|
|
TreePool::sharedPool()->registerStaticNodeIfRequired(&failure);
|
|
return &failure;
|
|
}
|
|
|
|
ExpressionNode::Sign SymbolNode::sign() const {
|
|
/* TODO: Maybe, we will want to know that from a context given in parameter:
|
|
if (context.expressionForSymbol(this) != nullptr) {
|
|
return context.expressionForSymbol(this)->sign(context);
|
|
}*/
|
|
if (m_name == Ion::Charset::SmallPi) {
|
|
return Sign::Positive;
|
|
}
|
|
if (m_name == Ion::Charset::Exponential) {
|
|
return Sign::Positive;
|
|
}
|
|
return Sign::Unknown;
|
|
}
|
|
|
|
Expression SymbolNode::replaceSymbolWithExpression(char symbol, Expression expression) const {
|
|
return Symbol(this).replaceSymbolWithExpression(symbol, expression);
|
|
}
|
|
|
|
int SymbolNode::polynomialDegree(char symbol) const {
|
|
if (m_name == symbol) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int SymbolNode::getPolynomialCoefficients(char symbolName, Expression coefficients[]) const {
|
|
return Symbol(this).getPolynomialCoefficients(symbolName, coefficients);
|
|
}
|
|
|
|
int SymbolNode::getVariables(isVariableTest isVariable, char * variables) const {
|
|
size_t variablesLength = strlen(variables);
|
|
if (isVariable(m_name)) {
|
|
char * currentChar = variables;
|
|
while (*currentChar != 0) {
|
|
if (*currentChar == m_name) {
|
|
return variablesLength;
|
|
}
|
|
currentChar++;
|
|
}
|
|
if (variablesLength < Expression::k_maxNumberOfVariables) {
|
|
variables[variablesLength] = m_name;
|
|
variables[variablesLength+1] = 0;
|
|
return variablesLength+1;
|
|
}
|
|
return -1;
|
|
}
|
|
return variablesLength;
|
|
}
|
|
|
|
float SymbolNode::characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const {
|
|
if (m_name == 'x') {
|
|
return NAN;
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
int SymbolNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const {
|
|
assert(e->type() == Type::Symbol);
|
|
if ((uint8_t)m_name == ((uint8_t)static_cast<const SymbolNode *>(e)->name())) {
|
|
return 0;
|
|
}
|
|
if ((uint8_t)m_name > ((uint8_t)static_cast<const SymbolNode *>(e)->name())) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
LayoutRef SymbolNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
|
|
if (m_name == Symbol::SpecialSymbols::Ans) {
|
|
return LayoutHelper::String("ans", 3);
|
|
}
|
|
if (m_name == Symbol::SpecialSymbols::un) {
|
|
return HorizontalLayoutRef(
|
|
CharLayoutRef('u'),
|
|
VerticalOffsetLayoutRef(
|
|
CharLayoutRef('n'),
|
|
VerticalOffsetLayoutNode::Type::Subscript));
|
|
}
|
|
if (m_name == Symbol::SpecialSymbols::un1) {
|
|
return HorizontalLayoutRef(
|
|
CharLayoutRef('u'),
|
|
VerticalOffsetLayoutRef(
|
|
LayoutHelper::String("n+1", 3),
|
|
VerticalOffsetLayoutNode::Type::Subscript));
|
|
}
|
|
if (m_name == Symbol::SpecialSymbols::vn) {
|
|
return HorizontalLayoutRef(
|
|
CharLayoutRef('v'),
|
|
VerticalOffsetLayoutRef(
|
|
CharLayoutRef('n'),
|
|
VerticalOffsetLayoutNode::Type::Subscript));
|
|
}
|
|
if (m_name == Symbol::SpecialSymbols::vn1) {
|
|
return HorizontalLayoutRef(
|
|
CharLayoutRef('v'),
|
|
VerticalOffsetLayoutRef(
|
|
LayoutHelper::String("n+1", 3),
|
|
VerticalOffsetLayoutNode::Type::Subscript));
|
|
}
|
|
if (Symbol::isMatrixSymbol(m_name) || Symbol::isSeriesSymbol(m_name) || Symbol::isRegressionSymbol(m_name)) {
|
|
return LayoutHelper::String(Symbol::textForSpecialSymbols(m_name), 2);
|
|
}
|
|
return LayoutHelper::String(&m_name, 1);
|
|
}
|
|
|
|
int SymbolNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
|
|
if (bufferSize == 0) {
|
|
return -1;
|
|
}
|
|
if (bufferSize == 1) {
|
|
buffer[bufferSize-1] = 0;
|
|
return 0;
|
|
}
|
|
/* Special cases for all special symbols */
|
|
if (m_name >0 && m_name < 32) {
|
|
return strlcpy(buffer, Symbol::textForSpecialSymbols(m_name), bufferSize);
|
|
}
|
|
buffer[0] = m_name;
|
|
buffer[1] = 0;
|
|
return 1;
|
|
}
|
|
|
|
Expression SymbolNode::shallowReduce(Context& context, Preferences::AngleUnit angleUnit) const {
|
|
return Symbol(this).shallowReduce(context, angleUnit);
|
|
}
|
|
|
|
bool SymbolNode::hasAnExactRepresentation(Context & context) const {
|
|
// TODO: so far, no symbols can be exact but A, ..Z should be able to hold exact values later.
|
|
return false;
|
|
}
|
|
|
|
template<typename T>
|
|
Evaluation<T> SymbolNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const {
|
|
if (m_name == Ion::Charset::IComplex) {
|
|
return Complex<T>(0.0, 1.0);
|
|
}
|
|
const Expression e = context.expressionForSymbol(Symbol(m_name));
|
|
if (e.isDefined()) {
|
|
return e.node()->approximate(T(), context, angleUnit);
|
|
}
|
|
return Complex<T>::Undefined();
|
|
}
|
|
|
|
const char * Symbol::textForSpecialSymbols(char name) {
|
|
switch (name) {
|
|
case SpecialSymbols::Ans:
|
|
return "ans";
|
|
case SpecialSymbols::un:
|
|
return "u(n)";
|
|
case SpecialSymbols::un1:
|
|
return "u(n+1)";
|
|
case SpecialSymbols::vn:
|
|
return "v(n)";
|
|
case SpecialSymbols::vn1:
|
|
return "v(n+1)";
|
|
case SpecialSymbols::M0:
|
|
return "M0";
|
|
case SpecialSymbols::M1:
|
|
return "M1";
|
|
case SpecialSymbols::M2:
|
|
return "M2";
|
|
case SpecialSymbols::M3:
|
|
return "M3";
|
|
case SpecialSymbols::M4:
|
|
return "M4";
|
|
case SpecialSymbols::M5:
|
|
return "M5";
|
|
case SpecialSymbols::M6:
|
|
return "M6";
|
|
case SpecialSymbols::M7:
|
|
return "M7";
|
|
case SpecialSymbols::M8:
|
|
return "M8";
|
|
case SpecialSymbols::M9:
|
|
return "M9";
|
|
case SpecialSymbols::V1:
|
|
return "V1";
|
|
case SpecialSymbols::N1:
|
|
return "N1";
|
|
case SpecialSymbols::V2:
|
|
return "V2";
|
|
case SpecialSymbols::N2:
|
|
return "N2";
|
|
case SpecialSymbols::V3:
|
|
return "V3";
|
|
case SpecialSymbols::N3:
|
|
return "N3";
|
|
case SpecialSymbols::X1:
|
|
return "X1";
|
|
case SpecialSymbols::Y1:
|
|
return "Y1";
|
|
case SpecialSymbols::X2:
|
|
return "X2";
|
|
case SpecialSymbols::Y2:
|
|
return "Y2";
|
|
case SpecialSymbols::X3:
|
|
return "X3";
|
|
case SpecialSymbols::Y3:
|
|
return "Y3";
|
|
default:
|
|
assert(false);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Symbol::SpecialSymbols Symbol::matrixSymbol(char index) {
|
|
switch (index - '0') {
|
|
case 0:
|
|
return SpecialSymbols::M0;
|
|
case 1:
|
|
return SpecialSymbols::M1;
|
|
case 2:
|
|
return SpecialSymbols::M2;
|
|
case 3:
|
|
return SpecialSymbols::M3;
|
|
case 4:
|
|
return SpecialSymbols::M4;
|
|
case 5:
|
|
return SpecialSymbols::M5;
|
|
case 6:
|
|
return SpecialSymbols::M6;
|
|
case 7:
|
|
return SpecialSymbols::M7;
|
|
case 8:
|
|
return SpecialSymbols::M8;
|
|
case 9:
|
|
return SpecialSymbols::M9;
|
|
default:
|
|
assert(false);
|
|
return SpecialSymbols::M0;
|
|
}
|
|
}
|
|
|
|
bool Symbol::isMatrixSymbol(char c) {
|
|
if (c >= (char)SpecialSymbols::M0 && c <= (char)SpecialSymbols::M9) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Symbol::isScalarSymbol(char c) {
|
|
if (c >= 'A' && c <= 'Z') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Symbol::isVariableSymbol(char c) {
|
|
if (c >= 'a' && c <= 'z') {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Symbol::isSeriesSymbol(char c) {
|
|
if (c >= (char)SpecialSymbols::V1 && c <= (char)SpecialSymbols::N3) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Symbol::isRegressionSymbol(char c) {
|
|
if (c >= (char)SpecialSymbols::X1 && c <= (char)SpecialSymbols::Y3) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Symbol::isApproximate(char c, Context & context) {
|
|
// TODO: so far, all symbols A to Z, M0->M9 hold an approximate values. But they should be able to hold exact values later.
|
|
if (Symbol::isScalarSymbol(c) || Symbol::isMatrixSymbol(c)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Expression Symbol::shallowReduce(Context& context, Preferences::AngleUnit angleUnit) const {
|
|
// TODO: avoid ExpressionRef comparison ? 2 solutions:
|
|
// - Compare identifiant
|
|
// - virtualize deepReduce to do nothing on the second child of Store?
|
|
#if 0
|
|
// Do not replace symbols in expression of type: 3->A
|
|
Expression p = parent();
|
|
if (p.isDefined() && p.type() == ExpressionNode::Type::Store && p.childAtIndex(1) == *this) {
|
|
return *this;
|
|
}
|
|
#endif
|
|
const Expression e = context.expressionForSymbol(*this);
|
|
if (e.isDefined() && node()->hasAnExactRepresentation(context)) {
|
|
// TODO: later AZ should be replaced.
|
|
/* The stored expression had been beautified which forces to call deepReduce. */
|
|
return e.deepReduce(context, angleUnit);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Expression Symbol::replaceSymbolWithExpression(char symbol, Expression expression) const {
|
|
if (name() == symbol) {
|
|
Expression value = expression;
|
|
Expression p = parent();
|
|
if (p.isDefined() && value.node()->needsParenthesesWithParent(p.node())) {
|
|
value = Parenthesis(value);
|
|
}
|
|
return value;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
int Symbol::getPolynomialCoefficients(char symbolName, Expression coefficients[]) const {
|
|
if (name() == symbolName) {
|
|
coefficients[0] = Rational(0);
|
|
coefficients[1] = Rational(1);
|
|
return 1;
|
|
}
|
|
coefficients[0] = Symbol(name());
|
|
return 0;
|
|
}
|
|
|
|
}
|