From 3c3ddec0dbf5153e8b3699a7ee2124528e83cc8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milie=20Feral?= Date: Tue, 26 Feb 2019 11:45:38 +0100 Subject: [PATCH] [sequence] Make Sequence inherit from StorageFunction --- apps/sequence/sequence.cpp | 321 ++++++++++++++++--------------------- apps/sequence/sequence.h | 145 ++++++++++++----- 2 files changed, 249 insertions(+), 217 deletions(-) diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index e108f1b25..0cb52c3a9 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -1,6 +1,6 @@ #include "sequence.h" -#include "sequence_store.h" #include "cache_context.h" +#include "sequence_store.h" #include #include #include @@ -14,72 +14,51 @@ using namespace Poincare; namespace Sequence { -Sequence::Sequence(const char * text, KDColor color) : - Function(text, color), - m_type(Type::Explicit), - m_firstInitialConditionText(), - m_secondInitialConditionText(), - m_firstInitialConditionExpression(), - m_secondInitialConditionExpression(), - m_firstInitialConditionLayout(), - m_secondInitialConditionLayout(), - m_nameLayout(), - m_definitionName(), - m_firstInitialConditionName(), - m_secondInitialConditionName(), - m_initialRank(0) -{ +void Sequence::tidy() { + m_definitionHandle.tidyName(); + StorageFunction::tidy(); // m_definitionName.tidy() + m_firstInitialConditionHandle.tidy(); + m_firstInitialConditionHandle.tidyName(); + m_secondInitialConditionHandle.tidy(); + m_secondInitialConditionHandle.tidyName(); + m_nameLayout = Layout(); } -uint32_t Sequence::checksum() { - constexpr size_t dataSize = k_dataLengthInBytes/sizeof(char); - char data[dataSize] = {}; - strlcpy(data, text(), dataSize); - strlcpy(data+TextField::maxBufferSize(), firstInitialConditionText(), dataSize - TextField::maxBufferSize()); - strlcpy(data+2*TextField::maxBufferSize(), secondInitialConditionText(), dataSize - 2*TextField::maxBufferSize()); - int * intAdress = (int *)(&data[3*TextField::maxBufferSize()]); - *intAdress = m_initialRank; - data[k_dataLengthInBytes-3] = (char)m_type; - data[k_dataLengthInBytes-2] = name()!= nullptr ? name()[0] : 0; - data[k_dataLengthInBytes-1] = (char)(isActive() ? 1 : 0); - return Ion::crc32((uint32_t *)data, k_dataLengthInBytes/sizeof(uint32_t)); +Sequence::Type Sequence::type() const { + return recordData()->type(); } -const char * Sequence::firstInitialConditionText() { - return m_firstInitialConditionText; +int Sequence::initialRank() const { + return recordData()->initialRank(); } -const char * Sequence::secondInitialConditionText() { - return m_secondInitialConditionText; -} - -Sequence::Type Sequence::type() { - return m_type; -} - -void Sequence::setType(Type type) { - if (m_type == Type::Explicit) { +void Sequence::setType(Type t) { + if (t == type()) { + return; + } + if (type() == Type::Explicit) { setInitialRank(0); } - m_type = type; + recordData()->setType(t); tidy(); /* Reset all contents */ - switch (m_type) { + switch (t) { case Type::Explicit: setContent(""); break; case Type::SingleRecurrence: { char ex[5] = "u(n)"; - ex[0] = name()[0]; + ex[0] = fullName()[0]; setContent(ex); break; } case Type::DoubleRecurrence: { char ex[12] = "u(n+1)+u(n)"; - ex[0] = name()[0]; - ex[7] = name()[0]; + char name = fullName()[0]; + ex[0] = name; + ex[7] = name; setContent(ex); break; } @@ -89,146 +68,49 @@ void Sequence::setType(Type type) { } void Sequence::setInitialRank(int rank) { - m_initialRank = rank; - m_firstInitialConditionName = Layout(); - m_secondInitialConditionName = Layout(); -} - -Poincare::Expression Sequence::firstInitialConditionExpression(Context * context) const { - if (m_firstInitialConditionExpression.isUninitialized()) { - m_firstInitialConditionExpression = PoincareHelpers::ParseAndSimplify(m_firstInitialConditionText, *context); - } - return m_firstInitialConditionExpression; -} - -Poincare::Expression Sequence::secondInitialConditionExpression(Context * context) const { - if (m_secondInitialConditionExpression.isUninitialized()) { - m_secondInitialConditionExpression = PoincareHelpers::ParseAndSimplify(m_secondInitialConditionText, *context); - } - return m_secondInitialConditionExpression; -} - -Poincare::Layout Sequence::firstInitialConditionLayout() { - if (m_firstInitialConditionLayout.isUninitialized()) { - Expression nonSimplifedExpression = Expression::Parse(m_firstInitialConditionText); - if (!nonSimplifedExpression.isUninitialized()) { - m_firstInitialConditionLayout = PoincareHelpers::CreateLayout(nonSimplifedExpression); - } - } - return m_firstInitialConditionLayout; -} - -Poincare::Layout Sequence::secondInitialConditionLayout() { - if (m_secondInitialConditionLayout.isUninitialized()) { - Expression nonSimplifedExpression = Expression::Parse(m_secondInitialConditionText); - if (!nonSimplifedExpression.isUninitialized()) { - m_secondInitialConditionLayout = PoincareHelpers::CreateLayout(nonSimplifedExpression); - } - } - return m_secondInitialConditionLayout; -} - -void Sequence::setFirstInitialConditionContent(const char * c) { - strlcpy(m_firstInitialConditionText, c, sizeof(m_firstInitialConditionText)); - m_firstInitialConditionExpression = Expression(); - m_firstInitialConditionLayout = Layout(); -} - -void Sequence::setSecondInitialConditionContent(const char * c) { - strlcpy(m_secondInitialConditionText, c, sizeof(m_secondInitialConditionText)); - m_secondInitialConditionExpression = Expression(); - m_secondInitialConditionLayout = Layout(); -} - -int Sequence::numberOfElements() { - return (int)m_type + 1; + recordData()->setInitialRank(rank); + m_firstInitialConditionHandle.tidyName(); + m_secondInitialConditionHandle.tidyName(); } Poincare::Layout Sequence::nameLayout() { if (m_nameLayout.isUninitialized()) { m_nameLayout = HorizontalLayout::Builder( - CodePointLayout::Builder(name()[0], KDFont::SmallFont), - VerticalOffsetLayout::Builder(CodePointLayout::Builder('n', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Subscript) + CodePointLayout::Builder(fullName()[0], KDFont::SmallFont), + VerticalOffsetLayout::Builder(CodePointLayout::Builder(symbol(), KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Subscript) ); } return m_nameLayout; } -Poincare::Layout Sequence::definitionName() { - if (m_definitionName.isUninitialized()) { - if (m_type == Type::Explicit) { - m_definitionName = HorizontalLayout::Builder( - CodePointLayout::Builder(name()[0], k_layoutFont), - VerticalOffsetLayout::Builder(LayoutHelper::String("n", 1, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); - } else if (m_type == Type::SingleRecurrence) { - m_definitionName = HorizontalLayout::Builder( - CodePointLayout::Builder(name()[0], k_layoutFont), - VerticalOffsetLayout::Builder(LayoutHelper::String("n+1", 3, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); - } else { - assert(m_type == Type::DoubleRecurrence); - m_definitionName = HorizontalLayout::Builder( - CodePointLayout::Builder(name()[0], k_layoutFont), - VerticalOffsetLayout::Builder(LayoutHelper::String("n+2", 3, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); - } - } - return m_definitionName; -} - -Poincare::Layout Sequence::firstInitialConditionName() { - char buffer[k_initialRankNumberOfDigits+1]; - Integer(m_initialRank).serialize(buffer, k_initialRankNumberOfDigits+1); - if (m_firstInitialConditionName.isUninitialized() - && (m_type == Type::SingleRecurrence - || m_type == Type::DoubleRecurrence)) - { - Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), k_layoutFont); - m_firstInitialConditionName = HorizontalLayout::Builder( - CodePointLayout::Builder(name()[0], k_layoutFont), - VerticalOffsetLayout::Builder(indexLayout, VerticalOffsetLayoutNode::Type::Subscript)); - } - return m_firstInitialConditionName; -} - -Poincare::Layout Sequence::secondInitialConditionName() { - char buffer[k_initialRankNumberOfDigits+1]; - Integer(m_initialRank+1).serialize(buffer, k_initialRankNumberOfDigits+1); - if (m_secondInitialConditionName.isUninitialized()) { - if (m_type == Type::DoubleRecurrence) { - Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), k_layoutFont); - m_secondInitialConditionName = HorizontalLayout::Builder( - CodePointLayout::Builder(name()[0], k_layoutFont), - VerticalOffsetLayout::Builder(indexLayout, VerticalOffsetLayoutNode::Type::Subscript)); - } - } - return m_secondInitialConditionName; -} - bool Sequence::isDefined() { - switch (m_type) { + SequenceRecordData * data = recordData(); + switch (type()) { case Type::Explicit: - return strlen(text()) != 0; + return StorageFunction::isDefined(); case Type::SingleRecurrence: - return strlen(text()) != 0 && strlen(firstInitialConditionText()) != 0; + return StorageFunction::isDefined() && data->firstInitialConditionSize() > 0; default: - return strlen(text()) != 0 && strlen(firstInitialConditionText()) != 0 && strlen(secondInitialConditionText()) != 0; + return StorageFunction::isDefined() && data->firstInitialConditionSize() > 0 && data->secondInitialConditionSize() > 0; } } bool Sequence::isEmpty() { - switch (m_type) { + SequenceRecordData * data = recordData(); + switch (type()) { case Type::Explicit: - return Function::isEmpty(); + return StorageFunction::isEmpty(); case Type::SingleRecurrence: - return Function::isEmpty() && strlen(m_firstInitialConditionText) == 0; + return StorageFunction::isEmpty() && data->firstInitialConditionSize() == 0; default: - return Function::isEmpty() && strlen(m_firstInitialConditionText) == 0 && strlen(m_secondInitialConditionText) == 0; + return StorageFunction::isEmpty() && data->firstInitialConditionSize() == 0 && data->secondInitialConditionSize() == 0; } } template T Sequence::templatedApproximateAtAbscissa(T x, SequenceContext * sqctx) const { T n = std::round(x); - int sequenceIndex = name()[0] == SequenceStore::k_sequenceNames[0][0] ? 0 : 1; + int sequenceIndex = fullName()[0] == SequenceStore::k_sequenceNames[0][0] ? 0 : 1; if (sqctx->iterateUntilRank(n)) { return sqctx->valueOfSequenceAtPreviousRank(sequenceIndex, 0); } @@ -237,9 +119,10 @@ T Sequence::templatedApproximateAtAbscissa(T x, SequenceContext * sqctx) const { template T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { - if (n < m_initialRank || n < 0) { + if (n < initialRank() || n < 0) { return NAN; } + char symb[] = {symbol(), 0}; CacheContext ctx = CacheContext(sqctx); T un = sqctx->valueOfSequenceAtPreviousRank(0, 0); T unm1 = sqctx->valueOfSequenceAtPreviousRank(0, 1); @@ -251,37 +134,37 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { Poincare::Symbol vn1Symbol = Symbol::Builder("v(n+1)", 6); Poincare::Symbol unSymbol = Symbol::Builder("u(n)", 4); Poincare::Symbol un1Symbol = Symbol::Builder("u(n+1)", 6); - switch (m_type) { + switch (type()) { case Type::Explicit: { ctx.setValueForSymbol(un, unSymbol); ctx.setValueForSymbol(vn, vnSymbol); - return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)n, ctx); + return PoincareHelpers::ApproximateWithValueForSymbol(expressionReduced(sqctx), symb, (T)n, ctx); } case Type::SingleRecurrence: { - if (n == m_initialRank) { - return PoincareHelpers::ApproximateToScalar(firstInitialConditionExpression(sqctx), *sqctx); + if (n == initialRank()) { + return PoincareHelpers::ApproximateToScalar(firstInitialConditionExpressionReduced(sqctx), *sqctx); } ctx.setValueForSymbol(un, un1Symbol); ctx.setValueForSymbol(unm1, unSymbol); ctx.setValueForSymbol(vn, vn1Symbol); ctx.setValueForSymbol(vnm1, vnSymbol); - return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)(n-1), ctx); + return PoincareHelpers::ApproximateWithValueForSymbol(expressionReduced(sqctx), symb, (T)(n-1), ctx); } default: { - if (n == m_initialRank) { - return PoincareHelpers::ApproximateToScalar(firstInitialConditionExpression(sqctx), *sqctx); + if (n == initialRank()) { + return PoincareHelpers::ApproximateToScalar(firstInitialConditionExpressionReduced(sqctx), *sqctx); } - if (n == m_initialRank+1) { - return PoincareHelpers::ApproximateToScalar(secondInitialConditionExpression(sqctx), *sqctx); + if (n == initialRank()+1) { + return PoincareHelpers::ApproximateToScalar(secondInitialConditionExpressionReduced(sqctx), *sqctx); } ctx.setValueForSymbol(unm1, un1Symbol); ctx.setValueForSymbol(unm2, unSymbol); ctx.setValueForSymbol(vnm1, vn1Symbol); ctx.setValueForSymbol(vnm2, vnSymbol); - return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)(n-2), ctx); + return PoincareHelpers::ApproximateWithValueForSymbol(expressionReduced(sqctx), symb, (T)(n-2), ctx); } } } @@ -302,20 +185,100 @@ double Sequence::sumBetweenBounds(double start, double end, Context * context) c return result; } -void Sequence::tidy() { - Function::tidy(); - m_firstInitialConditionLayout = Layout(); - m_secondInitialConditionLayout = Layout(); - m_firstInitialConditionExpression = Expression(); - m_secondInitialConditionExpression = Expression(); - m_nameLayout = Layout(); - m_definitionName = Layout(); - m_firstInitialConditionName = Layout(); - m_secondInitialConditionName = Layout(); +Sequence::SequenceRecordData * Sequence::recordData() const { + assert(!isNull()); + Ion::Storage::Record::Data d = value(); + return reinterpret_cast(const_cast(d.buffer)); +} + +/* Sequence Handle */ + +Poincare::Layout Sequence::SequenceHandle::name(Sequence * sequence) { + if (m_name.isUninitialized()) { + buildName(sequence); + } + return m_name; +} + +/* Definition Handle*/ + +void * Sequence::DefinitionHandle::expressionAddress(const Ion::Storage::Record * record) const { + return (char *)record->value().buffer+sizeof(SequenceRecordData); +} + +size_t Sequence::DefinitionHandle::expressionSize(const Ion::Storage::Record * record) const { + Ion::Storage::Record::Data data = record->value(); + SequenceRecordData * dataBuffer = static_cast(record)->recordData(); + return data.size-sizeof(SequenceRecordData) - dataBuffer->firstInitialConditionSize() - dataBuffer->secondInitialConditionSize(); +} + +void Sequence::DefinitionHandle::buildName(Sequence * sequence) { + char name = sequence->fullName()[0]; + if (sequence->type() == Type::Explicit) { + m_name = HorizontalLayout::Builder( + CodePointLayout::Builder(name, k_layoutFont), + VerticalOffsetLayout::Builder(LayoutHelper::String("n", 1, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); + } else if (sequence->type() == Type::SingleRecurrence) { + m_name = HorizontalLayout::Builder( + CodePointLayout::Builder(name, k_layoutFont), + VerticalOffsetLayout::Builder(LayoutHelper::String("n+1", 3, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); + } else { + assert(sequence->type() == Type::DoubleRecurrence); + m_name = HorizontalLayout::Builder( + CodePointLayout::Builder(name, k_layoutFont), + VerticalOffsetLayout::Builder(LayoutHelper::String("n+2", 3, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); + } +} + +/* First Initial Condition Handle*/ + +void * Sequence::FirstInitialConditionHandle::expressionAddress(const Ion::Storage::Record * record) const { + Ion::Storage::Record::Data data = record->value(); + SequenceRecordData * dataBuffer = static_cast(record)->recordData(); + size_t offset = data.size - dataBuffer->firstInitialConditionSize() - dataBuffer->secondInitialConditionSize(); + return (char *)data.buffer+offset; +} + +size_t Sequence::FirstInitialConditionHandle::expressionSize(const Ion::Storage::Record * record) const { + return static_cast(record)->recordData()->firstInitialConditionSize(); +} + +void Sequence::FirstInitialConditionHandle::buildName(Sequence * sequence) { + assert(sequence->type() == Type::SingleRecurrence || sequence->type() == Type::DoubleRecurrence); + char buffer[k_initialRankNumberOfDigits+1]; + Integer(sequence->initialRank()).serialize(buffer, k_initialRankNumberOfDigits+1); + Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), k_layoutFont); + m_name = HorizontalLayout::Builder( + CodePointLayout::Builder(sequence->fullName()[0], k_layoutFont), + VerticalOffsetLayout::Builder(indexLayout, VerticalOffsetLayoutNode::Type::Subscript)); +} + +/* Second Initial Condition Handle*/ + +void * Sequence::SecondInitialConditionHandle::expressionAddress(const Ion::Storage::Record * record) const { + Ion::Storage::Record::Data data = record->value(); + SequenceRecordData * dataBuffer = static_cast(record)->recordData(); + size_t offset = data.size - dataBuffer->secondInitialConditionSize(); + return (char *)data.buffer+offset; +} + +size_t Sequence::SecondInitialConditionHandle::expressionSize(const Ion::Storage::Record * record) const { + return static_cast(record)->recordData()->secondInitialConditionSize(); +} + +void Sequence::SecondInitialConditionHandle::buildName(Sequence * sequence) { + assert(sequence->type() == Type::DoubleRecurrence); + char buffer[k_initialRankNumberOfDigits+1]; + Integer(sequence->initialRank()+1).serialize(buffer, k_initialRankNumberOfDigits+1); + Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), k_layoutFont); + m_name = HorizontalLayout::Builder( + CodePointLayout::Builder(sequence->fullName()[0], k_layoutFont), + VerticalOffsetLayout::Builder(indexLayout, VerticalOffsetLayoutNode::Type::Subscript)); } 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; + } diff --git a/apps/sequence/sequence.h b/apps/sequence/sequence.h index bd9deb493..e195b2fae 100644 --- a/apps/sequence/sequence.h +++ b/apps/sequence/sequence.h @@ -1,45 +1,62 @@ #ifndef SEQUENCE_SEQUENCE_H #define SEQUENCE_SEQUENCE_H -#include "../shared/function.h" +#include "../shared/storage_function.h" #include "sequence_context.h" #include namespace Sequence { -class Sequence : public Shared::Function { +/* WARNING: after calling setType, setInitialRank, setContent, setFirstInitialConditionContent + * or setSecondInitialConditionContent, the sequence context needs to + * invalidate the cache because the sequences evaluations might have changed. */ + +class Sequence : public Shared::StorageFunction { +friend class SequenceStore; public: + static constexpr char extension[] = "seq"; // TODO: store this elsewhere? + + enum class Type { Explicit = 0, SingleRecurrence = 1, DoubleRecurrence = 2 }; - Sequence(const char * text = nullptr, KDColor color = KDColorBlack); - uint32_t checksum() override; - Type type(); - int initialRank() const { - return m_initialRank; - } - const char * firstInitialConditionText(); - const char * secondInitialConditionText(); - Poincare::Expression firstInitialConditionExpression(Poincare::Context * context) const; - Poincare::Expression secondInitialConditionExpression(Poincare::Context * context) const; - Poincare::Layout firstInitialConditionLayout(); - Poincare::Layout secondInitialConditionLayout(); - /* WARNING: after calling setType, setContent, setFirstInitialConditionContent - * or setSecondInitialConditionContent, the sequence context needs to - * invalidate the cache because the sequences evaluations might have changed. */ + Sequence(Ion::Storage::Record record = Record()) : + StorageFunction(record), + m_nameLayout() {} + static char Symbol() { return 'n'; } + char symbol() const override { return Symbol(); } + void tidy() override; + // MetaData getters + Type type() const; + int initialRank() const; + // MetaData setters void setType(Type type); void setInitialRank(int rank); - void setFirstInitialConditionContent(const char * c); - void setSecondInitialConditionContent(const char * c); - int numberOfElements(); + // Definition + Poincare::Layout definitionName() { return m_definitionHandle.name(this); } + // First initial condition + Poincare::Layout firstInitialConditionName() { return m_firstInitialConditionHandle.name(this); } + void firstInitialConditionText(char * buffer, size_t bufferSize) const { return m_firstInitialConditionHandle.text(this, buffer, bufferSize); } + Poincare::Expression firstInitialConditionExpressionReduced(Poincare::Context * context) const { return m_firstInitialConditionHandle.expressionReduced(this, context); } + Poincare::Expression firstInitialConditionExpressionClone() const { return m_firstInitialConditionHandle.expressionClone(this); } + Poincare::Layout firstInitialConditionLayout() { return m_firstInitialConditionHandle.layout(this); } + Ion::Storage::Record::ErrorStatus setFirstInitialConditionContent(const char * c) { return m_firstInitialConditionHandle.setContent(this, c); } + // Second initial condition + Poincare::Layout secondInitialConditionName() { return m_secondInitialConditionHandle.name(this); } + void secondInitialConditionText(char * buffer, size_t bufferSize) const { return m_secondInitialConditionHandle.text(this, buffer, bufferSize); } + Poincare::Expression secondInitialConditionExpressionReduced(Poincare::Context * context) const { return m_secondInitialConditionHandle.expressionReduced(this, context); } + Poincare::Expression secondInitialConditionExpressionClone() const { return m_secondInitialConditionHandle.expressionClone(this); } + Poincare::Layout secondInitialConditionLayout() { return m_secondInitialConditionHandle.layout(this); } + Ion::Storage::Record::ErrorStatus setSecondInitialConditionContent(const char * c) { return m_secondInitialConditionHandle.setContent(this, c); } + + // Sequence properties + int numberOfElements() { return (int)type() + 1; } Poincare::Layout nameLayout(); - Poincare::Layout definitionName(); - Poincare::Layout firstInitialConditionName(); - Poincare::Layout secondInitialConditionName(); bool isDefined() override; bool isEmpty() override; + // Approximation float evaluateAtAbscissa(float x, Poincare::Context * context) const override { return templatedApproximateAtAbscissa(x, static_cast(context)); } @@ -47,28 +64,80 @@ public: return templatedApproximateAtAbscissa(x, static_cast(context)); } template T approximateToNextRank(int n, SequenceContext * sqctx) const; + // Integral double sumBetweenBounds(double start, double end, Poincare::Context * context) const override; - void tidy() override; + constexpr static int k_initialRankNumberOfDigits = 3; // m_initialRank is capped by 999 private: constexpr static const KDFont * k_layoutFont = KDFont::LargeFont; constexpr static double k_maxNumberOfTermsInSum = 100000.0; - constexpr static size_t k_dataLengthInBytes = (3*TextField::maxBufferSize()+3)*sizeof(char)+sizeof(int)+1; - static_assert((k_dataLengthInBytes & 0x3) == 0, "The sequence data size is not a multiple of 4 bytes (cannot compute crc)"); // Assert that dataLengthInBytes is a multiple of 4 - const char * symbol() const override { return "n"; } + + class SequenceRecordData : public FunctionRecordData { + public: + SequenceRecordData(KDColor color) : + FunctionRecordData(color), + m_type(Type::Explicit), + m_initialRank(0), + m_firstInitialConditionSize(0), + m_secondInitialConditionSize(0) + {} + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + int initialRank() const { return m_initialRank; } + void setInitialRank(int initialRank) { m_initialRank = initialRank; } + size_t firstInitialConditionSize() const { return m_firstInitialConditionSize; } + void setFirstInitialConditionSize(size_t firstInitialConditionSize) { m_firstInitialConditionSize = firstInitialConditionSize; } + size_t secondInitialConditionSize() const { return m_secondInitialConditionSize; } + void setSecondInitialConditionSize(size_t secondInitialConditionSize) { m_secondInitialConditionSize = secondInitialConditionSize; } + private: + Type m_type; + int m_initialRank; + size_t m_firstInitialConditionSize; + size_t m_secondInitialConditionSize; + }; + + class SequenceHandle : public Shared::ExpressionModelHandle { + public: + SequenceHandle() : Shared::ExpressionModelHandle(), m_name() {} + void tidyName() { m_name = Poincare::Layout(); } + virtual Poincare::Layout name(Sequence * sequence); + protected: + virtual void buildName(Sequence * sequence) = 0; + Poincare::Layout m_name; + }; + + class DefinitionHandle : public SequenceHandle { + public: + void * expressionAddress(const Ion::Storage::Record * record) const override; + private: + size_t expressionSize(const Ion::Storage::Record * record) const override; + void buildName(Sequence * sequence) override; + }; + + class FirstInitialConditionHandle : public SequenceHandle { + public: + void * expressionAddress(const Ion::Storage::Record * record) const override; + private: + size_t expressionSize(const Ion::Storage::Record * record) const override; + void buildName(Sequence * sequence) override; + }; + + class SecondInitialConditionHandle : public SequenceHandle { + public: + void * expressionAddress(const Ion::Storage::Record * record) const override; + private: + size_t expressionSize(const Ion::Storage::Record * record) const override; + void buildName(Sequence * sequence) override; + }; + template T templatedApproximateAtAbscissa(T x, SequenceContext * sqctx) const; - Type m_type; - char m_firstInitialConditionText[TextField::maxBufferSize()]; - char m_secondInitialConditionText[TextField::maxBufferSize()]; - mutable Poincare::Expression m_firstInitialConditionExpression; - mutable Poincare::Expression m_secondInitialConditionExpression; - Poincare::Layout m_firstInitialConditionLayout; - Poincare::Layout m_secondInitialConditionLayout; + size_t metaDataSize() const override { return sizeof(SequenceRecordData); } + const Shared::ExpressionModelHandle * handle() const override { return &m_definitionHandle; } + SequenceRecordData * recordData() const; + DefinitionHandle m_definitionHandle; + FirstInitialConditionHandle m_firstInitialConditionHandle; + SecondInitialConditionHandle m_secondInitialConditionHandle; Poincare::Layout m_nameLayout; - Poincare::Layout m_definitionName; - Poincare::Layout m_firstInitialConditionName; - Poincare::Layout m_secondInitialConditionName; - int m_initialRank; }; }