diff --git a/sheets/PointStorage.h b/sheets/PointStorage.h index cb994cb7a58..bce73969c6a 100644 --- a/sheets/PointStorage.h +++ b/sheets/PointStorage.h @@ -1,882 +1,883 @@ /* This file is part of the KDE project Copyright 2007 Stefan Nikolaus This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef CALLIGRA_SHEETS_POINT_STORAGE #define CALLIGRA_SHEETS_POINT_STORAGE #include #include #include #include "Region.h" #include "calligra_sheets_limits.h" #include // #define KSPREAD_POINT_STORAGE_HASH namespace Calligra { namespace Sheets { /** * \ingroup Storage * A custom pointwise storage. * Based on a compressed sparse matrix data structure. * Usable for any kind of data attached to 2D coordinates. * * Only non-default data with its coordinate is stored. Hence, the storage * has a small memory footprint nearly regardless of the data's location. * Each empty row before a location occupy an integer, which is not the case * for columns. Iterating over the data becomes fast compared to dense * matrix/array, where each location has to be traversed irrespective of * default or non-default data. * * The actual data is stored in the list m_data. It is grouped by rows in * ascending order. The rows' beginnings and ends are stored in the list * m_rows. Its index corresponds to the row index. The values denote the * starting index of a row in m_data. The row's end is determined by * the starting position of the next row. The entries in each row are ordered * by column. The corresponding column indices are stored in m_cols. Hence, * m_cols has the same amount of entries as m_data. * * \author Stefan Nikolaus * * \note If you fill the storage, do it row-wise. That's more performant. * \note For data assigned to rectangular regions use RectStorage. * \note It's QVector based. To boost performance a lot, declare the stored * data type as movable. */ template class PointStorage { friend class PointStorageBenchmark; friend class PointStorageTest; public: /** * Constructor. * Creates an empty storage. Actually, does nothing. */ PointStorage() {} /** * Destructor. */ ~PointStorage() {} /** * Clears the storage. */ void clear() { m_cols.clear(); m_rows.clear(); m_data.clear(); } /** * Returns the number of items in the storage. * Usable to iterate over all non-default data. * \return number of items * \see col() * \see row() * \see data() */ int count() const { return m_data.count(); } /** * Inserts \p data at \p col , \p row . * \return the overridden data (default data, if no overwrite) */ T insert(int col, int row, const T& data) { Q_ASSERT(1 <= col && col <= KS_colMax); Q_ASSERT(1 <= row && row <= KS_rowMax); // row's missing? if (row > m_rows.count()) { // insert missing rows m_rows.insert(m_rows.count(), row - m_rows.count(), m_data.count()); // append the actual data #ifdef KSPREAD_POINT_STORAGE_HASH m_data.append(*m_usedData.insert(data)); #else m_data.append(data); #endif // append the column index m_cols.append(col); } // the row exists else { const QVector::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1)); const QVector::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end()); const QVector::const_iterator cit = std::lower_bound(cstart, cend, col); // column's missing? if (cit == cend || *cit != col) { // determine the index where the data and column has to be inserted const int index = m_rows.value(row - 1) + (cit - cstart); // insert the actual data #ifdef KSPREAD_POINT_STORAGE_HASH m_data.insert(index, *m_usedData.insert(data)); #else m_data.insert(index, data); #endif // insert the column index m_cols.insert(index, col); // adjust the offsets of the following rows for (int r = row; r < m_rows.count(); ++r) ++m_rows[r]; } // column exists else { const int index = m_rows.value(row - 1) + (cit - cstart); const T oldData = m_data[ index ]; #ifdef KSPREAD_POINT_STORAGE_HASH m_data[ index ] = *m_usedData.insert(data); #else m_data[ index ] = data; #endif return oldData; } } squeezeRows(); return T(); } /** * Looks up the data at \p col , \p row . If no data was found returns a * default object. * \return the data at the given coordinate */ T lookup(int col, int row, const T& defaultVal = T()) const { Q_ASSERT(1 <= col && col <= KS_colMax); Q_ASSERT(1 <= row && row <= KS_rowMax); // is the row not present? if (row > m_rows.count()) return defaultVal; const QVector::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1)); const QVector::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end()); const QVector::const_iterator cit = std::lower_bound(cstart, cend, col); // is the col not present? - if (cit == cend) + if (cit == cend || col != *cit) return defaultVal; return m_data.value(m_rows.value(row - 1) + (cit - cstart)); } /** * Removes data at \p col , \p row . * \return the removed data (default data, if none) */ T take(int col, int row, const T& defaultVal = T()) { Q_ASSERT(1 <= col && col <= KS_colMax); Q_ASSERT(1 <= row && row <= KS_rowMax); // row's missing? if (row > m_rows.count()) return defaultVal; const int rowStart = (row - 1 < m_rows.count()) ? m_rows.value(row - 1) : m_data.count(); const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); QVector::const_iterator cit = std::lower_bound(cols.begin(), cols.end(), col); // column's missing? - if (cit == cols.constEnd()) + if (cit == cols.constEnd() || col != *cit) return defaultVal; const int index = rowStart + (cit - cols.constBegin()); // save the old data const T oldData = m_data[ index ]; // remove the actual data m_data.remove(index); // remove the column index m_cols.remove(index); // adjust the offsets of the following rows for (int r = row; r < m_rows.count(); ++r) --m_rows[r]; squeezeRows(); return oldData; } /** * Insert \p number columns at \p position . * \return the data, that became out of range (shifted over the end) */ QVector< QPair > insertColumns(int position, int number) { Q_ASSERT(1 <= position && position <= KS_colMax); QVector< QPair > oldData; for (int row = m_rows.count(); row >= 1; --row) { const int rowStart = m_rows.value(row - 1); const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); for (int col = cols.count(); col >= 0; --col) { if (cols.value(col) + number > KS_colMax) { oldData.append(qMakePair(QPoint(cols.value(col), row), m_data.value(rowStart + col))); m_cols.remove(rowStart + col); m_data.remove(rowStart + col); // adjust the offsets of the following rows for (int r = row; r < m_rows.count(); ++r) --m_rows[r]; } else if (cols.value(col) >= position) m_cols[rowStart + col] += number; } } squeezeRows(); return oldData; } /** * Removes \p number columns at \p position . * \return the removed data */ QVector< QPair > removeColumns(int position, int number) { Q_ASSERT(1 <= position && position <= KS_colMax); QVector< QPair > oldData; for (int row = m_rows.count(); row >= 1; --row) { const int rowStart = m_rows.value(row - 1); const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); for (int col = cols.count() - 1; col >= 0; --col) { if (cols.value(col) >= position) { if (cols.value(col) < position + number) { oldData.append(qMakePair(QPoint(cols.value(col), row), m_data.value(rowStart + col))); m_cols.remove(rowStart + col); m_data.remove(rowStart + col); for (int r = row; r < m_rows.count(); ++r) --m_rows[r]; } else m_cols[rowStart + col] -= number; } } } squeezeRows(); return oldData; } /** * Insert \p number rows at \p position . * \return the data, that became out of range (shifted over the end) */ QVector< QPair > insertRows(int position, int number) { Q_ASSERT(1 <= position && position <= KS_rowMax); // row's missing? if (position > m_rows.count()) return QVector< QPair >(); QVector< QPair > oldData; int dataCount = 0; int rowCount = 0; // save the old data for (int row = KS_rowMax - number + 1; row <= m_rows.count() && row <= KS_rowMax; ++row) { const QVector::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1)); const QVector::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end()); for (QVector::const_iterator cit = cstart; cit != cend; ++cit) oldData.append(qMakePair(QPoint(*cit, row), m_data.value(cit - m_cols.constBegin()))); dataCount += (cend - cstart); ++rowCount; } // remove the out of range data while (dataCount-- > 0) { m_data.remove(m_data.count() - 1); m_cols.remove(m_cols.count() - 1); } while (rowCount-- > 0) m_rows.remove(m_rows.count() - 1); // insert the new rows const int index = m_rows.value(position - 1); for (int r = 0; r < number; ++r) m_rows.insert(position, index); squeezeRows(); return oldData; } /** * Removes \p number rows at \p position . * \return the removed data */ QVector< QPair > removeRows(int position, int number) { Q_ASSERT(1 <= position && position <= KS_rowMax); // row's missing? if (position > m_rows.count()) return QVector< QPair >(); QVector< QPair > oldData; int dataCount = 0; int rowCount = 0; // save the old data for (int row = position; row <= m_rows.count() && row <= position + number - 1; ++row) { const int rowStart = m_rows.value(row - 1); const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); const QVector data = m_data.mid(rowStart, rowLength); for (int col = 0; col < cols.count(); ++col) oldData.append(qMakePair(QPoint(cols.value(col), row), data.value(col))); dataCount += data.count(); ++rowCount; } // adjust the offsets of the following rows for (int r = position + number - 1; r < m_rows.count(); ++r) m_rows[r] -= dataCount; // remove the out of range data while (dataCount-- > 0) { m_data.remove(m_rows.value(position - 1)); m_cols.remove(m_rows.value(position - 1)); } while (rowCount-- > 0) m_rows.remove(position - 1); squeezeRows(); return oldData; } /** * Shifts the data right of \p rect to the left by the width of \p rect . * The data formerly contained in \p rect becomes overridden. * \return the removed data */ QVector< QPair > removeShiftLeft(const QRect& rect) { Q_ASSERT(1 <= rect.left() && rect.left() <= KS_colMax); QVector< QPair > oldData; for (int row = qMin(rect.bottom(), m_rows.count()); row >= rect.top(); --row) { const int rowStart = m_rows.value(row - 1); const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); for (int col = cols.count() - 1; col >= 0; --col) { if (cols.value(col) >= rect.left()) { if (cols.value(col) <= rect.right()) { oldData.append(qMakePair(QPoint(cols.value(col), row), m_data.value(rowStart + col))); m_cols.remove(rowStart + col); m_data.remove(rowStart + col); for (int r = row; r < m_rows.count(); ++r) --m_rows[r]; } else m_cols[rowStart + col] -= rect.width(); } } } squeezeRows(); return oldData; } /** * Shifts the data in and right of \p rect to the right by the width of \p rect . * \return the data, that became out of range (shifted over the end) */ QVector< QPair > insertShiftRight(const QRect& rect) { Q_ASSERT(1 <= rect.left() && rect.left() <= KS_colMax); QVector< QPair > oldData; for (int row = rect.top(); row <= rect.bottom() && row <= m_rows.count(); ++row) { const int rowStart = m_rows.value(row - 1); const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); for (int col = cols.count(); col >= 0; --col) { if (cols.value(col) + rect.width() > KS_colMax) { oldData.append(qMakePair(QPoint(cols.value(col), row), m_data.value(rowStart + col))); m_cols.remove(rowStart + col); m_data.remove(rowStart + col); // adjust the offsets of the following rows for (int r = row; r < m_rows.count(); ++r) --m_rows[r]; } else if (cols.value(col) >= rect.left()) m_cols[rowStart + col] += rect.width(); } } squeezeRows(); return oldData; } /** * Shifts the data below \p rect to the top by the height of \p rect . * The data formerly contained in \p rect becomes overridden. * \return the removed data */ QVector< QPair > removeShiftUp(const QRect& rect) { Q_ASSERT(1 <= rect.top() && rect.top() <= KS_rowMax); // row's missing? - if (rect.top() > m_rows.count()) + if (rect.top() > m_rows.count()) { return QVector< QPair >(); + } QVector< QPair > oldData; for (int row = rect.top(); row <= m_rows.count() && row <= KS_rowMax - rect.height(); ++row) { const int rowStart = m_rows.value(row - 1); const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); const QVector data = m_data.mid(rowStart, rowLength); // first, iterate over the destination row for (int col = cols.count() - 1; col >= 0; --col) { const int column = cols.value(col); // real column value (1...KS_colMax) if (column >= rect.left() && column <= rect.right()) { // save the old data if (row <= rect.bottom()) oldData.append(qMakePair(QPoint(column, row), data.value(col))); // search const int srcRow = row + rect.height(); const QVector::const_iterator cstart2((srcRow - 1 < m_rows.count()) ? m_cols.begin() + m_rows.value(srcRow - 1) : m_cols.end()); const QVector::const_iterator cend2((srcRow < m_rows.count()) ? (m_cols.begin() + m_rows.value(srcRow)) : m_cols.end()); const QVector::const_iterator cit2 = std::lower_bound(cstart2, cend2, column); // column's missing? - if (cit2 == cend2) { + if (cit2 == cend2 || *cit2 != column) { m_cols.remove(rowStart + col); m_data.remove(rowStart + col); // adjust the offsets of the following rows for (int r = row; r < m_rows.count(); ++r) --m_rows[r]; } // column exists else { // copy m_data[rowStart + col] = m_data.value(cit2 - m_cols.constBegin()); // remove m_cols.remove(cit2 - m_cols.constBegin()); m_data.remove(cit2 - m_cols.constBegin()); // adjust the offsets of the following rows for (int r = row + rect.height(); r < m_rows.count(); ++r) --m_rows[r]; } } } // last, iterate over the source row const int srcRow = row + rect.height(); const int rowStart2 = (srcRow - 1 < m_rows.count()) ? m_rows.value(srcRow - 1) : m_data.count(); const int rowLength2 = (srcRow < m_rows.count()) ? m_rows.value(srcRow) - rowStart2 : -1; const QVector cols2 = m_cols.mid(rowStart2, rowLength2); const QVector data2 = m_data.mid(rowStart2, rowLength2); int offset = 0; for (int col = cols2.count() - 1; col >= 0; --col) { const int column = cols2.value(col); // real column value (1...KS_colMax) if (column >= rect.left() && column <= rect.right()) { // find the insertion position const QVector::const_iterator cstart((row - 1 < m_rows.count()) ? m_cols.begin() + m_rows.value(row - 1) : m_cols.end()); const QVector::const_iterator cend(((row < m_rows.count())) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end()); const QVector::const_iterator cit = std::upper_bound(cstart, cend, cols2.value(col)); // Destination column: const QVector::const_iterator dstcit = std::lower_bound(cols.begin(), cols.end(), column); - if (dstcit != cols.end()) { // destination column exists + if (dstcit != cols.end() && *dstcit == column) { // destination column exists // replace the existing destination value const int dstCol = (dstcit - cols.constBegin()); m_data[rowStart + dstCol] = m_data.value(rowStart2 + col); // remove it from its old position m_data.remove(rowStart2 + col + 1); m_cols.remove(rowStart2 + col + 1); // The amount of values in the range from the // destination row to the source row have not changed. // adjust the offsets of the following rows for (int r = srcRow; r < m_rows.count(); ++r) { ++m_rows[r]; } } else { // destination column does not exist yet // copy it to its new position const int dstCol = cit - m_cols.constBegin(); m_data.insert(dstCol, data2.value(col)); m_cols.insert(dstCol, cols2.value(col)); // remove it from its old position m_data.remove(rowStart2 + col + 1 + offset); m_cols.remove(rowStart2 + col + 1 + offset); ++offset; // adjust the offsets of the following rows for (int r = row; r < srcRow; ++r) { ++m_rows[r]; } } } } } squeezeRows(); return oldData; } /** * Shifts the data in and below \p rect to the bottom by the height of \p rect . * \return the data, that became out of range (shifted over the end) */ QVector< QPair > insertShiftDown(const QRect& rect) { Q_ASSERT(1 <= rect.top() && rect.top() <= KS_rowMax); // row's missing? if (rect.top() > m_rows.count()) return QVector< QPair >(); QVector< QPair > oldData; for (int row = m_rows.count(); row >= rect.top(); --row) { const int rowStart = m_rows.value(row - 1); const int rowLength = (row < m_rows.count()) ? m_rows.value(row) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); const QVector data = m_data.mid(rowStart, rowLength); for (int col = cols.count() - 1; col >= 0; --col) { if (cols.value(col) >= rect.left() && cols.value(col) <= rect.right()) { if (row + rect.height() > KS_rowMax) { // save old data oldData.append(qMakePair(QPoint(cols.value(col), row), data.value(col))); } else { // insert missing rows if (row + rect.height() > m_rows.count()) m_rows.insert(m_rows.count(), row + rect.height() - m_rows.count(), m_data.count()); // copy the data down const int row2 = row + rect.height(); const QVector::const_iterator cstart2(m_cols.begin() + m_rows.value(row2 - 1)); const QVector::const_iterator cend2((row2 < m_rows.count()) ? (m_cols.begin() + m_rows.value(row2)) : m_cols.end()); const QVector::const_iterator cit2 = std::lower_bound(cstart2, cend2, cols.value(col)); // column's missing? if (cit2 == cend2 || *cit2 != cols.value(col)) { // determine the index where the data and column has to be inserted const int index = m_rows.value(row2 - 1) + (cit2 - cstart2); // insert the actual data m_data.insert(index, data.value(col)); // insert the column index m_cols.insert(index, cols.value(col)); // adjust the offsets of the following rows for (int r = row2; r < m_rows.count(); ++r) ++m_rows[r]; } // column exists else { const int index = m_rows.value(row2 - 1) + (cit2 - cstart2); m_data[ index ] = data.value(col); } } // remove the data m_cols.remove(rowStart + col); m_data.remove(rowStart + col); // adjust the offsets of the following rows for (int r = row; r < m_rows.count(); ++r) --m_rows[r]; } } } squeezeRows(); return oldData; } /** * Retrieve the first used data in \p col . * Can be used in conjunction with nextInColumn() to loop through a column. * \return the first used data in \p col or the default data, if the column is empty. */ T firstInColumn(int col, int* newRow = 0) const { Q_ASSERT(1 <= col && col <= KS_colMax); const int index = m_cols.indexOf(col); if (newRow) { if (index == -1) // not found *newRow = 0; else *newRow = std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin(); } return m_data.value(index); } /** * Retrieve the first used data in \p row . * Can be used in conjunction with nextInRow() to loop through a row. * \return the first used data in \p row or the default data, if the row is empty. */ T firstInRow(int row, int* newCol = 0) const { Q_ASSERT(1 <= row && row <= KS_rowMax); // row's empty? if (row > m_rows.count() || ((row < m_rows.count()) && m_rows.value(row - 1) == m_rows.value(row))) { if (newCol) *newCol = 0; return T(); } if (newCol) *newCol = m_cols.value(m_rows.value(row - 1)); return m_data.value(m_rows.value(row - 1)); } /** * Retrieve the last used data in \p col . * Can be used in conjunction with prevInColumn() to loop through a column. * \return the last used data in \p col or the default data, if the column is empty. */ T lastInColumn(int col, int* newRow = 0) const { Q_ASSERT(1 <= col && col <= KS_colMax); const int index = m_cols.lastIndexOf(col); if (newRow) { if (index == -1) // not found *newRow = 0; else *newRow = std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin(); } return m_data.value(index); } /** * Retrieve the last used data in \p row . * Can be used in conjunction with prevInRow() to loop through a row. * \return the last used data in \p row or the default data, if the row is empty. */ T lastInRow(int row, int* newCol = 0) const { Q_ASSERT(1 <= row && row <= KS_rowMax); // row's empty? if (m_rows.value(row - 1) == m_rows.value(row) || m_rows.value(row - 1) == m_data.count()) { if (newCol) *newCol = 0; return T(); } // last row ends on data vector end if (row == m_rows.count()) { if (newCol) *newCol = m_cols.value(m_data.count() - 1); return m_data.value(m_data.count() - 1); } if (newCol) *newCol = m_cols.value(m_rows.value(row) - 1); return m_data.value(m_rows.value(row) - 1); } /** * Retrieve the next used data in \p col after \p row . * Can be used in conjunction with firstInColumn() to loop through a column. * \return the next used data in \p col or the default data, there is no further data. */ T nextInColumn(int col, int row, int* newRow = 0) const { Q_ASSERT(1 <= col && col <= KS_colMax); Q_ASSERT(1 <= row && row <= KS_rowMax); // no next row? if (row + 1 > m_rows.count()) { if (newRow) *newRow = 0; return T(); } // search beginning in rows after the specified row const int index = m_cols.indexOf(col, m_rows.value(row)); if (newRow) { if (index == -1) // not found *newRow = 0; else *newRow = std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin(); } return m_data.value(index); } /** * Retrieve the next used data in \p row after \p col . * Can be used in conjunction with firstInRow() to loop through a row. * \return the next used data in \p row or the default data, if there is no further data. */ T nextInRow(int col, int row, int* newCol = 0) const { Q_ASSERT(1 <= col && col <= KS_colMax); Q_ASSERT(1 <= row && row <= KS_rowMax); // is the row not present? if (row > m_rows.count()) { if (newCol) *newCol = 0; return T(); } const QVector::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1)); const QVector::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end()); const QVector::const_iterator cit = std::upper_bound(cstart, cend, col); if (cit == cend || *cit <= col) { if (newCol) *newCol = 0; return T(); } if (newCol) *newCol = m_cols.value(m_rows.value(row - 1) + (cit - cstart)); return m_data.value(m_rows.value(row - 1) + (cit - cstart)); } /** * Retrieve the previous used data in \p col after \p row . * Can be used in conjunction with lastInColumn() to loop through a column. * \return the previous used data in \p col or the default data, there is no further data. */ T prevInColumn(int col, int row, int* newRow = 0) const { Q_ASSERT(1 <= col && col <= KS_colMax); Q_ASSERT(1 <= row && row <= KS_rowMax); // first row? if (row <= m_rows.count() && m_rows.value(row - 1) == 0) { if (newRow) *newRow = 0; return T(); } const int index = m_cols.lastIndexOf(col, m_rows.value(row - 1) - 1); if (newRow) { if (index == -1) // not found *newRow = 0; else *newRow = std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin(); } return m_data.value(index); } /** * Retrieve the previous used data in \p row after \p col . * Can be used in conjunction with lastInRow() to loop through a row. * \return the previous used data in \p row or the default data, if there is no further data. */ T prevInRow(int col, int row, int* newCol = 0) const { Q_ASSERT(1 <= col && col <= KS_colMax); Q_ASSERT(1 <= row && row <= KS_rowMax); const QVector::const_iterator cstart((row - 1 < m_rows.count()) ? m_cols.begin() + m_rows.value(row - 1) : m_cols.end()); const QVector::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end()); const QVector::const_iterator cit = std::lower_bound(cstart, cend, col); if (cit == cstart) { if (newCol) *newCol = 0; return T(); } if (newCol) *newCol = m_cols.value(cit - 1 - m_cols.begin()); return m_data.value(cit - 1 - m_cols.begin()); } /** * For debugging/testing purposes. * \note only works with primitive/printable data */ QString dump() const { QString str; // determine the dimension of the matrix (the missing column number) int maxCols = 0; for (int row = 0; row < m_rows.count(); ++row) { const int rowStart = m_rows.value(row); const int rowLength = (row + 1 < m_rows.count()) ? m_rows.value(row + 1) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); maxCols = qMax(maxCols, cols.value(cols.count() - 1)); } for (int row = 0; row < m_rows.count(); ++row) { str += '('; const int rowStart = m_rows.value(row); const int rowLength = (row + 1 < m_rows.count()) ? m_rows.value(row + 1) - rowStart : -1; const QVector cols = m_cols.mid(rowStart, rowLength); const QVector data = m_data.mid(rowStart, rowLength); int lastCol = 0; for (int col = 0; col < cols.count(); ++col) { int counter = cols.value(col) - lastCol; while (counter-- > 1) str += " ,"; str += QString("%1,").arg(data.value(col), 2); // str += QString( "%1," ).arg( (data.value( col ) == T()) ? "" : "_", 2 ); lastCol = cols.value(col); } // fill the column up to the max int counter = maxCols - lastCol; while (counter-- > 0) str += " ,"; // replace the last comma str[str.length()-1] = ')'; str += '\n'; } return str.isEmpty() ? QString("()") : str.mid(0, str.length() - 1); } /** * Returns the column of the non-default data at \p index . * \return the data's column at \p index . * \see count() * \see row() * \see data() */ int col(int index) const { return m_cols.value(index); } /** * Returns the row of the non-default data at \p index . * \return the data's row at \p index . * \see count() * \see col() * \see data() */ int row(int index) const { return std::upper_bound(m_rows.begin(), m_rows.end(), index) - m_rows.begin(); } /** * Returns the non-default data at \p index . * \return the data at \p index . * \see count() * \see col() * \see row() */ T data(int index) const { return m_data.value(index); } /** * The maximum occupied column, i.e. the horizontal storage dimension. * \return the maximum column */ int columns() const { int columns = 0; for (int c = 0; c < m_cols.count(); ++c) columns = qMax(m_cols.value(c), columns); return columns; } /** * The maximum occupied row, i.e. the vertical storage dimension. * \return the maximum row */ int rows() const { return m_rows.count(); } /** * Creates a substorage consisting of the values in \p region. * If \p keepOffset is \c true, the values' positions are not altered. * Otherwise, the upper left of \p region's bounding rect is used as new origin, * and all positions are adjusted. * \return a subset of the storage stripped down to the values in \p region */ PointStorage subStorage(const Region& region, bool keepOffset = true) const { // Determine the offset. const QPoint offset = keepOffset ? QPoint(0, 0) : region.boundingRect().topLeft() - QPoint(1, 1); // this generates an array of values PointStorage subStorage; Region::ConstIterator end(region.constEnd()); for (Region::ConstIterator it(region.constBegin()); it != end; ++it) { const QRect rect = (*it)->rect(); for (int row = rect.top(); row <= rect.bottom() && row <= m_rows.count(); ++row) { const QVector::const_iterator cstart(m_cols.begin() + m_rows.value(row - 1)); const QVector::const_iterator cend((row < m_rows.count()) ? (m_cols.begin() + m_rows.value(row)) : m_cols.end()); for (QVector::const_iterator cit = cstart; cit != cend; ++cit) { if (*cit >= rect.left() && *cit <= rect.right()) { if (keepOffset) subStorage.insert(*cit, row, m_data.value(cit - m_cols.begin())); else subStorage.insert(*cit - offset.x(), row - offset.y(), m_data.value(cit - m_cols.begin())); } } } } return subStorage; } /** * Equality operator. */ bool operator==(const PointStorage& o) const { return (m_rows == o.m_rows && m_cols == o.m_cols && m_data == o.m_data); } private: void squeezeRows() { int row = m_rows.count() - 1; while (m_rows.value(row) == m_data.count() && row >= 0) m_rows.remove(row--); } private: QVector m_cols; // stores the column indices (beginning with one) QVector m_rows; // stores the row offsets in m_data QVector m_data; // stores the actual non-default data #ifdef KSPREAD_POINT_STORAGE_HASH QSet m_usedData; #endif }; } // namespace Sheets } // namespace Calligra #endif // CALLIGRA_SHEETS_POINT_STORAGE diff --git a/sheets/tests/TestPointStorage.cpp b/sheets/tests/TestPointStorage.cpp index f00c9a4f15a..b75fa9bba3b 100644 --- a/sheets/tests/TestPointStorage.cpp +++ b/sheets/tests/TestPointStorage.cpp @@ -1,1201 +1,1217 @@ /* This file is part of the KDE project Copyright 2007 Stefan Nikolaus This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define KS_colMax 10 #define KS_rowMax 10 #include "TestPointStorage.h" #include "PointStorage.h" #include using namespace Calligra::Sheets; void PointStorageTest::testInsertion() { PointStorage storage; // int counter = 1; // for ( int col = 1; col <= 5; ++col ) // for ( int row = 1; row <= 5; ++row ) // cout << qPrintable( QString( "storage.insert( %1, %2, %3 );" ).arg(col,2).arg(row,2).arg(counter++,2) ) << endl; storage.insert( 1, 1, 1 ); storage.insert(1, 1, 1); storage.insert(1, 2, 2); storage.insert(1, 3, 3); storage.insert(1, 4, 4); storage.insert(1, 5, 5); storage.insert(2, 1, 6); storage.insert(2, 2, 7); storage.insert(2, 3, 8); storage.insert(2, 4, 9); storage.insert(2, 5, 10); storage.insert(3, 1, 11); storage.insert(3, 2, 12); storage.insert(3, 3, 13); storage.insert(3, 4, 14); storage.insert(3, 5, 15); storage.insert(4, 1, 16); storage.insert(4, 2, 17); storage.insert(4, 3, 18); storage.insert(4, 4, 19); storage.insert(4, 5, 20); storage.insert(5, 1, 21); storage.insert(5, 2, 22); storage.insert(5, 3, 23); storage.insert(5, 4, 24); storage.insert(5, 5, 25); // overwrite int old = storage.insert(5, 5, 30); QCOMPARE(old, 25); // ( 1, 6,11,16,21) // ( 2, 7,12,17,22) // ( 3, 8,13,18,23) // ( 4, 9,14,19,24) // ( 5,10,15,20,25) // qDebug() << endl << qPrintable( storage.dump() ); QVector data(QVector() << 1 << 6 << 11 << 16 << 21 << 2 << 7 << 12 << 17 << 22 << 3 << 8 << 13 << 18 << 23 << 4 << 9 << 14 << 19 << 24 << 5 << 10 << 15 << 20 << 30); QVector rows(QVector() << 0 << 5 << 10 << 15 << 20); QVector cols(QVector() << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); // qDebug() << "data result: " << storage.m_data; // qDebug() << "data expect: " << data; // qDebug() << "rows result: " << storage.m_rows; // qDebug() << "rows expect: " << rows; // qDebug() << "cols result: " << storage.m_cols; // qDebug() << "cols expect: " << cols; // reverse filling storage.clear(); storage.insert(2, 2, 4); storage.insert(1, 2, 3); storage.insert(2, 1, 2); storage.insert(1, 1, 1); // ( 1, 2) // ( 3, 4) data = QVector() << 1 << 2 << 3 << 4; rows = QVector() << 0 << 2; cols = QVector() << 1 << 2 << 1 << 2; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testLookup() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QCOMPARE(storage.lookup(1, 1), 1); QCOMPARE(storage.lookup(1, 2), 4); QCOMPARE(storage.lookup(1, 3), 0); QCOMPARE(storage.lookup(1, 4), 0); QCOMPARE(storage.lookup(1, 5), 11); QCOMPARE(storage.lookup(2, 1), 2); QCOMPARE(storage.lookup(2, 2), 5); QCOMPARE(storage.lookup(2, 3), 7); QCOMPARE(storage.lookup(2, 4), 0); QCOMPARE(storage.lookup(2, 5), 0); QCOMPARE(storage.lookup(3, 1), 0); QCOMPARE(storage.lookup(3, 2), 6); QCOMPARE(storage.lookup(3, 3), 8); QCOMPARE(storage.lookup(3, 4), 0); QCOMPARE(storage.lookup(3, 5), 0); QCOMPARE(storage.lookup(4, 1), 0); QCOMPARE(storage.lookup(4, 2), 0); QCOMPARE(storage.lookup(4, 3), 0); QCOMPARE(storage.lookup(4, 4), 10); QCOMPARE(storage.lookup(4, 5), 0); QCOMPARE(storage.lookup(5, 1), 3); QCOMPARE(storage.lookup(5, 2), 0); QCOMPARE(storage.lookup(5, 3), 9); QCOMPARE(storage.lookup(5, 4), 0); QCOMPARE(storage.lookup(5, 5), 12); // empty row checking storage.clear(); storage.insert(1, 1, 1); // ( 1) QCOMPARE(storage.lookup(1, 1), 1); QCOMPARE(storage.lookup(2, 1), 0); QCOMPARE(storage.lookup(1, 2), 0); QCOMPARE(storage.lookup(2, 2), 0); } void PointStorageTest::testDeletion() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int old = storage.take(4, 4); QCOMPARE(old, 10); old = storage.take(5, 1); QCOMPARE(old, 3); old = storage.take(2, 2); QCOMPARE(old, 5); // ( 1, 2, , , ) // ( 4, , 6, , ) // ( , 7, 8, , 9) // ( , , , , ) // (11, , , ,12) const QVector data(QVector() << 1 << 2 << 4 << 6 << 7 << 8 << 9 << 11 << 12); const QVector rows(QVector() << 0 << 2 << 4 << 7 << 7); const QVector cols(QVector() << 1 << 2 << 1 << 3 << 2 << 3 << 5 << 1 << 5); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); // empty row checking storage.clear(); storage.insert(1, 1, 1); // ( 1) old = storage.take(2, 2); QCOMPARE(old, 0); old = storage.take(1, 2); QCOMPARE(old, 0); + old = storage.take(2, 1); QCOMPARE(old, 0); old = storage.take(1, 1); QCOMPARE(old, 1); QCOMPARE(storage.lookup(1, 1), 0); QCOMPARE(storage.lookup(2, 1), 0); QCOMPARE(storage.lookup(1, 2), 0); QCOMPARE(storage.lookup(2, 2), 0); } void PointStorageTest::testInsertColumns() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector< QPair > old; old = storage.insertColumns(2, 2); // in the middle QVERIFY(old.count() == 0); old = storage.insertColumns(9, 1); // beyond the current end QVERIFY(old.count() == 0); // ( 1, , , 2, , , 3) // ( 4, , , 5, 6, , ) // ( , , , 7, 8, , 9) // ( , , , , ,10, ) // (11, , , , , ,12) QVector data(QVector() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12); QVector rows(QVector() << 0 << 3 << 6 << 9 << 10); QVector cols(QVector() << 1 << 4 << 7 << 1 << 4 << 5 << 4 << 5 << 7 << 6 << 1 << 7); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); old = storage.insertColumns(6, 4); // shift the last column out of range QVERIFY(old.count() == 3); QVERIFY(old.contains(qMakePair(QPoint(7, 1), 3))); QVERIFY(old.contains(qMakePair(QPoint(7, 3), 9))); QVERIFY(old.contains(qMakePair(QPoint(7, 5), 12))); // ( 1, , , 2, , , , , , ) // ( 4, , , 5, 6, , , , , ) // ( , , , 7, 8, , , , , ) // ( , , , , , , , , ,10) // (11, , , , , , , , , ) data = QVector() << 1 << 2 << 4 << 5 << 6 << 7 << 8 << 10 << 11; rows = QVector() << 0 << 2 << 5 << 7 << 8; cols = QVector() << 1 << 4 << 1 << 4 << 5 << 4 << 5 << 10 << 1; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); // empty row checking storage.clear(); storage.insert(1, 1, 1); // ( 1) old = storage.insertColumns(1, 1); QVERIFY(old.count() == 0); // ( , 1) data = QVector() << 1; rows = QVector() << 0; cols = QVector() << 2; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testDeleteColumns() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector< QPair > old; old = storage.removeColumns(2, 2); // in the middle QVERIFY(old.count() == 5); QVERIFY(old.contains(qMakePair(QPoint(2, 1), 2))); QVERIFY(old.contains(qMakePair(QPoint(2, 2), 5))); QVERIFY(old.contains(qMakePair(QPoint(3, 2), 6))); QVERIFY(old.contains(qMakePair(QPoint(2, 3), 7))); QVERIFY(old.contains(qMakePair(QPoint(3, 3), 8))); old = storage.removeColumns(3, 1); // beyond the current end QVERIFY(old.count() == 3); QVERIFY(old.contains(qMakePair(QPoint(3, 1), 3))); QVERIFY(old.contains(qMakePair(QPoint(3, 3), 9))); QVERIFY(old.contains(qMakePair(QPoint(3, 5), 12))); // ( 1, ) // ( 4, ) // ( , ) // ( ,10) // (11, ) const QVector data(QVector() << 1 << 4 << 10 << 11); const QVector rows(QVector() << 0 << 1 << 2 << 2 << 3); const QVector cols(QVector() << 1 << 1 << 2 << 1); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testInsertRows() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector< QPair > old; old = storage.insertRows(2, 2); // in the middle QVERIFY(old.count() == 0); old = storage.insertRows(9, 1); // beyond the current end QVERIFY(old.count() == 0); // ( 1, 2, , , 3) // ( , , , , ) // ( , , , , ) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector data(QVector() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12); QVector rows(QVector() << 0 << 3 << 3 << 3 << 6 << 9 << 10); QVector cols(QVector() << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); old = storage.insertRows(6, 4); // shift the last row out of range QVERIFY(old.count() == 2); QVERIFY(old.contains(qMakePair(QPoint(1, 7), 11))); QVERIFY(old.contains(qMakePair(QPoint(5, 7), 12))); // ( 1, 2, , , 3) // ( , , , , ) // ( , , , , ) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , , , ) // ( , , , , ) // ( , , , , ) // ( , , , , ) // ( , , ,10, ) data = QVector() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10; rows = QVector() << 0 << 3 << 3 << 3 << 6 << 9 << 9 << 9 << 9 << 9; cols = QVector() << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); // first row checking storage.clear(); storage.insert(1, 1, 1); // ( 1) old = storage.insertRows(1, 1); QVERIFY(old.count() == 0); // ( ) // ( 1) data = QVector() << 1; rows = QVector() << 0 << 0; cols = QVector() << 1; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testDeleteRows() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector< QPair > old; old = storage.removeRows(2, 2); // in the middle QVERIFY(old.count() == 6); QVERIFY(old.contains(qMakePair(QPoint(1, 2), 4))); QVERIFY(old.contains(qMakePair(QPoint(2, 2), 5))); QVERIFY(old.contains(qMakePair(QPoint(3, 2), 6))); QVERIFY(old.contains(qMakePair(QPoint(2, 3), 7))); QVERIFY(old.contains(qMakePair(QPoint(3, 3), 8))); QVERIFY(old.contains(qMakePair(QPoint(5, 3), 9))); old = storage.removeRows(3, 1); // at the current end QVERIFY(old.count() == 2); QVERIFY(old.contains(qMakePair(QPoint(1, 3), 11))); QVERIFY(old.contains(qMakePair(QPoint(5, 3), 12))); // ( 1, 2, , , 3) // ( , , ,10, ) const QVector data(QVector() << 1 << 2 << 3 << 10); const QVector rows(QVector() << 0 << 3); const QVector cols(QVector() << 1 << 2 << 5 << 4); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testShiftLeft() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector< QPair > old; old = storage.removeShiftLeft(QRect(2, 2, 2, 2)); QVERIFY(old.count() == 4); QVERIFY(old.contains(qMakePair(QPoint(2, 2), 5))); QVERIFY(old.contains(qMakePair(QPoint(3, 2), 6))); QVERIFY(old.contains(qMakePair(QPoint(2, 3), 7))); QVERIFY(old.contains(qMakePair(QPoint(3, 3), 8))); old = storage.removeShiftLeft(QRect(5, 5, 1, 1)); QVERIFY(old.count() == 1); QVERIFY(old.contains(qMakePair(QPoint(5, 5), 12))); // ( 1, 2, , , 3) // ( 4, , , , ) // ( , , 9, , ) // ( , , ,10, ) // (11, , , , ) const QVector data(QVector() << 1 << 2 << 3 << 4 << 9 << 10 << 11); const QVector rows(QVector() << 0 << 3 << 4 << 5 << 6); const QVector cols(QVector() << 1 << 2 << 5 << 1 << 3 << 4 << 1); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testShiftRight() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector< QPair > old; old = storage.insertShiftRight(QRect(2, 2, 2, 2)); QVERIFY(old.count() == 0); old = storage.insertShiftRight(QRect(5, 5, 1, 1)); QVERIFY(old.count() == 0); // ( 1, 2, , , 3, , ) // ( 4, , , 5, 6, , ) // ( , , , 7, 8, , 9) // ( , , ,10, , , ) // (11, , , , ,12, ) QVector data(QVector() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12); QVector rows(QVector() << 0 << 3 << 6 << 9 << 10); QVector cols(QVector() << 1 << 2 << 5 << 1 << 4 << 5 << 4 << 5 << 7 << 4 << 1 << 6); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); old = storage.insertShiftRight(QRect(4, 2, 6, 1)); // shift the 6 out of range QVERIFY(old.count() == 1); QVERIFY(old.contains(qMakePair(QPoint(5, 2), 6))); // ( 1, 2, , , 3, , , , , ) // ( 4, , , , , , , , , 5) // ( , , , 7, 8, , 9, , , ) // ( , , ,10, , , , , , ) // (11, , , , ,12, , , , ) data = QVector() << 1 << 2 << 3 << 4 << 5 << 7 << 8 << 9 << 10 << 11 << 12; rows = QVector() << 0 << 3 << 5 << 8 << 9; cols = QVector() << 1 << 2 << 5 << 1 << 10 << 4 << 5 << 7 << 4 << 1 << 6; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testShiftUp() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) + QRect rect; QVector< QPair > old; - old = storage.removeShiftUp(QRect(2, 2, 2, 1)); + rect = QRect(2, 2, 2, 1); + old = storage.removeShiftUp(rect); +// qDebug() << "moved two filled cells up onto filled cells:"< 5,5 +// qDebug() << "moved 1 cell from non-existent row onto filled cell:"< data(QVector() << 1 << 2 << 3 << 4 << 7 << 8 << 9 << 10 << 11); QVector rows(QVector() << 0 << 3 << 6 << 7 << 8); QVector cols(QVector() << 1 << 2 << 5 << 1 << 2 << 3 << 5 << 4 << 1); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); + rect = QRect(1, 4, 1, 1); + old = storage.removeShiftUp(rect); +// qDebug() << "moved 1 filled cell onto unfilled cell:"<() << 4; rows = QVector() << 0; cols = QVector() << 2; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testShiftDown() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector< QPair > old; old = storage.insertShiftDown(QRect(2, 2, 2, 2)); QVERIFY(old.count() == 0); old = storage.insertShiftDown(QRect(5, 5, 1, 1)); QVERIFY(old.count() == 0); // ( 1, 2, , , 3) // ( 4, , , , ) // ( , , , , 9) // ( , 5, 6,10, ) // (11, 7, 8, , ) // ( , , , ,12) QVector data(QVector() << 1 << 2 << 3 << 4 << 9 << 5 << 6 << 10 << 11 << 7 << 8 << 12); QVector rows(QVector() << 0 << 3 << 4 << 5 << 8 << 11); QVector cols(QVector() << 1 << 2 << 5 << 1 << 5 << 2 << 3 << 4 << 1 << 2 << 3 << 5); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); old = storage.insertShiftDown(QRect(2, 4, 1, 6)); // shift the 7 out of range QVERIFY(old.count() == 1); QVERIFY(old.contains(qMakePair(QPoint(2, 5), 7))); // ( 1, 2, , , 3) // ( 4, , , , ) // ( , , , , 9) // ( , , 6,10, ) // (11, , 8, , ) // ( , , , ,12) // ( , , , , ) // ( , , , , ) // ( , , , , ) // ( , , , , ) // ( , 5, , , ) data = QVector() << 1 << 2 << 3 << 4 << 9 << 6 << 10 << 11 << 8 << 12 << 5; rows = QVector() << 0 << 3 << 4 << 5 << 7 << 9 << 10 << 10 << 10 << 10; cols = QVector() << 1 << 2 << 5 << 1 << 5 << 3 << 4 << 1 << 3 << 5 << 2; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); // first row storage.clear(); storage.m_data << 1 << 2 << 3 << 4; storage.m_rows << 0 << 1 << 3; storage.m_cols << 1 << 1 << 2 << 2; // ( 1, ) // ( 2, 3) // ( , 4) old = storage.insertShiftDown(QRect(1, 1, 2, 2)); QVERIFY(old.count() == 0); // ( , ) // ( , ) // ( 1, ) // ( 2, 3) // ( , 4) data = QVector() << 1 << 2 << 3 << 4; rows = QVector() << 0 << 0 << 0 << 1 << 3; cols = QVector() << 1 << 1 << 2 << 2; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testShiftDownUp() { PointStorage storage; for (int row = 1; row < 6; ++row) { for (int col = 1; col < 6; ++col) { storage.m_data << (row * col); storage.m_cols << col; } storage.m_rows << (5 *(row - 1)); } // qDebug() << "Origin:" << endl << qPrintable(storage.dump()); // ( 1, 2, 3, 4, 5) // ( 2, 4, 6, 8,10) // ( 3, 6, 8,12,15) // ( 4, 8,12,16,20) // ( 5,10,15,20,25) QVector data(QVector() << 1 << 2 << 3 << 4 << 5 << 2 << 4 << 6 << 8 << 10 << 3 << 6 << 9 << 12 << 15 << 4 << 8 << 12 << 16 << 20 << 5 << 10 << 15 << 20 << 25); QVector rows(QVector() << 0 << 5 << 10 << 15 << 20); QVector cols(QVector() << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5); QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); QVector< QPair > old; old = storage.insertShiftDown(QRect(3, 2, 2, 2)); QVERIFY(old.count() == 0); // qDebug() << endl << qPrintable(storage.dump()); // ( 1, 2, 3, 4, 5) // ( 2, 4, , ,10) // ( 3, 6, , ,15) // ( 4, 8, 6, 8,20) // ( 5,10, 9,12,25) // ( , ,12,16, ) // ( , ,15,20, ) data = QVector() << 1 << 2 << 3 << 4 << 5 << 2 << 4 << 10 << 3 << 6 << 15 << 4 << 8 << 6 << 8 << 20 << 5 << 10 << 9 << 12 << 25 << 12 << 16 << 15 << 20; cols = QVector() << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 5 << 1 << 2 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 3 << 4 << 3 << 4; rows = QVector() << 0 << 5 << 8 << 11 << 16 << 21 << 23; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); old = storage.removeShiftUp(QRect(3, 2, 2, 2)); QVERIFY(old.count() == 0); data = QVector() << 1 << 2 << 3 << 4 << 5 << 2 << 4 << 6 << 8 << 10 << 3 << 6 << 9 << 12 << 15 << 4 << 8 << 12 << 16 << 20 << 5 << 10 << 15 << 20 << 25; rows = QVector() << 0 << 5 << 10 << 15 << 20; cols = QVector() << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5 << 1 << 2 << 3 << 4 << 5; QCOMPARE(storage.m_data, data); QCOMPARE(storage.m_rows, rows); QCOMPARE(storage.m_cols, cols); } void PointStorageTest::testFirstInColumn() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int newRow = 0; QCOMPARE(storage.firstInColumn(1, &newRow), 1); QCOMPARE(newRow, 1); QCOMPARE(storage.firstInColumn(2, &newRow), 2); QCOMPARE(newRow, 1); QCOMPARE(storage.firstInColumn(3, &newRow), 6); QCOMPARE(newRow, 2); QCOMPARE(storage.firstInColumn(4, &newRow), 10); QCOMPARE(newRow, 4); QCOMPARE(storage.firstInColumn(5, &newRow), 3); QCOMPARE(newRow, 1); storage.clear(); storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 9; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , , , ) // (11, , , ,12) QCOMPARE(storage.firstInColumn(4, &newRow), 0); QCOMPARE(newRow, 0); QCOMPARE(storage.firstInColumn(5, &newRow), 3); QCOMPARE(newRow, 1); } void PointStorageTest::testFirstInRow() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int newCol = 0; QCOMPARE(storage.firstInRow(1, &newCol), 1); QCOMPARE(newCol, 1); QCOMPARE(storage.firstInRow(2, &newCol), 4); QCOMPARE(newCol, 1); QCOMPARE(storage.firstInRow(3, &newCol), 7); QCOMPARE(newCol, 2); QCOMPARE(storage.firstInRow(4, &newCol), 10); QCOMPARE(newCol, 4); QCOMPARE(storage.firstInRow(5, &newCol), 11); QCOMPARE(newCol, 1); storage.clear(); storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 9; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , , , ) // (11, , , ,12) QCOMPARE(storage.firstInRow(4, &newCol), 0); QCOMPARE(newCol, 0); QCOMPARE(storage.firstInRow(5, &newCol), 11); QCOMPARE(newCol, 1); storage.clear(); storage.m_data << 1; storage.m_rows << 0 << 0; storage.m_cols << 1; // ( ) // ( 1) QCOMPARE(storage.firstInRow(1, &newCol), 0); QCOMPARE(newCol, 0); QCOMPARE(storage.firstInRow(2, &newCol), 1); QCOMPARE(newCol, 1); } void PointStorageTest::testLastInColumn() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int newRow = 0; QCOMPARE(storage.lastInColumn(1, &newRow), 11); QCOMPARE(newRow, 5); QCOMPARE(storage.lastInColumn(2, &newRow), 7); QCOMPARE(newRow, 3); QCOMPARE(storage.lastInColumn(3, &newRow), 8); QCOMPARE(newRow, 3); QCOMPARE(storage.lastInColumn(4, &newRow), 10); QCOMPARE(newRow, 4); QCOMPARE(storage.lastInColumn(5, &newRow), 12); QCOMPARE(newRow, 5); QCOMPARE(storage.lastInColumn(6, &newRow), 0); QCOMPARE(newRow, 0); } void PointStorageTest::testLastInRow() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int newCol = 0; QCOMPARE(storage.lastInRow(1, &newCol), 3); QCOMPARE(newCol, 5); QCOMPARE(storage.lastInRow(2, &newCol), 6); QCOMPARE(newCol, 3); QCOMPARE(storage.lastInRow(3, &newCol), 9); QCOMPARE(newCol, 5); QCOMPARE(storage.lastInRow(4, &newCol), 10); QCOMPARE(newCol, 4); QCOMPARE(storage.lastInRow(5, &newCol), 12); QCOMPARE(newCol, 5); QCOMPARE(storage.lastInRow(6, &newCol), 0); QCOMPARE(newCol, 0); } void PointStorageTest::testNextInColumn() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int newRow = 0; QCOMPARE(storage.nextInColumn(1, 3, &newRow), 11); QCOMPARE(newRow, 5); QCOMPARE(storage.nextInColumn(2, 3, &newRow), 0); QCOMPARE(newRow, 0); QCOMPARE(storage.nextInColumn(3, 3, &newRow), 0); QCOMPARE(newRow, 0); QCOMPARE(storage.nextInColumn(4, 3, &newRow), 10); QCOMPARE(newRow, 4); QCOMPARE(storage.nextInColumn(5, 3, &newRow), 12); QCOMPARE(newRow, 5); QCOMPARE(storage.nextInColumn(6, 3, &newRow), 0); QCOMPARE(newRow, 0); // storage.clear(); storage.m_data << 1 << 2 << 3 << 4; storage.m_rows << 0 << 0 << 0 << 1 << 2; storage.m_cols << 1 << 1 << 1 << 2; // ( , ) // ( , ) // ( 1, ) // ( 2, ) // ( 3, 4) QCOMPARE(storage.nextInColumn(1, 1, &newRow), 1); QCOMPARE(newRow, 3); QCOMPARE(storage.nextInColumn(2, 1, &newRow), 4); QCOMPARE(newRow, 5); QCOMPARE(storage.nextInColumn(1, 7, &newRow), 0); QCOMPARE(newRow, 0); QCOMPARE(storage.nextInColumn(2, 5, &newRow), 0); QCOMPARE(newRow, 0); } void PointStorageTest::testNextInRow() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int newCol = 0; QCOMPARE(storage.nextInRow(3, 1, &newCol), 3); QCOMPARE(newCol, 5); QCOMPARE(storage.nextInRow(3, 2, &newCol), 0); QCOMPARE(newCol, 0); QCOMPARE(storage.nextInRow(3, 3, &newCol), 9); QCOMPARE(newCol, 5); QCOMPARE(storage.nextInRow(3, 4, &newCol), 10); QCOMPARE(newCol, 4); QCOMPARE(storage.nextInRow(3, 5, &newCol), 12); QCOMPARE(newCol, 5); QCOMPARE(storage.nextInRow(3, 6, &newCol), 0); QCOMPARE(newCol, 0); } void PointStorageTest::testPrevInColumn() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int newRow = 0; QCOMPARE(storage.prevInColumn(1, 3, &newRow), 4); QCOMPARE(newRow, 2); QCOMPARE(storage.prevInColumn(2, 3, &newRow), 5); QCOMPARE(newRow, 2); QCOMPARE(storage.prevInColumn(3, 3, &newRow), 6); QCOMPARE(newRow, 2); QCOMPARE(storage.prevInColumn(4, 3, &newRow), 0); QCOMPARE(newRow, 0); QCOMPARE(storage.prevInColumn(5, 3, &newRow), 3); QCOMPARE(newRow, 1); QCOMPARE(storage.prevInColumn(6, 3, &newRow), 0); QCOMPARE(newRow, 0); QCOMPARE(storage.prevInColumn(3, 7, &newRow), 8); QCOMPARE(newRow, 3); } void PointStorageTest::testPrevInRow() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) int newCol = 0; QCOMPARE(storage.prevInRow(3, 1, &newCol), 2); QCOMPARE(newCol, 2); QCOMPARE(storage.prevInRow(3, 2, &newCol), 5); QCOMPARE(newCol, 2); QCOMPARE(storage.prevInRow(3, 3, &newCol), 7); QCOMPARE(newCol, 2); QCOMPARE(storage.prevInRow(3, 4, &newCol), 0); QCOMPARE(newCol, 0); QCOMPARE(storage.prevInRow(3, 5, &newCol), 11); QCOMPARE(newCol, 1); QCOMPARE(storage.prevInRow(3, 6, &newCol), 0); QCOMPARE(newCol, 0); } void PointStorageTest::testIteration() { PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QCOMPARE(storage.data(0), 1); QCOMPARE(storage.data(1), 2); QCOMPARE(storage.data(2), 3); QCOMPARE(storage.data(3), 4); QCOMPARE(storage.data(4), 5); QCOMPARE(storage.data(5), 6); QCOMPARE(storage.data(6), 7); QCOMPARE(storage.data(7), 8); QCOMPARE(storage.data(8), 9); QCOMPARE(storage.data(9), 10); QCOMPARE(storage.data(10), 11); QCOMPARE(storage.data(11), 12); QCOMPARE(storage.col(0), 1); QCOMPARE(storage.col(1), 2); QCOMPARE(storage.col(2), 5); QCOMPARE(storage.col(3), 1); QCOMPARE(storage.col(4), 2); QCOMPARE(storage.col(5), 3); QCOMPARE(storage.col(6), 2); QCOMPARE(storage.col(7), 3); QCOMPARE(storage.col(8), 5); QCOMPARE(storage.col(9), 4); QCOMPARE(storage.col(10), 1); QCOMPARE(storage.col(11), 5); QCOMPARE(storage.row(0), 1); QCOMPARE(storage.row(1), 1); QCOMPARE(storage.row(2), 1); QCOMPARE(storage.row(3), 2); QCOMPARE(storage.row(4), 2); QCOMPARE(storage.row(5), 2); QCOMPARE(storage.row(6), 3); QCOMPARE(storage.row(7), 3); QCOMPARE(storage.row(8), 3); QCOMPARE(storage.row(9), 4); QCOMPARE(storage.row(10), 5); QCOMPARE(storage.row(11), 5); } void PointStorageTest::testColumnIteration() { PointStorage storage; storage.insert(1, 1, 27); int row = -1; QCOMPARE(storage.firstInColumn(1, &row), 27); QCOMPARE(row, 1); row = -1; // QCOMPARE(storage.nextInColumn(1, 0, &row), 27); // QCOMPARE(row, 1); QCOMPARE(storage.nextInColumn(1, 1, &row), 0); QCOMPARE(row, 0); storage.insert(1, 5, 5); QCOMPARE(storage.nextInColumn(1, 1, &row), 5); QCOMPARE(row, 5); QCOMPARE(storage.nextInColumn(5, 1, &row), 0); QCOMPARE(row, 0); row = -1; QCOMPARE(storage.nextInColumn(6, 1, &row), 0); QCOMPARE(row, 0); // reverse iteration QCOMPARE(storage.lastInColumn(1, &row), 5); QCOMPARE(row, 5); row = -1; // QCOMPARE(storage.prevInColumn(1, KS_rowMax + 1, &row), 5); // QCOMPARE(row, 5); QCOMPARE(storage.prevInColumn(1, 6, &row), 5); QCOMPARE(row, 5); QCOMPARE(storage.prevInColumn(1, 5, &row), 27); QCOMPARE(row, 1); QCOMPARE(storage.prevInColumn(1, 1, &row), 0); QCOMPARE(row, 0); } void PointStorageTest::testRowIteration() { PointStorage storage; storage.insert(1, 1, 27); int col = -1; QCOMPARE(storage.firstInRow(1, &col), 27); QCOMPARE(col, 1); col = -1; QCOMPARE(storage.nextInRow(1, 1, &col), 0); QCOMPARE(col, 0); storage.insert(5, 1, 5); QCOMPARE(storage.nextInRow(1, 1, &col), 5); QCOMPARE(col, 5); QCOMPARE(storage.nextInRow(1, 5, &col), 0); QCOMPARE(col, 0); col = -1; QCOMPARE(storage.nextInRow(1, 6, &col), 0); QCOMPARE(col, 0); // reverse iteration QEXPECT_FAIL("", "Will fix in the next release", Continue); QCOMPARE(storage.lastInRow(1, &col), 5); QEXPECT_FAIL("", "Will fix in the next release", Continue); QCOMPARE(col, 5); col = -1; // QCOMPARE(storage.prevInRow(KS_colMax + 1, 1, &col), 5); // QCOMPARE(col, 5); QCOMPARE(storage.prevInRow(6, 1, &col), 5); QCOMPARE(col, 5); QCOMPARE(storage.prevInRow(5, 1, &col), 27); QCOMPARE(col, 1); QCOMPARE(storage.prevInRow(1, 1, &col), 0); QCOMPARE(col, 0); } void PointStorageTest::testDimension() { PointStorage storage; QCOMPARE(storage.rows(), 0); QCOMPARE(storage.columns(), 0); storage.insert(1, 1, 27); QCOMPARE(storage.rows(), 1); QCOMPARE(storage.columns(), 1); storage.insert(3, 1, 27); QCOMPARE(storage.rows(), 1); QCOMPARE(storage.columns(), 3); storage.insert(3, 9, 27); QCOMPARE(storage.rows(), 9); QCOMPARE(storage.columns(), 3); storage.insert(9, 9, 27); QCOMPARE(storage.rows(), 9); QCOMPARE(storage.columns(), 9); storage.insert(10, 9, 27); QCOMPARE(storage.rows(), 9); QCOMPARE(storage.columns(), 10); storage.insert(10, 10, 27); QCOMPARE(storage.rows(), 10); QCOMPARE(storage.columns(), 10); } void PointStorageTest::testSubStorage() { // #if 0 PointStorage storage; storage.m_data << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; storage.m_rows << 0 << 3 << 6 << 9 << 10; storage.m_cols << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) PointStorage subStorage; subStorage = storage.subStorage(Region(QRect(1, 1, 5, 5))); // all // ( 1, 2, , , 3) // ( 4, 5, 6, , ) // ( , 7, 8, , 9) // ( , , ,10, ) // (11, , , ,12) QVector data = QVector() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12; QVector rows = QVector() << 0 << 3 << 6 << 9 << 10; QVector cols = QVector() << 1 << 2 << 5 << 1 << 2 << 3 << 2 << 3 << 5 << 4 << 1 << 5; QCOMPARE(subStorage.m_data, data); QCOMPARE(subStorage.m_rows, rows); QCOMPARE(subStorage.m_cols, cols); subStorage = storage.subStorage(Region(QRect(1, 1, 3, 3))); // upper left // ( 1, 2, ) // ( 4, 5, 6) // ( , 7, 8) data = QVector() << 1 << 2 << 4 << 5 << 6 << 7 << 8; rows = QVector() << 0 << 2 << 5; cols = QVector() << 1 << 2 << 1 << 2 << 3 << 2 << 3; QCOMPARE(subStorage.m_data, data); QCOMPARE(subStorage.m_rows, rows); QCOMPARE(subStorage.m_cols, cols); subStorage = storage.subStorage(Region(QRect(4, 4, 5, 5))); // lower right // ( , , , , ) // ( , , , , ) // ( , , , , ) // ( , , ,10, ) // ( , , , ,12) data = QVector() << 10 << 12; rows = QVector() << 0 << 0 << 0 << 0 << 1; cols = QVector() << 4 << 5; QCOMPARE(subStorage.m_data, data); QCOMPARE(subStorage.m_rows, rows); QCOMPARE(subStorage.m_cols, cols); // #endif } QTEST_MAIN(PointStorageTest)