mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-19 22:00:28 +01:00
[sequence] Make Sequence inherit from StorageFunction
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#include "sequence.h"
|
||||
#include "sequence_store.h"
|
||||
#include "cache_context.h"
|
||||
#include "sequence_store.h"
|
||||
#include <poincare/layout_helper.h>
|
||||
#include <poincare/code_point_layout.h>
|
||||
#include <poincare/vertical_offset_layout.h>
|
||||
@@ -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<typename T>
|
||||
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<T>(n)) {
|
||||
return sqctx->valueOfSequenceAtPreviousRank<T>(sequenceIndex, 0);
|
||||
}
|
||||
@@ -237,9 +119,10 @@ T Sequence::templatedApproximateAtAbscissa(T x, SequenceContext * sqctx) const {
|
||||
|
||||
template<typename T>
|
||||
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<T> ctx = CacheContext<T>(sqctx);
|
||||
T un = sqctx->valueOfSequenceAtPreviousRank<T>(0, 0);
|
||||
T unm1 = sqctx->valueOfSequenceAtPreviousRank<T>(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<T>(firstInitialConditionExpression(sqctx), *sqctx);
|
||||
if (n == initialRank()) {
|
||||
return PoincareHelpers::ApproximateToScalar<T>(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<T>(firstInitialConditionExpression(sqctx), *sqctx);
|
||||
if (n == initialRank()) {
|
||||
return PoincareHelpers::ApproximateToScalar<T>(firstInitialConditionExpressionReduced(sqctx), *sqctx);
|
||||
}
|
||||
if (n == m_initialRank+1) {
|
||||
return PoincareHelpers::ApproximateToScalar<T>(secondInitialConditionExpression(sqctx), *sqctx);
|
||||
if (n == initialRank()+1) {
|
||||
return PoincareHelpers::ApproximateToScalar<T>(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<SequenceRecordData *>(const_cast<void *>(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<const Sequence *>(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<const Sequence *>(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<const Sequence *>(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<const Sequence *>(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<const Sequence *>(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>(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;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user