[Sequences] Fixed a few crashes

Change-Id: Ib929bbae0f9ca06409706336ff799075e1288694
This commit is contained in:
Arthur Camouseigt
2020-09-17 11:43:27 +02:00
committed by Émilie Feral
parent 26bbdead7d
commit 960335c330
7 changed files with 60 additions and 16 deletions

View File

@@ -12,9 +12,10 @@ using namespace Poincare;
namespace Shared {
template<typename T>
CacheContext<T>::CacheContext(Context * parentContext) :
ContextWithParent(parentContext),
m_values{{NAN, NAN},{NAN, NAN},{NAN,NAN}}
CacheContext<T>::CacheContext(SequenceContext * sequenceContext) :
ContextWithParent(sequenceContext),
m_values{{NAN, NAN},{NAN, NAN},{NAN,NAN}},
m_sequenceContext(sequenceContext)
{
}
@@ -34,9 +35,14 @@ const Expression CacheContext<T>::expressionForSymbolAbstract(const Poincare::Sy
Sequence * seq = m_sequenceContext->sequenceStore()->modelForRecord(record);
rank.replaceSymbolWithExpression(Symbol::Builder(UCodePointUnknown), Float<T>::Builder(m_nValue));
T n = PoincareHelpers::ApproximateToScalar<T>(rank, this);
// In case the rank is not int or sequence referenced is not defined, return NAN
if (std::floor(n) == n && seq->fullName() != nullptr) {
return Float<T>::Builder(seq->valueAtRank<T>(n, m_sequenceContext));
// In case the sequence referenced is not defined or if the rank is not an int, return NAN
if (seq->fullName() != nullptr) {
if (std::floor(n) == n) {
Expression sequenceExpression = seq->expressionReduced(this);
if (seq->hasValidExpression(this)) {
return Float<T>::Builder(seq->valueAtRank<T>(n, m_sequenceContext));
}
}
}
} else {
return Float<T>::Builder(NAN);

View File

@@ -11,11 +11,10 @@ namespace Shared {
template<typename T>
class CacheContext : public Poincare::ContextWithParent {
public:
CacheContext(Poincare::Context * parentContext);
CacheContext(SequenceContext * sequenceContext);
const Poincare::Expression expressionForSymbolAbstract(const Poincare::SymbolAbstract & symbol, bool clone, float unknownSymbolValue = NAN) override;
void setValueForSymbol(T value, const Poincare::Symbol & symbol);
void setNValue(int n) { m_nValue = n; }
void setSequenceContext(SequenceContext * sequenceContext) { m_sequenceContext = sequenceContext;}
private:
int nameIndexForSymbol(const Poincare::Symbol & symbol);
int rankIndexForSymbol(const Poincare::Symbol & symbol);

View File

@@ -127,7 +127,7 @@ const Expression GlobalContext::ExpressionForSequence(const SymbolAbstract & sym
char unknownN[bufferSize];
Poincare::SerializationHelper::CodePoint(unknownN, bufferSize, UCodePointUnknown);
float rank = symbol.childAtIndex(0).approximateWithValueForSymbol<float>(unknownN, unknownSymbolValue, ctx, Preferences::sharedPreferences()->complexFormat(),Preferences::sharedPreferences()->angleUnit());
if (std::floor(rank) == rank && seq.hasValidExpression()) {
if (std::floor(rank) == rank) {
SequenceContext sqctx(ctx, sequenceStore());
return Float<double>::Builder(seq.evaluateXYAtParameter(rank, &sqctx).x2());
} else {

View File

@@ -7,6 +7,8 @@
#include <poincare/sum.h>
#include <poincare/vertical_offset_layout.h>
#include <poincare/integer.h>
#include <poincare/rational.h>
#include <poincare/addition.h>
#include "../shared/poincare_helpers.h"
#include <string.h>
#include <apps/i18n.h>
@@ -131,6 +133,40 @@ bool Sequence::isEmpty() {
(type == Type::SingleRecurrence || data->initialConditionSize(1) == 0)));
}
bool Sequence::badlyReferencesItself(Context * context) {
Expression e = expressionReduced(context);
bool value = e.hasExpression([](Expression e, const void * sequencePointer) {
if (e.type() != ExpressionNode::Type::Sequence) {
return false;
}
Sequence * seq = (Sequence *)(sequencePointer);
const char * symbolName = static_cast<Symbol&>(e).name();
/* symbolName is either u, v or w while seq->fullName has the extention .seq
* at the end. Therefore we cannot use strcmp on the two strings. We just
* want to check if the first char are identical*/
if (strncmp(symbolName, seq->fullName(), strlen(symbolName)) == 0) {
/* The expression of the sequence contains a reference to itself.
* We must check if the sequence can be calculated before continuing
* If the sequence is of explicit type, it cannot reference itself.
* If the sequence is of SingleRecurrent type, it can be defined by:
* u(initialRank and u(n).
* If the sequence is of DoubleRecurrent type, it can be defined by:
* u(initialRank), u(initialRank+1), u(n) and u(n+1).
* In any other case, the value of the sequence cannot be computed.
* We therefore return NAN. */
Expression rank = e.childAtIndex(0);
if (seq->type() == Sequence::Type::Explicit ||
(!(rank.isIdenticalTo(Rational::Builder(seq->initialRank())) || rank.isIdenticalTo(Symbol::Builder(UCodePointUnknown))) &&
(seq->type() == Sequence::Type::SingleRecurrence || (seq->type() == Sequence::Type::DoubleRecurrence && !(rank.isIdenticalTo(Rational::Builder(seq->initialRank()+1)) || rank.isIdenticalTo(Addition::Builder(Symbol::Builder(UCodePointUnknown), Rational::Builder(1))))))))
{
return true;
}
}
return false;
}, reinterpret_cast<const void *>(this));
return value;
}
template<typename T>
T Sequence::templatedApproximateAtAbscissa(T x, SequenceContext * sqctx) const {
T n = std::round(x);
@@ -143,7 +179,7 @@ T Sequence::templatedApproximateAtAbscissa(T x, SequenceContext * sqctx) const {
template<typename T>
T Sequence::valueAtRank(int n, SequenceContext *sqctx) {
if (n < 0) {
if (n < 0 || badlyReferencesItself(sqctx)) {
return NAN;
}
int sequenceIndex = SequenceStore::sequenceIndexForName(fullName()[0]);
@@ -174,7 +210,6 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx, int sequenceIn
Poincare::SerializationHelper::CodePoint(unknownN, bufferSize, UCodePointUnknown);
CacheContext<T> ctx = CacheContext<T>(sqctx);
ctx.setSequenceContext(sqctx);
// Hold values u(n), u(n-1), u(n-2), v(n), v(n-1), v(n-2)...
T values[MaxNumberOfSequences][MaxRecurrenceDepth+1];

View File

@@ -59,7 +59,8 @@ public:
Poincare::Layout nameLayout();
bool isDefined() override;
bool isEmpty() override;
bool hasValidExpression() { return m_definition.hasValidExpression(); }
bool hasValidExpression(Poincare::Context * context) { return m_definition.hasValidExpression() && !badlyReferencesItself(context); }
bool badlyReferencesItself(Poincare::Context * context);
// Approximation
Poincare::Coordinate2D<float> evaluateXYAtParameter(float x, Poincare::Context * context) const override {
return Poincare::Coordinate2D<float>(x, templatedApproximateAtAbscissa(x, static_cast<SequenceContext *>(context)));

View File

@@ -102,7 +102,7 @@ void EquationStore::approximateSolve(Poincare::Context * context, bool shouldRep
for (int i = 0; i <= k_maxNumberOfApproximateSolutions; i++) {
root = PoincareHelpers::NextRoot(undevelopedExpression, m_variables[0], start, step, m_intervalApproximateSolutions[1], context);
if (i == k_maxNumberOfApproximateSolutions) {
m_hasMoreThanMaxNumberOfApproximateSolution = !isnan(root);
m_hasMoreThanMaxNumberOfApproximateSolution = !std::isnan(root);
break;
}
m_approximateSolutions[i] = root;

View File

@@ -97,9 +97,12 @@ Expression Sequence::replaceSymbolWithExpression(const SymbolAbstract & symbol,
}
Expression Sequence::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined
|| childAtIndex(0).isUndefined())
{
Expression e = Expression::defaultShallowReduce();
e = e.defaultHandleUnitsInChildren();
if (e.isUndefined()) {
return e;
}
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined) {
return replaceWithUndefinedInPlace();
}
return *this;