mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-18 21:30:38 +01:00
[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
This commit is contained in:
committed by
Émilie Feral
parent
688394abce
commit
8434da60ef
@@ -1,5 +1,8 @@
|
||||
#include "cache_context.h"
|
||||
#include "sequence.h"
|
||||
#include "sequence_store.h"
|
||||
#include "../shared/poincare_helpers.h"
|
||||
#include <poincare/serialization_helper.h>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Poincare;
|
||||
@@ -16,13 +19,30 @@ CacheContext<T>::CacheContext(Context * parentContext) :
|
||||
template<typename T>
|
||||
const Expression CacheContext<T>::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<Symbol &>(static_cast<const Symbol &>(symbol));
|
||||
return Float<T>::Builder(m_values[nameIndexForSymbol(s)][rankIndexForSymbol(s)]);
|
||||
if (strcmp(symbol.name()+1, "(n)") == 0 || strcmp(symbol.name()+1, "(n+1)") == 0) {
|
||||
return Float<T>::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<T>::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<T>();
|
||||
return Float<T>::Builder(seq.valueAtRank<T>(x, m_sequenceContext));
|
||||
}
|
||||
}
|
||||
return ContextWithParent::expressionForSymbolAbstract(symbol, clone);
|
||||
}
|
||||
@@ -34,7 +54,7 @@ void CacheContext<T>::setValueForSymbol(T value, const Poincare::Symbol & symbol
|
||||
|
||||
template<typename T>
|
||||
int CacheContext<T>::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<T>::nameIndexForSymbol(const Poincare::Symbol & symbol) {
|
||||
|
||||
template<typename T>
|
||||
int CacheContext<T>::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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -118,7 +118,30 @@ T Sequence::templatedApproximateAtAbscissa(T x, SequenceContext * sqctx) const {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<T>(sequenceIndex) > n || sqctx->independantSequenceRank<T>(sequenceIndex) < 0) {
|
||||
// Reset cache indexes and cache values
|
||||
sqctx->setIndependantSequenceRank<T>(-1, sequenceIndex);
|
||||
for (int i = 0 ; i < MaxRecurrenceDepth+1; i++) {
|
||||
sqctx->setIndependantSequenceValue(NAN, sequenceIndex, i);
|
||||
}
|
||||
}
|
||||
|
||||
while(sqctx->independantSequenceRank<T>(sequenceIndex) < n) {
|
||||
sqctx->stepSequenceAtIndex<T>(sequenceIndex);
|
||||
}
|
||||
/* In case we have sqctx->independantSequenceRank<T>(sequenceIndex) = n, we can return the
|
||||
* value */
|
||||
T value = sqctx->independantSequenceValue<T>(sequenceIndex, 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<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];
|
||||
|
||||
/* 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<T>(sequenceIndex);
|
||||
for (int i = 0; i < MaxNumberOfSequences; i++) {
|
||||
for (int j = 0; j < MaxRecurrenceDepth+1; j++) {
|
||||
values[i][j] = sqctx->valueOfSequenceAtPreviousRank<T>(i, j);
|
||||
if (sequenceIndex != -1 && sqctx->independantSequenceRank<T>(i) != independantRank) {
|
||||
int offset = independantRank - sqctx->independantSequenceRank<T>(i);
|
||||
if (offset != 0) {
|
||||
for (int j = MaxRecurrenceDepth; j >= 0; j--) {
|
||||
values[i][j] = j-offset < 0 ? NAN : sqctx->independantSequenceValue<T>(i, j-offset);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int j = 0; j < MaxRecurrenceDepth+1; j++) {
|
||||
values[i][j] = sequenceIndex != -1 ? sqctx->independantSequenceValue<T>(i, j) : sqctx->valueOfSequenceAtPreviousRank<T>(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<T>(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<T>(firstInitialConditionExpressionReduced(sqctx), sqctx);
|
||||
return PoincareHelpers::ApproximateWithValueForSymbol(firstInitialConditionExpressionReduced(sqctx), unknownN, (T)NAN, &ctx);
|
||||
}
|
||||
if (n == initialRank()+1) {
|
||||
return PoincareHelpers::ApproximateToScalar<T>(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>(double, SequenceContext*) const;
|
||||
template float Sequence::templatedApproximateAtAbscissa<float>(float, SequenceContext*) const;
|
||||
template double Sequence::approximateToNextRank<double>(int, SequenceContext*) const;
|
||||
template float Sequence::approximateToNextRank<float>(int, SequenceContext*) const;
|
||||
template double Sequence::approximateToNextRank<double>(int, SequenceContext*, int) const;
|
||||
template float Sequence::approximateToNextRank<float>(int, SequenceContext*, int) const;
|
||||
template double Sequence::valueAtRank<double>(int, SequenceContext *);
|
||||
template float Sequence::valueAtRank<float>(int, SequenceContext *);
|
||||
|
||||
}
|
||||
|
||||
@@ -65,7 +65,8 @@ public:
|
||||
Poincare::Coordinate2D<double> evaluateXYAtParameter(double x, Poincare::Context * context) const override {
|
||||
return Poincare::Coordinate2D<double>(x,templatedApproximateAtAbscissa(x, static_cast<SequenceContext *>(context)));
|
||||
}
|
||||
template<typename T> T approximateToNextRank(int n, SequenceContext * sqctx) const;
|
||||
template<typename T> T approximateToNextRank(int n, SequenceContext * sqctx, int sequenceIndex = -1) const;
|
||||
template<typename T> 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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "sequence_context.h"
|
||||
#include "sequence_store.h"
|
||||
#include "cache_context.h"
|
||||
#include "../shared/poincare_helpers.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace Poincare;
|
||||
@@ -9,63 +11,104 @@ namespace Sequence {
|
||||
|
||||
template<typename T>
|
||||
TemplatedSequenceContext<T>::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<typename T>
|
||||
T TemplatedSequenceContext<T>::valueOfSequenceAtPreviousRank(int sequenceIndex, int rank) const {
|
||||
return m_values[sequenceIndex][rank];
|
||||
return m_commonRankValues[sequenceIndex][rank];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void TemplatedSequenceContext<T>::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<typename T>
|
||||
bool TemplatedSequenceContext<T>::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<typename T>
|
||||
void TemplatedSequenceContext<T>::step(SequenceStore * sequenceStore, SequenceContext * sqctx) {
|
||||
/* Shift values */
|
||||
for (int i = 0; i < MaxNumberOfSequences; i++) {
|
||||
void TemplatedSequenceContext<T>::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<T*>(&m_commonRankValues);
|
||||
rank = m_commonRank;
|
||||
} else {
|
||||
start = sequenceIndex;
|
||||
stop = sequenceIndex + 1;
|
||||
sequenceArray = reinterpret_cast<T*>(&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<T>(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<T>(rank, sqctx, sequenceIndex) : NAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,5 +116,7 @@ void TemplatedSequenceContext<T>::step(SequenceStore * sequenceStore, SequenceCo
|
||||
|
||||
template class TemplatedSequenceContext<float>;
|
||||
template class TemplatedSequenceContext<double>;
|
||||
template void * SequenceContext::helper<float>();
|
||||
template void * SequenceContext::helper<double>();
|
||||
|
||||
}
|
||||
|
||||
@@ -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<typename T> 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<typename T> T valueOfSequenceAtPreviousRank(int sequenceIndex, int rank) {
|
||||
return static_cast<TemplatedSequenceContext<T>*>(helper<T>())->valueOfSequenceAtPreviousRank(sequenceIndex, rank);
|
||||
}
|
||||
|
||||
void resetCache() {
|
||||
m_floatSequenceContext.resetCache();
|
||||
m_doubleSequenceContext.resetCache();
|
||||
}
|
||||
|
||||
template<typename T> 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<TemplatedSequenceContext<T>*>(helper<T>())->iterateUntilRank(n, m_sequenceStore, this);
|
||||
}
|
||||
|
||||
template<typename T> int independantSequenceRank(int sequenceIndex) {
|
||||
return static_cast<TemplatedSequenceContext<T>*>(helper<T>())->independantSequenceRank(sequenceIndex);
|
||||
}
|
||||
|
||||
template<typename T> void setIndependantSequenceRank(int rank, int sequenceIndex) {
|
||||
static_cast<TemplatedSequenceContext<T>*>(helper<T>())->setIndependantSequenceRank(rank, sequenceIndex);
|
||||
}
|
||||
|
||||
template<typename T> T independantSequenceValue(int sequenceIndex, int depth) {
|
||||
return static_cast<TemplatedSequenceContext<T>*>(helper<T>())->independantSequenceValue(sequenceIndex, depth);
|
||||
}
|
||||
|
||||
template<typename T> void setIndependantSequenceValue(T value, int sequenceIndex, int depth) {
|
||||
static_cast<TemplatedSequenceContext<T>*>(helper<T>())->setIndependantSequenceValue(value, sequenceIndex, depth);
|
||||
}
|
||||
|
||||
template<typename T> void stepSequenceAtIndex(int sequenceIndex) {
|
||||
static_cast<TemplatedSequenceContext<T>*>(helper<T>())->step(this, sequenceIndex);
|
||||
}
|
||||
SequenceStore * sequenceStore() { return m_sequenceStore; }
|
||||
private:
|
||||
TemplatedSequenceContext<float> m_floatSequenceContext;
|
||||
TemplatedSequenceContext<double> m_doubleSequenceContext;
|
||||
SequenceStore * m_sequenceStore;
|
||||
template<typename T> void * helper() { return sizeof(T) == sizeof(float) ? (void*) &m_floatSequenceContext : (void*) &m_doubleSequenceContext; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user