From 8434da60efff25cd5a0bb5744992b0927d977d8c Mon Sep 17 00:00:00 2001 From: Arthur Camouseigt Date: Fri, 3 Jul 2020 17:15:38 +0200 Subject: [PATCH] [sequence] Changed sequences behavior Sequences can now be defined using specific terms form other sequences : Un = n Vn = u(3) Initial values can also depend on other sequences. Should there be a circular dependency, the sequences concerned will display "undef" as value Change-Id: I6fe1f3ff7b500f35d480ddefb42de729c327432e --- apps/sequence/cache_context.cpp | 36 +++++-- apps/sequence/cache_context.h | 2 + apps/sequence/sequence.cpp | 59 +++++++++-- apps/sequence/sequence.h | 3 +- apps/sequence/sequence_context.cpp | 97 ++++++++++++++----- apps/sequence/sequence_context.h | 72 ++++++++++---- apps/sequence/sequence_store.h | 1 + apps/sequence/test/sequence.cpp | 64 ++++++++++++ .../include/poincare/context_with_parent.h | 1 + poincare/src/parsing/parser.cpp | 10 ++ poincare/src/symbol.cpp | 16 +++ 11 files changed, 301 insertions(+), 60 deletions(-) diff --git a/apps/sequence/cache_context.cpp b/apps/sequence/cache_context.cpp index be5a834b0..f871eba5a 100644 --- a/apps/sequence/cache_context.cpp +++ b/apps/sequence/cache_context.cpp @@ -1,5 +1,8 @@ #include "cache_context.h" +#include "sequence.h" #include "sequence_store.h" +#include "../shared/poincare_helpers.h" +#include #include using namespace Poincare; @@ -16,13 +19,30 @@ CacheContext::CacheContext(Context * parentContext) : template const Expression CacheContext::expressionForSymbolAbstract(const SymbolAbstract & symbol, bool clone) { // [u|v|w](n(+1)?) + // u,v,w are reserved names. They can only be set through the sequence app if (symbol.type() == ExpressionNode::Type::Symbol && symbol.name()[0] >= SequenceStore::k_sequenceNames[0][0] - && symbol.name()[0] <= SequenceStore::k_sequenceNames[MaxNumberOfSequences-1][0] - && (strcmp(symbol.name()+1, "(n)") == 0 || strcmp(symbol.name()+1, "(n+1)") == 0)) - { + && symbol.name()[0] <= SequenceStore::k_sequenceNames[MaxNumberOfSequences-1][0]) { + assert((symbol.name()+1)[0] == '('); Symbol s = const_cast(static_cast(symbol)); - return Float::Builder(m_values[nameIndexForSymbol(s)][rankIndexForSymbol(s)]); + if (strcmp(symbol.name()+1, "(n)") == 0 || strcmp(symbol.name()+1, "(n+1)") == 0) { + return Float::Builder(m_values[nameIndexForSymbol(s)][rankIndexForSymbol(s)]); + } else { + Sequence seq = m_sequenceContext->sequenceStore()->sequenceAtIndex(nameIndexForSymbol(s)); + // In case the sequence referenced is not defined, return NAN + if (seq.fullName() == nullptr) { + return Float::Builder(NAN); + } + int numberOfDigits = 1; + constexpr int offset = 2; // 2 = 1 for ('u') + 1 for ('(') + while (symbol.name()[offset+numberOfDigits] != ')') { + numberOfDigits++; + } + // Get the value of k in u(k) and store it in x + Integer integer(symbol.name()+2, numberOfDigits, false); + T x = integer.approximate(); + return Float::Builder(seq.valueAtRank(x, m_sequenceContext)); + } } return ContextWithParent::expressionForSymbolAbstract(symbol, clone); } @@ -34,7 +54,7 @@ void CacheContext::setValueForSymbol(T value, const Poincare::Symbol & symbol template int CacheContext::nameIndexForSymbol(const Poincare::Symbol & symbol) { - assert(strlen(symbol.name()) == 4 || strlen(symbol.name()) == 6); // [u|v|w](n(+1)?) + assert(strlen(symbol.name()) >= 4); // [u|v|w](n(+1) or k ?) char name = symbol.name()[0]; assert(name >= SequenceStore::k_sequenceNames[0][0] && name <= SequenceStore::k_sequenceNames[MaxNumberOfSequences-1][0]); // u, v or w return name - 'u'; @@ -42,11 +62,11 @@ int CacheContext::nameIndexForSymbol(const Poincare::Symbol & symbol) { template int CacheContext::rankIndexForSymbol(const Poincare::Symbol & symbol) { - assert(strlen(symbol.name()) == 4 || strlen(symbol.name()) == 6); // u(n) or u(n+1) - if (symbol.name()[3] == ')') { // .(n) + assert(strcmp(symbol.name()+1, "(n)") == 0 || strcmp(symbol.name()+1, "(n+1)") == 0); // u(n) or u(n+1) + if (symbol.name()[3] == ')') { // (n) return 0; } - // .(n+1) + // (n+1) return 1; } diff --git a/apps/sequence/cache_context.h b/apps/sequence/cache_context.h index 37c6200e5..4bb4f0493 100644 --- a/apps/sequence/cache_context.h +++ b/apps/sequence/cache_context.h @@ -14,10 +14,12 @@ public: CacheContext(Poincare::Context * parentContext); const Poincare::Expression expressionForSymbolAbstract(const Poincare::SymbolAbstract & symbol, bool clone) override; void setValueForSymbol(T value, const Poincare::Symbol & symbol); + void setSequenceContext(SequenceContext * sequenceContext) { m_sequenceContext = sequenceContext;} private: int nameIndexForSymbol(const Poincare::Symbol & symbol); int rankIndexForSymbol(const Poincare::Symbol & symbol); T m_values[MaxNumberOfSequences][MaxRecurrenceDepth]; + SequenceContext * m_sequenceContext; }; } diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index 64643ace1..667afc7d2 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -118,7 +118,30 @@ T Sequence::templatedApproximateAtAbscissa(T x, SequenceContext * sqctx) const { } template -T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { +T Sequence::valueAtRank(int n, SequenceContext *sqctx) { + if (n < 0) { + return NAN; + } + int sequenceIndex = SequenceStore::sequenceIndexForName(fullName()[0]); + if (sqctx->independantSequenceRank(sequenceIndex) > n || sqctx->independantSequenceRank(sequenceIndex) < 0) { + // Reset cache indexes and cache values + sqctx->setIndependantSequenceRank(-1, sequenceIndex); + for (int i = 0 ; i < MaxRecurrenceDepth+1; i++) { + sqctx->setIndependantSequenceValue(NAN, sequenceIndex, i); + } + } + + while(sqctx->independantSequenceRank(sequenceIndex) < n) { + sqctx->stepSequenceAtIndex(sequenceIndex); + } + /* In case we have sqctx->independantSequenceRank(sequenceIndex) = n, we can return the + * value */ + T value = sqctx->independantSequenceValue(sequenceIndex, 0); + return value; +} + +template +T Sequence::approximateToNextRank(int n, SequenceContext * sqctx, int sequenceIndex) const { if (n < initialRank() || n < 0) { return NAN; } @@ -128,11 +151,29 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { Poincare::SerializationHelper::CodePoint(unknownN, bufferSize, UCodePointUnknown); CacheContext ctx = CacheContext(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]; + + /* In case we step only one sequence to the next step, the data stored in + * values is not necessarily u(n), u(n-1).... Indeed, since the indexes are + * independant, if the index for u is 3 but the one for v is 5, value will + * hold u(3), u(2), u(1) | v(5), v(4), v(3). Therefore, the calculation will + * be wrong if they relay on a symbol such as u(n). To prevent this, we align + * all the values around the index of the sequence we are stepping. */ + int independantRank = sqctx->independantSequenceRank(sequenceIndex); for (int i = 0; i < MaxNumberOfSequences; i++) { - for (int j = 0; j < MaxRecurrenceDepth+1; j++) { - values[i][j] = sqctx->valueOfSequenceAtPreviousRank(i, j); + if (sequenceIndex != -1 && sqctx->independantSequenceRank(i) != independantRank) { + int offset = independantRank - sqctx->independantSequenceRank(i); + if (offset != 0) { + for (int j = MaxRecurrenceDepth; j >= 0; j--) { + values[i][j] = j-offset < 0 ? NAN : sqctx->independantSequenceValue(i, j-offset); + } + } + } else { + for (int j = 0; j < MaxRecurrenceDepth+1; j++) { + values[i][j] = sequenceIndex != -1 ? sqctx->independantSequenceValue(i, j) : sqctx->valueOfSequenceAtPreviousRank(i, j); + } } } // Hold symbols u(n), u(n+1), v(n), v(n+1), w(n), w(n+1) @@ -157,7 +198,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { case Type::SingleRecurrence: { if (n == initialRank()) { - return PoincareHelpers::ApproximateToScalar(firstInitialConditionExpressionReduced(sqctx), sqctx); + return PoincareHelpers::ApproximateWithValueForSymbol(firstInitialConditionExpressionReduced(sqctx), unknownN, (T)NAN, &ctx); } for (int i = 0; i < MaxNumberOfSequences; i++) { // Set in context u(n) = u(n-1) and u(n+1) = u(n) for all sequences @@ -169,10 +210,10 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { default: { if (n == initialRank()) { - return PoincareHelpers::ApproximateToScalar(firstInitialConditionExpressionReduced(sqctx), sqctx); + return PoincareHelpers::ApproximateWithValueForSymbol(firstInitialConditionExpressionReduced(sqctx), unknownN, (T)NAN, &ctx); } if (n == initialRank()+1) { - return PoincareHelpers::ApproximateToScalar(secondInitialConditionExpressionReduced(sqctx), sqctx); + return PoincareHelpers::ApproximateWithValueForSymbol(secondInitialConditionExpressionReduced(sqctx), unknownN, (T)NAN, &ctx); } for (int i = 0; i < MaxNumberOfSequences; i++) { // Set in context u(n) = u(n-2) and u(n+1) = u(n-1) for all sequences @@ -293,7 +334,9 @@ void Sequence::InitialConditionModel::buildName(Sequence * sequence) { template double Sequence::templatedApproximateAtAbscissa(double, SequenceContext*) const; template float Sequence::templatedApproximateAtAbscissa(float, SequenceContext*) const; -template double Sequence::approximateToNextRank(int, SequenceContext*) const; -template float Sequence::approximateToNextRank(int, SequenceContext*) const; +template double Sequence::approximateToNextRank(int, SequenceContext*, int) const; +template float Sequence::approximateToNextRank(int, SequenceContext*, int) const; +template double Sequence::valueAtRank(int, SequenceContext *); +template float Sequence::valueAtRank(int, SequenceContext *); } diff --git a/apps/sequence/sequence.h b/apps/sequence/sequence.h index 850cffdb0..1843acb6f 100644 --- a/apps/sequence/sequence.h +++ b/apps/sequence/sequence.h @@ -65,7 +65,8 @@ public: Poincare::Coordinate2D evaluateXYAtParameter(double x, Poincare::Context * context) const override { return Poincare::Coordinate2D(x,templatedApproximateAtAbscissa(x, static_cast(context))); } - template T approximateToNextRank(int n, SequenceContext * sqctx) const; + template T approximateToNextRank(int n, SequenceContext * sqctx, int sequenceIndex = -1) const; + template T valueAtRank(int n, SequenceContext * sqctx); Poincare::Expression sumBetweenBounds(double start, double end, Poincare::Context * context) const override; constexpr static int k_initialRankNumberOfDigits = 3; // m_initialRank is capped by 999 diff --git a/apps/sequence/sequence_context.cpp b/apps/sequence/sequence_context.cpp index fbb485f14..264738b09 100644 --- a/apps/sequence/sequence_context.cpp +++ b/apps/sequence/sequence_context.cpp @@ -1,5 +1,7 @@ #include "sequence_context.h" #include "sequence_store.h" +#include "cache_context.h" +#include "../shared/poincare_helpers.h" #include using namespace Poincare; @@ -9,63 +11,104 @@ namespace Sequence { template TemplatedSequenceContext::TemplatedSequenceContext() : - m_rank(-1), - m_values{{NAN, NAN, NAN}, {NAN, NAN, NAN}, {NAN, NAN, NAN}} + m_commonRank(-1), + m_commonRankValues{{NAN, NAN, NAN}, {NAN, NAN, NAN}, {NAN, NAN, NAN}}, + m_independantRanks{-1, -1, -1}, + m_independantRankValues{{NAN, NAN, NAN}, {NAN, NAN, NAN}, {NAN, NAN, NAN}} { } template T TemplatedSequenceContext::valueOfSequenceAtPreviousRank(int sequenceIndex, int rank) const { - return m_values[sequenceIndex][rank]; + return m_commonRankValues[sequenceIndex][rank]; } template void TemplatedSequenceContext::resetCache() { - m_rank = -1; + /* We only need to reset the ranks. Indeed, when we compute the values of the + * sequences, we use ranks as memoization indexes. Therefore, we know that the + * values stored in m_commomValues and m_independantRankValues are dirty + * and do not use them. */ + m_commonRank = -1; + for (int i = 0; i < MaxNumberOfSequences; i ++) { + m_independantRanks[i] = -1; + } } template bool TemplatedSequenceContext::iterateUntilRank(int n, SequenceStore * sequenceStore, SequenceContext * sqctx) { - if (m_rank > n) { - m_rank = -1; + if (m_commonRank > n) { + m_commonRank = -1; } - if (n < 0 || n-m_rank > k_maxRecurrentRank) { + if (n < 0 || n-m_commonRank > k_maxRecurrentRank) { return false; } - while (m_rank++ < n) { - step(sequenceStore, sqctx); + while (m_commonRank < n) { + step(sqctx); } - m_rank--; return true; } template -void TemplatedSequenceContext::step(SequenceStore * sequenceStore, SequenceContext * sqctx) { - /* Shift values */ - for (int i = 0; i < MaxNumberOfSequences; i++) { +void TemplatedSequenceContext::step(SequenceContext * sqctx, int sequenceIndex) { + // First we increment the rank + bool stepMultipleSequences = sequenceIndex == -1; + if (stepMultipleSequences) { + m_commonRank++; + } else { + setIndependantSequenceRank(independantSequenceRank(sequenceIndex) + 1, sequenceIndex); + } + + // Then we shift the values stored in the arrays + int start, stop, rank; + T * sequenceArray; + if (stepMultipleSequences) { + start = 0; + stop = MaxNumberOfSequences; + sequenceArray = reinterpret_cast(&m_commonRankValues); + rank = m_commonRank; + } else { + start = sequenceIndex; + stop = sequenceIndex + 1; + sequenceArray = reinterpret_cast(&m_independantRankValues); + rank = independantSequenceRank(sequenceIndex); + } + + for (int i = start; i < stop; i++) { + T * rowPointer = sequenceArray + i * (MaxRecurrenceDepth + 1); for (int j = MaxRecurrenceDepth; j > 0; j--) { - m_values[i][j] = m_values[i][j-1]; + *(rowPointer + j) = *(rowPointer + j - 1); } - m_values[i][0] = NAN; + *rowPointer= NAN; } - /* Evaluate new u(n) and v(n) */ - // sequences hold u, v, w in this order + // We create an array containing the sequences we want to update Sequence * sequences[MaxNumberOfSequences] = {nullptr, nullptr, nullptr}; - for (int i = 0; i < sequenceStore->numberOfModels(); i++) { + int usedSize = stepMultipleSequences ? MaxNumberOfSequences : 1; + SequenceStore * sequenceStore = sqctx->sequenceStore(); + stop = stepMultipleSequences ? sequenceStore->numberOfModels() : start + 1; + for (int i = start; i < stop; i++) { Sequence * u = sequenceStore->modelForRecord(sequenceStore->recordAtIndex(i)); - sequences[SequenceStore::sequenceIndexForName(u->fullName()[0])] = u->isDefined() ? u : nullptr; + int index = stepMultipleSequences ? SequenceStore::sequenceIndexForName(u->fullName()[0]) : 0; + sequences[index] = u->isDefined() ? u : nullptr; } - /* Approximate u, v and w at the new rank. We have to evaluate all sequences - * MaxNumberOfSequences times in case the evaluations depend on each other. + // We approximate the value of the next rank for each sequence we want to update + stop = stepMultipleSequences ? MaxNumberOfSequences : sequenceIndex + 1; + /* In case stop is MaxNumberOfSequences, we approximate u, v and w at the new + * rank. We have to evaluate all sequences MaxNumberOfSequences times in case + * the evaluations depend on each other. * For example, if u expression depends on v and v depends on w. At the first * iteration, we can only evaluate w, at the second iteration we evaluate v - * and u can only be known at the third iteration . */ - for (int k = 0; k < MaxNumberOfSequences; k++) { - for (int i = 0; i < MaxNumberOfSequences; i++) { - if (std::isnan(m_values[i][0])) { - m_values[i][0] = sequences[i] ? sequences[i]->approximateToNextRank(m_rank, sqctx) : NAN; + * and u can only be known at the third iteration. + * In case stop is 1, there is only one sequence we want to update. Moreover, + * the call to approximateToNextRank will handle potential dependencies. */ + for (int k = 0; k < usedSize; k++) { + for (int i = start; i < stop; i++) { + T * pointerToUpdate = sequenceArray + i * (MaxRecurrenceDepth + 1); + if (std::isnan(*(pointerToUpdate))) { + int j = stepMultipleSequences ? i : 0; + *(pointerToUpdate) = sequences[j] ? sequences[j]->template approximateToNextRank(rank, sqctx, sequenceIndex) : NAN; } } } @@ -73,5 +116,7 @@ void TemplatedSequenceContext::step(SequenceStore * sequenceStore, SequenceCo template class TemplatedSequenceContext; template class TemplatedSequenceContext; +template void * SequenceContext::helper(); +template void * SequenceContext::helper(); } diff --git a/apps/sequence/sequence_context.h b/apps/sequence/sequence_context.h index e4ca2cb42..702fa8b9f 100644 --- a/apps/sequence/sequence_context.h +++ b/apps/sequence/sequence_context.h @@ -20,17 +20,37 @@ public: T valueOfSequenceAtPreviousRank(int sequenceIndex, int rank) const; void resetCache(); bool iterateUntilRank(int n, SequenceStore * sequenceStore, SequenceContext * sqctx); + + int independantSequenceRank(int sequenceIndex) { return m_independantRanks[sequenceIndex]; } + void setIndependantSequenceRank(int rank, int sequenceIndex) { m_independantRanks[sequenceIndex] = rank; } + T independantSequenceValue(int sequenceIndex, int depth) { return m_independantRankValues[sequenceIndex][depth]; } + void setIndependantSequenceValue(T value, int sequenceIndex, int depth) { m_independantRankValues[sequenceIndex][depth] = value; } + void step(SequenceContext * sqctx, int sequenceIndex = -1); private: constexpr static int k_maxRecurrentRank = 10000; /* Cache: - * In order to accelerate the computation of values of recurrent sequences, - * we memoize the last computed values of the sequence and their associated - * ranks (n and n+1 for instance). Thereby, when another evaluation at a - * superior rank k > n+1 is called, we avoid iterating from 0 but can start - * from n. */ - void step(SequenceStore * sequenceStore, SequenceContext * sqctx); - int m_rank; - T m_values[MaxNumberOfSequences][MaxRecurrenceDepth+1]; + * We use two types of cache : + * The first one is used to to accelerate the + * computation of values of recurrent sequences. We memoize the last computed + * values of the sequences and their associated ranks (n and n+1 for instance). + * Thereby, when another evaluation at a superior rank k > n+1 is called, + * we avoid iterating from 0 but can start from n. This cache allows us to step + * all of the sequences at once. + * + * The second one used used for fixed term computation. For instance, if a + * sequence is defined using a fixed term of another, u(3) for instance, we + * compute its value through the second type of cache. This way, we do not + * erase the data stored in the first type of cache and we can compute the + * values of each sequence at independant rank. This means that + * (u(3), v(5), w(10)) can be computed at the same time. + * This cache is therefore used for independant steps of sequences + */ + int m_commonRank; + T m_commonRankValues[MaxNumberOfSequences][MaxRecurrenceDepth+1]; + + // Used for fixed computations + int m_independantRanks[MaxNumberOfSequences]; + T m_independantRankValues[MaxNumberOfSequences][MaxRecurrenceDepth+1]; }; class SequenceContext : public Poincare::ContextWithParent { @@ -44,26 +64,44 @@ public: * context respective methods. Indeed, special chars like n, u(n), u(n+1), * v(n), v(n+1) are taken into accound only when evaluating sequences which * is done in another context. */ - template T valueOfSequenceAtPreviousRank(int sequenceIndex, int rank) const { - if (sizeof(T) == sizeof(float)) { - return m_floatSequenceContext.valueOfSequenceAtPreviousRank(sequenceIndex, rank); - } - return m_doubleSequenceContext.valueOfSequenceAtPreviousRank(sequenceIndex, rank); + template T valueOfSequenceAtPreviousRank(int sequenceIndex, int rank) { + return static_cast*>(helper())->valueOfSequenceAtPreviousRank(sequenceIndex, rank); } + void resetCache() { m_floatSequenceContext.resetCache(); m_doubleSequenceContext.resetCache(); } + template bool iterateUntilRank(int n) { - if (sizeof(T) == sizeof(float)) { - return m_floatSequenceContext.iterateUntilRank(n, m_sequenceStore, this); - } - return m_doubleSequenceContext.iterateUntilRank(n, m_sequenceStore, this); + return static_cast*>(helper())->iterateUntilRank(n, m_sequenceStore, this); } + + template int independantSequenceRank(int sequenceIndex) { + return static_cast*>(helper())->independantSequenceRank(sequenceIndex); + } + + template void setIndependantSequenceRank(int rank, int sequenceIndex) { + static_cast*>(helper())->setIndependantSequenceRank(rank, sequenceIndex); + } + + template T independantSequenceValue(int sequenceIndex, int depth) { + return static_cast*>(helper())->independantSequenceValue(sequenceIndex, depth); + } + + template void setIndependantSequenceValue(T value, int sequenceIndex, int depth) { + static_cast*>(helper())->setIndependantSequenceValue(value, sequenceIndex, depth); + } + + template void stepSequenceAtIndex(int sequenceIndex) { + static_cast*>(helper())->step(this, sequenceIndex); + } + SequenceStore * sequenceStore() { return m_sequenceStore; } private: TemplatedSequenceContext m_floatSequenceContext; TemplatedSequenceContext m_doubleSequenceContext; SequenceStore * m_sequenceStore; + template void * helper() { return sizeof(T) == sizeof(float) ? (void*) &m_floatSequenceContext : (void*) &m_doubleSequenceContext; } }; } diff --git a/apps/sequence/sequence_store.h b/apps/sequence/sequence_store.h index dd6346ee9..1d1013ca5 100644 --- a/apps/sequence/sequence_store.h +++ b/apps/sequence/sequence_store.h @@ -27,6 +27,7 @@ public: static constexpr const char * k_sequenceNames[MaxNumberOfSequences] = { "u", "v", "w" }; + Sequence sequenceAtIndex(int i) { assert(i < MaxNumberOfSequences && i >= 0); return m_sequences[i]; } private: const char * modelExtension() const override { return Ion::Storage::seqExtension; } diff --git a/apps/sequence/test/sequence.cpp b/apps/sequence/test/sequence.cpp index 8d423b010..224e4ef26 100644 --- a/apps/sequence/test/sequence.cpp +++ b/apps/sequence/test/sequence.cpp @@ -336,6 +336,70 @@ QUIZ_CASE(sequence_evaluation) { conditions1[2] = nullptr; conditions2[2] = nullptr; check_sequences_defined_by(results28, types, definitions, conditions1, conditions2); + + // u independent, v depends on u(3) + // u(n) = n; v(n) = u(5)+n + double results29[MaxNumberOfSequences][10] = {{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + {5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0}, + {}}; + types[0] = Sequence::Type::Explicit; + types[1] = Sequence::Type::Explicit; + definitions[0] = "n"; + definitions[1] = "u(5)+n"; + definitions[2] = nullptr; + conditions1[0] = nullptr; + conditions2[0] = nullptr; + conditions1[1] = nullptr; + conditions2[1] = nullptr; + conditions1[2] = nullptr; + conditions2[2] = nullptr; + check_sequences_defined_by(results29, types, definitions, conditions1, conditions2); + +// u independent, v depends on u(2) +// u(n) = n; v(n+1) = v(n)-u(2) + double results30[MaxNumberOfSequences][10] = {{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + {2.0, 0.0, -2.0, -4.0, -6.0, -8.0, -10.0, -12.0, -14.0, -16.0}, + {}}; + types[0] = Sequence::Type::Explicit; + types[1] = Sequence::Type::SingleRecurrence; + definitions[0] = "n"; + definitions[1] = "v(n)-u(2)"; + conditions1[0] = nullptr; + conditions2[0] = nullptr; + conditions1[1] = "u(2)"; + check_sequences_defined_by(results30, types, definitions, conditions1, conditions2); + +// u and v interdependent +// u(n+2) = n + v(3) + u(n+1) - u(n); v(n) = u(n) - u(1) + double results31[MaxNumberOfSequences][10] = {{0.0, 3.0, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN}, + {-3.0, 0.0, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN}, + {}}; + types[0] = Sequence::Type::DoubleRecurrence; + types[1] = Sequence::Type::Explicit; + definitions[0] = "n+v(3)+u(n+1)-u(n)"; + definitions[1] = "u(n)-u(1)"; + conditions1[0] = "0"; + conditions2[0] = "3"; + check_sequences_defined_by(results31, types, definitions, conditions1, conditions2); + +// u is independent, v depends on u(120) and w(5), w depends on u(8) +// u(n) = n; v(n+2) = v(n+1) + v(n) + u(120); w(n+1) = w(n) - u(8) + double results32[MaxNumberOfSequences][10] = {{0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + {46.0, 6.0, 172.0, 298.0, 590.0, 1008.0, 1718.0, 2846.0, 4684.0, 7650.0}, + {6.0, 14.0, 22.0, 30.0, 38.0, 46.0, 54.0, 62.0, 70.0, 78.0}}; + types[0] = Sequence::Type::Explicit; + types[1] = Sequence::Type::DoubleRecurrence; + types[2] = Sequence::Type::SingleRecurrence; + definitions[0] = "n"; + definitions[1] = "v(n+1)+v(n)+u(120)"; + definitions[2] = "w(n)+u(8)"; + conditions1[0] = nullptr; + conditions2[0] = nullptr; + conditions1[1] = "w(5)"; + conditions2[1] = "6"; + conditions1[2] = "6"; + conditions2[2] = nullptr; + check_sequences_defined_by(results32, types, definitions, conditions1, conditions2); } QUIZ_CASE(sequence_sum_evaluation) { diff --git a/poincare/include/poincare/context_with_parent.h b/poincare/include/poincare/context_with_parent.h index 7f628abad..4e52ce828 100644 --- a/poincare/include/poincare/context_with_parent.h +++ b/poincare/include/poincare/context_with_parent.h @@ -15,6 +15,7 @@ public: SymbolAbstractType expressionTypeForIdentifier(const char * identifier, int length) override { return m_parentContext->expressionTypeForIdentifier(identifier, length); } void setExpressionForSymbolAbstract(const Expression & expression, const SymbolAbstract & symbol) override { m_parentContext->setExpressionForSymbolAbstract(expression, symbol); } const Expression expressionForSymbolAbstract(const SymbolAbstract & symbol, bool clone) override { return m_parentContext->expressionForSymbolAbstract(symbol, clone); } + private: Context * m_parentContext; }; diff --git a/poincare/src/parsing/parser.cpp b/poincare/src/parsing/parser.cpp index d20b3960b..36c90252f 100644 --- a/poincare/src/parsing/parser.cpp +++ b/poincare/src/parsing/parser.cpp @@ -421,6 +421,16 @@ void Parser::parseSequence(Expression & leftHandSide, const char name, Token::Ty constexpr int symbolNameSize = 5; char sym[symbolNameSize] = {name, '(', 'n', ')', 0}; leftHandSide = Symbol::Builder(sym, symbolNameSize); + } else if (rank.type() == ExpressionNode::Type::BasedInteger) { + Integer integer = static_cast(rank).integer(); + int symbolNameSize = 4 + Integer::NumberOfBase10DigitsWithoutSign(integer); + char sym[symbolNameSize]; + sym[0] = name; + sym[1] = '('; + integer.serialize(&sym[2], Integer::NumberOfBase10DigitsWithoutSign(integer)+1); + sym[symbolNameSize-2] = ')'; + sym[symbolNameSize-1] = '\0'; + leftHandSide = Symbol::Builder(sym, symbolNameSize); } else if (rank.isIdenticalTo(Addition::Builder(Symbol::Builder('n'), BasedInteger::Builder("1")))) { constexpr int symbolNameSize = 7; char sym[symbolNameSize] = {name, '(', 'n', '+', '1', ')', 0}; diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index e04e35656..369ed5832 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -81,6 +81,22 @@ Layout SymbolNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, in VerticalOffsetLayoutNode::Position::Subscript)); } } + /* Checking for u(k) forms with k positive integer. Since the sequence + * parser only handles sequenceIndexes like above and BasedIntegers, there + * is no need to be carefull with how we process m_name. */ + if (m_name[1] == '(') { + int numberOfDigits = 0; + while (m_name[numberOfDigits + 2] != ')') { + if (m_name[numberOfDigits + 2] <= '9' && m_name[numberOfDigits + 2] >= '0') { + numberOfDigits++; + } + } + return HorizontalLayout::Builder( + CodePointLayout::Builder(sequenceName), + VerticalOffsetLayout::Builder( + LayoutHelper::String(m_name+2, numberOfDigits), + VerticalOffsetLayoutNode::Position::Subscript)); + } } } return LayoutHelper::String(m_name, strlen(m_name));