diff --git a/apps/math_variable_box_controller.cpp b/apps/math_variable_box_controller.cpp index 87b671788..28f179e26 100644 --- a/apps/math_variable_box_controller.cpp +++ b/apps/math_variable_box_controller.cpp @@ -124,7 +124,7 @@ void MathVariableBoxController::willDisplayCellForIndex(HighlightCell * cell, in symbolName, Shared::Sequence::k_maxNameWithArgumentSize ); - Expression symbolExpression = Expression::ParseAndSimplify(symbolName, AppsContainer::sharedAppsContainer()->globalContext(), Poincare::Preferences::sharedPreferences()->complexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat()); + Expression symbolExpression = Expression::Parse(symbolName, AppsContainer::sharedAppsContainer()->globalContext()); symbolLayout = symbolExpression.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::Preferences::sharedPreferences()->numberOfSignificantDigits()); } if (symbolLayout.isUninitialized()) { diff --git a/ion/include/ion/storage.h b/ion/include/ion/storage.h index 3cca0c4ca..f1a091d49 100644 --- a/ion/include/ion/storage.h +++ b/ion/include/ion/storage.h @@ -28,7 +28,7 @@ public: static constexpr char funcExtension[] = "func"; static constexpr char seqExtension[] = "seq"; - class Record { + class Record { /* A Record is identified by the CRC32 on its fullName because: * - A record is identified by its fullName, which is unique * - We cannot keep the address pointing to the fullName because if another diff --git a/poincare/include/poincare/context.h b/poincare/include/poincare/context.h index 5b23367c7..2b46c62aa 100644 --- a/poincare/include/poincare/context.h +++ b/poincare/include/poincare/context.h @@ -16,7 +16,8 @@ public: None, Function, Sequence, - Symbol + Symbol, + Integer // Used to simplify sequences }; virtual SymbolAbstractType expressionTypeForIdentifier(const char * identifier, int length) = 0; virtual const Expression expressionForSymbolAbstract(const SymbolAbstract & symbol, bool clone, float unknownSymbolValue = NAN) = 0; diff --git a/poincare/include/poincare/integer_variable_context.h b/poincare/include/poincare/integer_variable_context.h new file mode 100644 index 000000000..f5ec81fa8 --- /dev/null +++ b/poincare/include/poincare/integer_variable_context.h @@ -0,0 +1,24 @@ +#ifndef POINCARE_INTEGER_VARIABLE_CONTEXT_H +#define POINCARE_INTEGER_VARIABLE_CONTEXT_H + +#include +#include + +namespace Poincare { + +class IntegerVariableContext : public ContextWithParent { +public: + IntegerVariableContext(const char * name, Context * parentContext) : + ContextWithParent(parentContext), + m_name(name) + {} + + SymbolAbstractType expressionTypeForIdentifier(const char * identifier, int length) override { return strcmp(m_name, identifier) == 0 ? SymbolAbstractType::Integer : ContextWithParent::expressionTypeForIdentifier(identifier, length); } + +private: + const char * m_name; +}; + +} + +#endif diff --git a/poincare/include/poincare/sequence.h b/poincare/include/poincare/sequence.h index a5e7656de..cbc04d3c6 100644 --- a/poincare/include/poincare/sequence.h +++ b/poincare/include/poincare/sequence.h @@ -18,6 +18,7 @@ public: #endif Type type() const override { return Type::Sequence; } + virtual Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) override; Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) override; int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted, bool ignoreParentheses) const override; @@ -46,6 +47,7 @@ public: static Sequence Builder(const char * name, size_t length, Expression child = Expression()); // Simplification + Expression replacedByDefinitionIfPossible(Context * reductionContext); Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression); Expression shallowReduce(ExpressionNode::ReductionContext reductionContext); Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount); diff --git a/poincare/include/poincare/sum_and_product.h b/poincare/include/poincare/sum_and_product.h index 54f038bfe..a28d00f9a 100644 --- a/poincare/include/poincare/sum_and_product.h +++ b/poincare/include/poincare/sum_and_product.h @@ -10,6 +10,8 @@ namespace Poincare { class SumAndProductNode : public ParameteredExpressionNode { public: int numberOfChildren() const override { return 4; } + virtual void deepReduceChildren(ReductionContext reductionContext) override; + virtual Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) override; private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; virtual Layout createSumAndProductLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const = 0; @@ -28,6 +30,7 @@ private: class SumAndProduct : public Expression { public: SumAndProduct(const SumAndProductNode * n) : Expression(n) {} + Expression deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount); Expression shallowReduce(Context * context); }; diff --git a/poincare/src/function.cpp b/poincare/src/function.cpp index 16832db33..23bf8550c 100644 --- a/poincare/src/function.cpp +++ b/poincare/src/function.cpp @@ -133,7 +133,7 @@ Expression Function::shallowReduce(ExpressionNode::ReductionContext reductionCon Expression Function::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) { { // Replace replaceable symbols in child - Expression self = defaultReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly ,parameteredAncestorsCount); + Expression self = defaultReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount); if (self.isUninitialized()) { // if the child is circularly defined, escape return self; } diff --git a/poincare/src/sequence.cpp b/poincare/src/sequence.cpp index bae19166c..666b418c7 100644 --- a/poincare/src/sequence.cpp +++ b/poincare/src/sequence.cpp @@ -9,6 +9,10 @@ #include #include #include +#include +#include +#include +#include namespace Poincare { @@ -106,19 +110,94 @@ Expression Sequence::replaceSymbolWithExpression(const SymbolAbstract & symbol, } Expression Sequence::shallowReduce(ExpressionNode::ReductionContext reductionContext) { - Expression e = Expression::defaultShallowReduce(); - e = e.defaultHandleUnitsInChildren(); - if (e.isUndefined()) { - return e; - } if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined) { return replaceWithUndefinedInPlace(); } - return *this; + + if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::DoNotReplaceAnySymbol) { + return *this; + } + + Expression result = replacedByDefinitionIfPossible(reductionContext.context()); + result = Expression::ExpressionWithoutSymbols(result, reductionContext.context()); + + if (result.isUninitialized()) { + return *this; + } + + replaceWithInPlace(result); + + // We simplify the expression entered by the user + return result.deepReduce(reductionContext); +} + +Expression SequenceNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) { + return Sequence(this).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount); } Expression Sequence::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) { + { + // Replace replaceable symbols in child + Expression self = defaultReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount); + if (self.isUninitialized()) { // if the child is circularly defined, escape + return self; + } + assert(*this == self); + } + Expression e = replacedByDefinitionIfPossible(context); + if (e.isUninitialized()) { + return *this; + } + // If the function contains itself, return undefined + if (e.hasExpression([](Expression e, const void * context) { + if (e.type() != ExpressionNode::Type::Sequence) { + return false; + } + return strcmp(static_cast(e).name(), reinterpret_cast(context)) == 0; + }, reinterpret_cast(name()))) + { + return Expression(); + } + replaceWithInPlace(e); + *didReplace = true; + return e; return *this; } +Expression Sequence::replacedByDefinitionIfPossible(Context * context) { + // We try to replace the sequence by his definition ONLY if the index is an integer + bool canBeReplacedByExpression = false; + + if (childAtIndex(0).type() == ExpressionNode::Type::Symbol) { + const char * symbolName = (childAtIndex(0).convert()).name(); + if (context->expressionTypeForIdentifier(symbolName, strlen(symbolName)) == Context::SymbolAbstractType::Integer) { + canBeReplacedByExpression = true; + } + } else if (childAtIndex(0).type() == ExpressionNode::Type::Rational) { + Rational r = childAtIndex(0).convert(); + if (r.isInteger()) { + canBeReplacedByExpression = true; + } + } + + if (!canBeReplacedByExpression) { + return Expression(); + } + + Ion::Storage::Record r = Ion::Storage::sharedStorage()->recordBaseNamedWithExtension(name(), Ion::Storage::seqExtension); + + if (r.isNull()) { + return Expression(); + } + + Shared::Sequence seq(r); + + if (seq.type() != Shared::Sequence::Type::Explicit) { + return Expression(); + } + + Expression result = seq.expressionClone(); + return result.replaceSymbolWithExpression(Symbol::Builder(UCodePointUnknown), childAtIndex(0)); +} + } diff --git a/poincare/src/sum_and_product.cpp b/poincare/src/sum_and_product.cpp index 2cf0f1f4f..a05e3e955 100644 --- a/poincare/src/sum_and_product.cpp +++ b/poincare/src/sum_and_product.cpp @@ -2,6 +2,10 @@ #include #include #include +#include +#include +#include +#include extern "C" { #include #include @@ -56,6 +60,36 @@ Evaluation SumAndProductNode::templatedApproximate(ApproximationContext appro return result; } +Expression SumAndProductNode::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) { + return SumAndProduct(this).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount); +} + +Expression SumAndProduct::deepReplaceReplaceableSymbols(Context * context, bool * didReplace, bool replaceFunctionsOnly, int parameteredAncestorsCount) { + int nbChildren = numberOfChildren(); + for (int i = 1; i < nbChildren; i++) { + Expression c = childAtIndex(i).deepReplaceReplaceableSymbols(context, didReplace, replaceFunctionsOnly, parameteredAncestorsCount); + if (c.isUninitialized()) { + return Expression(); + } + } + + Symbol symbol = childAtIndex(1).convert(); + IntegerVariableContext newContext = IntegerVariableContext(symbol.name(), context); + Expression c = childAtIndex(0).deepReplaceReplaceableSymbols(&newContext, didReplace, replaceFunctionsOnly, parameteredAncestorsCount); + if (c.isUninitialized()) { + return Expression(); + } + + return *this; +} + +void SumAndProductNode::deepReduceChildren(ReductionContext reductionContext) { + SymbolNode * symbol = static_cast(childAtIndex(1)); + IntegerVariableContext newContext = IntegerVariableContext(symbol->name(), reductionContext.context()); + reductionContext.setContext(&newContext); + ExpressionNode::deepReduceChildren(reductionContext); +} + Expression SumAndProduct::shallowReduce(Context * context) { { Expression e = Expression::defaultShallowReduce();