diff --git a/apps/shared/float_pair_store.cpp b/apps/shared/float_pair_store.cpp index c03e64e67..e9a5e0938 100644 --- a/apps/shared/float_pair_store.cpp +++ b/apps/shared/float_pair_store.cpp @@ -6,64 +6,55 @@ namespace Shared { -FloatPairStore::FloatPairStore() : - m_numberOfPairs(0), - m_data{} -{ -} - -double FloatPairStore::get(int i, int j) { - assert(j < m_numberOfPairs); - return m_data[i][j]; -} - -void FloatPairStore::set(double f, int i, int j) { +void FloatPairStore::set(double f, int series, int i, int j) { + assert(series >= 0 && series < k_numberOfSeries); if (j >= k_maxNumberOfPairs) { return; } - m_data[i][j] = f; + m_data[series][i][j] = f; if (j >= m_numberOfPairs) { int otherI = i == 0 ? 1 : 0; - m_data[otherI][j] = defaultValue(otherI, j); - m_numberOfPairs++; + m_data[series][otherI][j] = defaultValue(otherI, j); + m_numberOfPairs[series]++; } } -int FloatPairStore::numberOfPairs() { - return m_numberOfPairs; -} - -void FloatPairStore::deletePairAtIndex(int i) { - m_numberOfPairs--; - for (int k = i; k < m_numberOfPairs; k++) { - m_data[0][k] = m_data[0][k+1]; - m_data[1][k] = m_data[1][k+1]; +void FloatPairStore::deletePairAtIndex(int series, int j) { + m_numberOfPairs[series]--; + for (int k = j; k < m_numberOfPairs[series]; k++) { + m_data[series][0][k] = m_data[series][0][k+1]; + m_data[series][1][k] = m_data[series][1][k+1]; } /* We reset the values of the empty row to ensure the correctness of the * checksum. */ - m_data[0][m_numberOfPairs] = 0; - m_data[1][m_numberOfPairs] = 0; + m_data[series][0][m_numberOfPairs] = 0; + m_data[series][1][m_numberOfPairs] = 0; } -void FloatPairStore::deleteAllPairs() { +void FloatPairStore::deleteAllPairs(int series) { + assert(series >= 0 && series < k_numberOfSeries); /* We reset all values to 0 to ensure the correctness of the checksum.*/ - for (int k = 0; k < m_numberOfPairs; k++) { - m_data[0][k] = 0; - m_data[1][k] = 0; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + m_data[series][0][k] = 0; + m_data[series][1][k] = 0; } - m_numberOfPairs = 0; + m_numberOfPairs[series] = 0; } -void FloatPairStore::resetColumn(int i) { - for (int k = 0; k < m_numberOfPairs; k++) { - m_data[i][k] = defaultValue(i, k); +void FloatPairStore::resetColumn(int series, int i) { + assert(series >= 0 && series < k_numberOfSeries); + assert(i == 0 || i == 1); + for (int k = 0; k < m_numberOfPairs[series]; k++) { + m_data[series][i][k] = defaultValue(i, k); } } -double FloatPairStore::sumOfColumn(int i) { +double FloatPairStore::sumOfColumn(int series, int i) { + assert(series >= 0 && series < k_numberOfSeries); + assert(i == 0 || i == 1); double result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { - result += m_data[i][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + result += m_data[series][i][k]; } return result; } @@ -73,14 +64,15 @@ uint32_t FloatPairStore::storeChecksum() { * pairs. However, the two values of a pair are not stored consecutively. We * thus compute the checksum on all pairs and ensure to set the pair at 0 * when removing them. */ - size_t dataLengthInBytes = k_maxNumberOfPairs*2*sizeof(double); + size_t dataLengthInBytes = k_numberOfSeries*k_maxNumberOfPairs*2*sizeof(double); assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 return Ion::crc32((uint32_t *)m_data, dataLengthInBytes/sizeof(uint32_t)); } -double FloatPairStore::defaultValue(int i, int j) { +double FloatPairStore::defaultValue(int series, int i, int j) { + assert(series >= 0 && series < k_numberOfSeries); if(i == 0 && j > 1) { - return 2*m_data[i][j-1]-m_data[i][j-2]; + return 2*m_data[series][i][j-1]-m_data[i][j-2]; } else { return 0.0; } diff --git a/apps/shared/float_pair_store.h b/apps/shared/float_pair_store.h index 6efdfe840..2a642c3f8 100644 --- a/apps/shared/float_pair_store.h +++ b/apps/shared/float_pair_store.h @@ -2,27 +2,35 @@ #define SHARED_FLOAT_PAIR_STORE_H #include +#include namespace Shared { class FloatPairStore { public: - FloatPairStore(); + constexpr static int k_numberOfSeries = 3; + constexpr static int k_maxNumberOfPairs = 100; + FloatPairStore() : + m_numberOfPairs{}, + m_data{} + {} // Delete the implicit copy constructor: the object is heavy FloatPairStore(const FloatPairStore&) = delete; - double get(int i, int j); - void set(double f, int i, int j); - int numberOfPairs(); - void deletePairAtIndex(int j); - void deleteAllPairs(); - void resetColumn(int i); - double sumOfColumn(int i); + double get(int series, int i, int j) const { + assert(j < m_numberOfPairs[series]); + return m_data[series][i][j]; + } + void set(double f, int series, int i, int j); + int numberOfPairsOfSeries(int series) const { return m_numberOfPairs[series]; } + void deletePairAtIndex(int series, int j); + void deleteAllPairs(int series); + void resetColumn(int series, int i); + double sumOfColumn(int series, int i); uint32_t storeChecksum(); - constexpr static int k_maxNumberOfPairs = 100; protected: - virtual double defaultValue(int i, int j); - int m_numberOfPairs; - double m_data[2][k_maxNumberOfPairs]; + virtual double defaultValue(int series, int i, int j); + int m_numberOfPairs[k_numberOfSeries]; + double m_data[k_numberOfSeries][2][k_maxNumberOfPairs]; }; } diff --git a/apps/statistics/store.cpp b/apps/statistics/store.cpp index d70136d53..32bb60478 100644 --- a/apps/statistics/store.cpp +++ b/apps/statistics/store.cpp @@ -32,34 +32,34 @@ void Store::setBarWidth(double barWidth) { } } -double Store::heightOfBarAtIndex(int index) { - return sumOfValuesBetween(startOfBarAtIndex(index), endOfBarAtIndex(index)); +double Store::heightOfBarAtIndex(int series, int index) { + return sumOfValuesBetween(startOfBarAtIndex(series, index), endOfBarAtIndex(series, index)); } -double Store::heightOfBarAtValue(double value) { +double Store::heightOfBarAtValue(int series, double value) { double width = barWidth(); int barNumber = std::floor((value - m_firstDrawnBarAbscissa)/width); double lowerBound = m_firstDrawnBarAbscissa + barNumber*width; double upperBound = m_firstDrawnBarAbscissa + (barNumber+1)*width; - return sumOfValuesBetween(lowerBound, upperBound); + return sumOfValuesBetween(series, lowerBound, upperBound); } -double Store::startOfBarAtIndex(int index) { - double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue()- m_firstDrawnBarAbscissa)/m_barWidth); +double Store::startOfBarAtIndex(int series, int index) { + double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue(series)- m_firstDrawnBarAbscissa)/m_barWidth); return firstBarAbscissa + index * m_barWidth; } -double Store::endOfBarAtIndex(int index) { - return startOfBarAtIndex(index+1); +double Store::endOfBarAtIndex(int series, int index) { + return startOfBarAtIndex(series, index+1); } -double Store::numberOfBars() { - double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue()- m_firstDrawnBarAbscissa)/m_barWidth); - return std::ceil((maxValue() - firstBarAbscissa)/m_barWidth)+1; +double Store::numberOfBars(int series) { + double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue(series)- m_firstDrawnBarAbscissa)/m_barWidth); + return std::ceil((maxValue(series) - firstBarAbscissa)/m_barWidth)+1; } -bool Store::scrollToSelectedBarIndex(int index) { - float startSelectedBar = startOfBarAtIndex(index); +bool Store::scrollToSelectedBarIndex(int series, int index) { + float startSelectedBar = startOfBarAtIndex(series, index); float windowRange = m_xMax - m_xMin; float range = windowRange/(1+k_displayLeftMarginRatio+k_displayRightMarginRatio); if (m_xMin + k_displayLeftMarginRatio*range > startSelectedBar) { @@ -67,7 +67,7 @@ bool Store::scrollToSelectedBarIndex(int index) { m_xMax = m_xMin + windowRange; return true; } - float endSelectedBar = endOfBarAtIndex(index); + float endSelectedBar = endOfBarAtIndex(series, index); if (endSelectedBar > m_xMax - k_displayRightMarginRatio*range) { m_xMax = endSelectedBar + k_displayRightMarginRatio*range; m_xMin = m_xMax - windowRange; @@ -78,124 +78,124 @@ bool Store::scrollToSelectedBarIndex(int index) { /* Calculation */ -double Store::sumOfOccurrences() { - return sumOfColumn(1); +double Store::sumOfOccurrences(int series) { + return sumOfColumn(series, 1); } -double Store::maxValue() { +double Store::maxValue(int series) { double max = -DBL_MAX; for (int k = 0; k < m_numberOfPairs; k++) { - if (m_data[0][k] > max && m_data[1][k] > 0) { - max = m_data[0][k]; + if (m_data[series][0][k] > max && m_data[series][1][k] > 0) { + max = m_data[series][0][k]; } } return max; } -double Store::minValue() { +double Store::minValue(int series) { double min = DBL_MAX; for (int k = 0; k < m_numberOfPairs; k++) { - if (m_data[0][k] < min && m_data[1][k] > 0) { - min = m_data[0][k]; + if (m_data[series][0][k] < min && m_data[series][1][k] > 0) { + min = m_data[series][0][k]; } } return min; } -double Store::range() { - return maxValue()-minValue(); +double Store::range(int series) { + return maxValue(series)-minValue(series); } -double Store::mean() { - return sum()/sumOfOccurrences(); +double Store::mean(int series) { + return sum(series)/sumOfOccurrences(series); } -double Store::variance() { - double m = mean(); - return squaredValueSum()/sumOfOccurrences() - m*m; +double Store::variance(int series) { + double m = mean(series); + return squaredValueSum(series)/sumOfOccurrences(series) - m*m; } -double Store::standardDeviation() { - return std::sqrt(variance()); +double Store::standardDeviation(int series) { + return std::sqrt(variance(series)); } -double Store::sampleStandardDeviation() { - double n = sumOfOccurrences(); +double Store::sampleStandardDeviation(int series) { + double n = sumOfOccurrences(series); double s = std::sqrt(n/(n-1.0)); - return s*standardDeviation(); + return s*standardDeviation(series); } -double Store::firstQuartile() { - int firstQuartileIndex = std::ceil(sumOfOccurrences()/4); - return sortedElementNumber(firstQuartileIndex); +double Store::firstQuartile(int series) { + int firstQuartileIndex = std::ceil(sumOfOccurrences(series)/4); + return sortedElementNumber(series, firstQuartileIndex); } -double Store::thirdQuartile() { - int thirdQuartileIndex = std::ceil(3*sumOfOccurrences()/4); - return sortedElementNumber(thirdQuartileIndex); +double Store::thirdQuartile(int series) { + int thirdQuartileIndex = std::ceil(3*sumOfOccurrences(series)/4); + return sortedElementNumber(series, thirdQuartileIndex); } -double Store::quartileRange() { +double Store::quartileRange(int series) { return thirdQuartile()-firstQuartile(); } -double Store::median() { - int total = sumOfOccurrences(); +double Store::median(int series) { + int total = sumOfOccurrences(series); int halfTotal = total/2; int totalMod2 = total - 2*halfTotal; if (totalMod2 == 0) { - double minusMedian = sortedElementNumber(halfTotal); - double maxMedian = sortedElementNumber(halfTotal+1); + double minusMedian = sortedElementNumber(series, halfTotal); + double maxMedian = sortedElementNumber(series, halfTotal+1); return (minusMedian+maxMedian)/2.0; } else { - return sortedElementNumber(halfTotal+1); + return sortedElementNumber(series, halfTotal+1); } } -double Store::sum() { +double Store::sum(int series) { double result = 0; for (int k = 0; k < m_numberOfPairs; k++) { - result += m_data[0][k]*m_data[1][k]; + result += m_data[series][0][k]*m_data[series][1][k]; } return result; } -double Store::squaredValueSum() { +double Store::squaredValueSum(int series) { double result = 0; for (int k = 0; k < m_numberOfPairs; k++) { - result += m_data[0][k]*m_data[0][k]*m_data[1][k]; + result += m_data[series][0][k]*m_data[series][0][k]*m_data[series][1][k]; } return result; } -/* private methods */ +/* Private methods */ -double Store::defaultValue(int i, int j) { - return i == 0 ? FloatPairStore::defaultValue(i, j) : 1.0; +double Store::defaultValue(int series, int i, int j) { + return i == 0 ? FloatPairStore::defaultValue(series, i, j) : 1.0; } double Store::sumOfValuesBetween(double x1, double x2) { double result = 0; - for (int k = 0; k < m_numberOfPairs; k++) { - if (m_data[0][k] < x2 && x1 <= m_data[0][k]) { - result += m_data[1][k]; + for (int k = 0; k < m_numberOfPairs[series]; k++) { + if (m_data[series][0][k] < x2 && x1 <= m_data[series][0][k]) { + result += m_data[series][1][k]; } } return result; } -double Store::sortedElementNumber(int k) { +double Store::sortedElementNumber(int series, int k) { // TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity - double bufferValues[m_numberOfPairs]; - memcpy(bufferValues, m_data[0], m_numberOfPairs*sizeof(double)); + double bufferValues[m_numberOfPairs[series]]; + memcpy(bufferValues, m_data[series][0], m_numberOfPairs[series]*sizeof(double)); int sortedElementIndex = 0; double cumulatedSize = 0.0; while (cumulatedSize < k) { - sortedElementIndex = minIndex(bufferValues, m_numberOfPairs); + sortedElementIndex = minIndex(bufferValues, m_numberOfPairs[series]); bufferValues[sortedElementIndex] = DBL_MAX; - cumulatedSize += m_data[1][sortedElementIndex]; + cumulatedSize += m_data[series][1][sortedElementIndex]; } - return m_data[0][sortedElementIndex]; + return m_data[series][0][sortedElementIndex]; } int Store::minIndex(double * bufferValues, int bufferLength) { diff --git a/apps/statistics/store.h b/apps/statistics/store.h index 634d3116d..f8406c963 100644 --- a/apps/statistics/store.h +++ b/apps/statistics/store.h @@ -15,38 +15,38 @@ public: void setBarWidth(double barWidth); double firstDrawnBarAbscissa() const { return m_firstDrawnBarAbscissa; } void setFirstDrawnBarAbscissa(double firstDrawnBarAbscissa) { m_firstDrawnBarAbscissa = firstBarAbscissa;} - double heightOfBarAtIndex(int index); - double heightOfBarAtValue(double value); - double startOfBarAtIndex(int index); - double endOfBarAtIndex(int index); - double numberOfBars(); + double heightOfBarAtIndex(int series, int index); + double heightOfBarAtValue(int series, double value); + double startOfBarAtIndex(int series, int index); + double endOfBarAtIndex(int series, int index); + double numberOfBars(int series); // return true if the window has scrolled - bool scrollToSelectedBarIndex(int index); + bool scrollToSelectedBarIndex(int series, int index); // Calculation - double sumOfOccurrences(); - double maxValue(); - double minValue(); - double range(); - double mean(); - double variance(); - double standardDeviation(); - double sampleStandardDeviation(); - double firstQuartile(); - double thirdQuartile(); - double quartileRange(); - double median(); - double sum(); - double squaredValueSum(); + double sumOfOccurrences(int series); + double maxValue(int series); + double minValue(int series); + double range(int series); + double mean(int series); + double variance(int series); + double standardDeviation(int series); + double sampleStandardDeviation(int series); + double firstQuartile(int series); + double thirdQuartile(int series); + double quartileRange(int series); + double median(int series); + double sum(int series); + double squaredValueSum(int series); constexpr static double k_maxNumberOfBars = 10000.0; constexpr static float k_displayTopMarginRatio = 0.1f; constexpr static float k_displayRightMarginRatio = 0.04f; constexpr static float k_displayBottomMarginRatio = 0.4f; constexpr static float k_displayLeftMarginRatio = 0.04f; private: - double defaultValue(int i, int j) override; - double sumOfValuesBetween(double x1, double x2); - double sortedElementNumber(int k); + double defaultValue(int series, int i, int j) override; + double sumOfValuesBetween(int series, double x1, double x2); + double sortedElementNumber(int series, int k); int minIndex(double * bufferValues, int bufferLength); // Histogram bars double m_barWidth;