diff --git a/src/backend/core/AbstractColumn.h b/src/backend/core/AbstractColumn.h --- a/src/backend/core/AbstractColumn.h +++ b/src/backend/core/AbstractColumn.h @@ -91,6 +91,13 @@ // QMatrix // etc. }; + enum Properties { + No = 0x00, + Constant = 0x01, + MonotonicIncreasing = 0x02, // prev_value >= value for all values in column + MonotonicDecreasing = 0x04 // prev_value <= value for all values in column + // add new values with next bit set (0x08) + }; struct ColumnStatistics { ColumnStatistics() { @@ -187,6 +194,7 @@ virtual int integerAt(int row) const; virtual void setIntegerAt(int row, int new_value); virtual void replaceInteger(int first, const QVector& new_values); + virtual int properties() const; signals: void plotDesignationAboutToChange(const AbstractColumn* source); diff --git a/src/backend/core/AbstractColumn.cpp b/src/backend/core/AbstractColumn.cpp --- a/src/backend/core/AbstractColumn.cpp +++ b/src/backend/core/AbstractColumn.cpp @@ -541,6 +541,16 @@ Q_UNUSED(first) Q_UNUSED(new_values) } +/** + * @brief AbstractColumn::properties + * Returns the properties hold by this column (no, monotonic increasing, monotonic decreasing,...) + * Will be used in XYCurve to improve the search velocity for the y value for a specific x value + * @return + */ +int AbstractColumn::properties() const { + return AbstractColumn::Properties::No; +} + /**********************************************************************/ double AbstractColumn::minimum(int count) const { Q_UNUSED(count); diff --git a/src/backend/core/column/Column.h b/src/backend/core/column/Column.h --- a/src/backend/core/column/Column.h +++ b/src/backend/core/column/Column.h @@ -104,6 +104,7 @@ int integerAt(int) const override; void setIntegerAt(int, int) override; void replaceInteger(int, const QVector&) override; + int properties() const override; double maximum(int count = 0) const override; double minimum(int count = 0) const override; diff --git a/src/backend/core/column/Column.cpp b/src/backend/core/column/Column.cpp --- a/src/backend/core/column/Column.cpp +++ b/src/backend/core/column/Column.cpp @@ -243,6 +243,7 @@ d->statisticsAvailable = false; d->hasValuesAvailable = false; + d->propertiesAvailable = false; } /** @@ -256,6 +257,7 @@ d->statisticsAvailable = false; d->hasValuesAvailable = false; + d->propertiesAvailable = false; } /** @@ -355,6 +357,7 @@ void Column::setTextAt(int row, const QString& new_value) { DEBUG("Column::setTextAt()"); d->statisticsAvailable = false; + d->propertiesAvailable = false; exec(new ColumnSetTextCmd(d, row, new_value)); } @@ -367,6 +370,7 @@ DEBUG("Column::replaceTexts()"); if (!new_values.isEmpty()) { //TODO: do we really need this check? d->statisticsAvailable = false; + d->propertiesAvailable = false; exec(new ColumnReplaceTextsCmd(d, first, new_values)); } } @@ -378,6 +382,7 @@ */ void Column::setDateAt(int row, QDate new_value) { d->statisticsAvailable = false; + d->propertiesAvailable = false; setDateTimeAt(row, QDateTime(new_value, timeAt(row))); } @@ -388,6 +393,7 @@ */ void Column::setTimeAt(int row, QTime new_value) { d->statisticsAvailable = false; + d->propertiesAvailable = false; setDateTimeAt(row, QDateTime(dateAt(row), new_value)); } @@ -398,6 +404,7 @@ */ void Column::setDateTimeAt(int row, const QDateTime& new_value) { d->statisticsAvailable = false; + d->propertiesAvailable = false; exec(new ColumnSetDateTimeCmd(d, row, new_value)); } @@ -409,6 +416,7 @@ void Column::replaceDateTimes(int first, const QVector& new_values) { if (!new_values.isEmpty()) { d->statisticsAvailable = false; + d->propertiesAvailable = false; exec(new ColumnReplaceDateTimesCmd(d, first, new_values)); } } @@ -422,6 +430,7 @@ // DEBUG("Column::setValueAt()"); d->statisticsAvailable = false; d->hasValuesAvailable = false; + d->propertiesAvailable = false; exec(new ColumnSetValueCmd(d, row, new_value)); } @@ -435,6 +444,7 @@ if (!new_values.isEmpty()) { d->statisticsAvailable = false; d->hasValuesAvailable = false; + d->propertiesAvailable = false; exec(new ColumnReplaceValuesCmd(d, first, new_values)); } } @@ -448,6 +458,7 @@ DEBUG("Column::setIntegerAt()"); d->statisticsAvailable = false; d->hasValuesAvailable = false; + d->propertiesAvailable = false; exec(new ColumnSetIntegerCmd(d, row, new_value)); } @@ -461,9 +472,22 @@ if (!new_values.isEmpty()) { d->statisticsAvailable = false; d->hasValuesAvailable = false; + d->propertiesAvailable = false; exec(new ColumnReplaceIntegersCmd(d, first, new_values)); } } +/*! + * \brief Column::properties + * Returns the column properties of this curve (monoton increasing, monoton decreasing, ... ) + * See AbstractColumn::properties + * \return + */ +int Column::properties() const{ + if (!d->propertiesAvailable) + d->updateProperties(); + + return d->properties; +} const Column::ColumnStatistics& Column::statistics() const { if (!d->statisticsAvailable) @@ -573,6 +597,7 @@ } statistics.entropy = -entropy; + d->statisticsAvailable = true; } @@ -671,11 +696,15 @@ * This is used e.g. in \c XYFitCurvePrivate::recalculate() */ void Column::setChanged() { + + d->propertiesAvailable = false; + if (!m_suppressDataChangedSignal) emit dataChanged(this); d->statisticsAvailable = false; d->hasValuesAvailable = false; + } //////////////////////////////////////////////////////////////////////////////// @@ -1101,6 +1130,7 @@ d->statisticsAvailable = false; d->hasValuesAvailable = false; + d->propertiesAvailable = false; DEBUG("Column::handleFormatChange() DONE"); } diff --git a/src/backend/core/column/ColumnPrivate.h b/src/backend/core/column/ColumnPrivate.h --- a/src/backend/core/column/ColumnPrivate.h +++ b/src/backend/core/column/ColumnPrivate.h @@ -105,12 +105,16 @@ void setIntegerAt(int row, int new_value); void replaceInteger(int first, const QVector&); + void updateProperties(); + mutable AbstractColumn::ColumnStatistics statistics; bool statisticsAvailable{false}; //is 'statistics' already available or needs to be (re-)calculated? bool hasValues{false}; bool hasValuesAvailable{false}; //is 'hasValues' already available or needs to be (re-)calculated? + mutable bool propertiesAvailable{false}; //is 'properties' already available (true) or needs to be (re-)calculated (false)? + mutable int properties{AbstractColumn::Properties::No}; // declares the properties of the curve (monotonic increasing/decreasing ...). Speed up algorithms private: AbstractColumn::ColumnMode m_column_mode; // type of column data void* m_data; //pointer to the data container (QVector) diff --git a/src/backend/core/column/ColumnPrivate.cpp b/src/backend/core/column/ColumnPrivate.cpp --- a/src/backend/core/column/ColumnPrivate.cpp +++ b/src/backend/core/column/ColumnPrivate.cpp @@ -575,7 +575,6 @@ if (!m_owner->m_suppressDataChangedSignal) emit m_owner->dataChanged(m_owner); - return true; } @@ -1213,6 +1212,117 @@ emit m_owner->dataChanged(m_owner); } +/*! + * \brief ColumnPrivate::updateProperties + * updates the properties. Will be called, when data in the column changed. + * The properies will be used to speed up some algorithms. + * See where variable properties will be used. + */ +void ColumnPrivate::updateProperties() { + + // TODO: for double Properties::Constant will never be used. Use an epsilon (difference smaller than epsilon is zero) + if (rowCount() == 0) { + properties = AbstractColumn::Properties::No; + propertiesAvailable = true; + return; + } + + double prevValue = NAN; + int prevValueInt = 0; + qint64 prevValueDatetime; + + if (m_column_mode == AbstractColumn::Integer) + prevValueInt = integerAt(0); + else if (m_column_mode == AbstractColumn::Numeric) + prevValue = valueAt(0); + else if (m_column_mode == AbstractColumn::DateTime || + m_column_mode == AbstractColumn::Month || + m_column_mode == AbstractColumn::Day) + prevValueDatetime = dateTimeAt(0).toMSecsSinceEpoch(); + + + int monotonic_decreasing = -1; + int monotonic_increasing = -1; + + double value; + int valueInt; + qint64 valueDateTime; + + for (int row=1; row< rowCount(); row++) { + + if (m_column_mode == AbstractColumn::Integer) { + valueInt = integerAt(row); + + // check monotonic increasing + if (valueInt >= prevValueInt && monotonic_increasing < 0) + monotonic_increasing = 1; + else if (valueInt < prevValueInt && monotonic_increasing >=0) + monotonic_increasing = 0; + // else: nothing + + // check monotonic decreasing + if (valueInt <= prevValueInt && monotonic_decreasing < 0) + monotonic_decreasing = 1; + else if (valueInt > prevValueInt && monotonic_decreasing >=0) + monotonic_decreasing = 0; + + prevValueInt = valueInt; + + } else if (m_column_mode == AbstractColumn::Numeric) { + value = valueAt(row); + + // check monotonic increasing + if (value >= prevValue && monotonic_increasing < 0) + monotonic_increasing = 1; + else if (value < prevValue) + monotonic_increasing = 0; + // else: nothing + + // check monotonic decreasing + if (value <= prevValue && monotonic_decreasing < 0) + monotonic_decreasing = 1; + else if (value > prevValue) + monotonic_decreasing = 0; + + prevValue = value; + + } else if (m_column_mode == AbstractColumn::DateTime || + m_column_mode == AbstractColumn::Month || + m_column_mode == AbstractColumn::Day) { + + valueDateTime = dateTimeAt(row).toMSecsSinceEpoch(); + + // check monotonic increasing + if (valueDateTime >= prevValueDatetime && monotonic_increasing < 0) + monotonic_increasing = 1; + else if (valueDateTime < prevValueDatetime) + monotonic_increasing = 0; + // else: nothing + + // check monotonic decreasing + if (valueDateTime <= prevValueDatetime && monotonic_decreasing < 0) + monotonic_decreasing = 1; + else if (valueDateTime > prevValueDatetime) + monotonic_decreasing = 0; + + prevValueDatetime = valueDateTime; + }else { + properties = AbstractColumn::Properties::No; + return; + } + } + + properties = AbstractColumn::Properties::No; + if (monotonic_increasing > 0 && monotonic_decreasing > 0) + properties += AbstractColumn::Properties::Constant; + else if (monotonic_decreasing > 0) + properties += AbstractColumn::Properties::MonotonicDecreasing; + else if (monotonic_increasing > 0) + properties += AbstractColumn::Properties::MonotonicIncreasing; + + propertiesAvailable = true; +} + //////////////////////////////////////////////////////////////////////////////// //@} ////////////////////////////////////////////////////////////////////////////////