#include "store.h" #include #include #include #include #include using namespace Shared; namespace Statistics { static_assert(Store::k_numberOfSeries == 3, "The constructor of Statistics::Store should be changed"); Store::Store() : MemoizedCurveViewRange(), DoublePairStore(), m_barWidth(1.0), m_firstDrawnBarAbscissa(0.0), m_seriesEmpty{true, true, true}, m_numberOfNonEmptySeries(0) { } uint32_t Store::barChecksum() const { double data[2] = {m_barWidth, m_firstDrawnBarAbscissa}; size_t dataLengthInBytes = 2*sizeof(double); assert((dataLengthInBytes & 0x3) == 0); // Assert that dataLengthInBytes is a multiple of 4 return Ion::crc32((uint32_t *)data, dataLengthInBytes/sizeof(uint32_t)); } /* Histogram bars */ void Store::setBarWidth(double barWidth) { if (barWidth > 0.0) { m_barWidth = barWidth; } } double Store::heightOfBarAtIndex(int series, int index) const { return sumOfValuesBetween(series, startOfBarAtIndex(series, index), endOfBarAtIndex(series, index)); } double Store::heightOfBarAtValue(int series, double value) const { 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(series, lowerBound, upperBound); } double Store::startOfBarAtIndex(int series, int index) const { double firstBarAbscissa = m_firstDrawnBarAbscissa + m_barWidth*std::floor((minValue(series)- m_firstDrawnBarAbscissa)/m_barWidth); return firstBarAbscissa + index * m_barWidth; } double Store::endOfBarAtIndex(int series, int index) const { return startOfBarAtIndex(series, index+1); } double Store::numberOfBars(int series) const { 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 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) { m_xMin = startSelectedBar - k_displayLeftMarginRatio*range; m_xMax = m_xMin + windowRange; return true; } float endSelectedBar = endOfBarAtIndex(series, index); if (endSelectedBar > m_xMax - k_displayRightMarginRatio*range) { m_xMax = endSelectedBar + k_displayRightMarginRatio*range; m_xMin = m_xMax - windowRange; return true; } return false; } bool Store::isEmpty() const { return numberOfNonEmptySeries() == 0; } bool Store::seriesIsEmpty(int i) const { return m_seriesEmpty[i]; } int Store::numberOfNonEmptySeries() const { return m_numberOfNonEmptySeries; } /* Calculation */ double Store::sumOfOccurrences(int series) const { return sumOfColumn(series, 1); } double Store::maxValueForAllSeries() const { assert(DoublePairStore::k_numberOfSeries > 0); double result = maxValue(0); for (int i = 1; i < DoublePairStore::k_numberOfSeries; i++) { double maxCurrentSeries = maxValue(i); if (result < maxCurrentSeries) { result = maxCurrentSeries; } } return result; } double Store::minValueForAllSeries() const { assert(DoublePairStore::k_numberOfSeries > 0); double result = minValue(0); for (int i = 1; i < DoublePairStore::k_numberOfSeries; i++) { double minCurrentSeries = minValue(i); if (result > minCurrentSeries) { result = minCurrentSeries; } } return result; } double Store::maxValue(int series) const { double max = -DBL_MAX; for (int k = 0; k < numberOfPairsOfSeries(series); 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(int series) const { double min = DBL_MAX; for (int k = 0; k < numberOfPairsOfSeries(series); 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(int series) const { return maxValue(series)-minValue(series); } double Store::mean(int series) const { return sum(series)/sumOfOccurrences(series); } double Store::variance(int series) const { double m = mean(series); return squaredValueSum(series)/sumOfOccurrences(series) - m*m; } double Store::standardDeviation(int series) const { return std::sqrt(variance(series)); } double Store::sampleStandardDeviation(int series) const { double n = sumOfOccurrences(series); double s = std::sqrt(n/(n-1.0)); return s*standardDeviation(series); } double Store::firstQuartile(int series) const { return sortedElementAtCumulatedFrequency(series, 1.0/4.0); } double Store::thirdQuartile(int series) const { return sortedElementAtCumulatedFrequency(series, 3.0/4.0); } double Store::quartileRange(int series) const { return thirdQuartile(series)-firstQuartile(series); } double Store::median(int series) const { return sortedElementAtCumulatedFrequency(series, 1.0/2.0, true); } double Store::sum(int series) const { double result = 0; for (int k = 0; k < numberOfPairsOfSeries(series); k++) { result += m_data[series][0][k]*m_data[series][1][k]; } return result; } double Store::squaredValueSum(int series) const { double result = 0; for (int k = 0; k < numberOfPairsOfSeries(series); k++) { result += m_data[series][0][k]*m_data[series][0][k]*m_data[series][1][k]; } return result; } void Store::set(double f, int series, int i, int j) { DoublePairStore::set(f, series, i, j); m_seriesEmpty[series] = sumOfOccurrences(series) == 0; updateNonEmptySeriesCount(); } void Store::deletePairOfSeriesAtIndex(int series, int j) { DoublePairStore::deletePairOfSeriesAtIndex(series, j); m_seriesEmpty[series] = sumOfOccurrences(series) == 0; updateNonEmptySeriesCount(); } void Store::deleteAllPairsOfSeries(int series) { DoublePairStore::deleteAllPairsOfSeries(series); m_seriesEmpty[series] = true; updateNonEmptySeriesCount(); } void Store::updateNonEmptySeriesCount() { int nonEmptySeriesCount = 0; for (int i = 0; i< k_numberOfSeries; i++) { if (!m_seriesEmpty[i]) { nonEmptySeriesCount++; } } m_numberOfNonEmptySeries = nonEmptySeriesCount; } /* Private methods */ double Store::defaultValue(int series, int i, int j) const { return i == 0 ? DoublePairStore::defaultValue(series, i, j) : 1.0; } double Store::sumOfValuesBetween(int series, double x1, double x2) const { double result = 0; for (int k = 0; k < numberOfPairsOfSeries(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::sortedElementAtCumulatedFrequency(int series, double k, bool createMiddleElement) const { // TODO: use an other algorithm (ex quickselect) to avoid quadratic complexity assert(k >= 0.0 && k <= 1.0); double totalNumberOfElements = sumOfOccurrences(series); double numberOfElementsAtFrequencyK = totalNumberOfElements * k; double bufferValues[numberOfPairsOfSeries(series)]; memcpy(bufferValues, m_data[series][0], numberOfPairsOfSeries(series)*sizeof(double)); int sortedElementIndex = 0; double cumulatedNumberOfElements = 0.0; while (cumulatedNumberOfElements < numberOfElementsAtFrequencyK-DBL_EPSILON) { sortedElementIndex = minIndex(bufferValues, numberOfPairsOfSeries(series)); bufferValues[sortedElementIndex] = DBL_MAX; cumulatedNumberOfElements += m_data[series][1][sortedElementIndex]; } if (createMiddleElement && std::fabs(cumulatedNumberOfElements - numberOfElementsAtFrequencyK) < DBL_EPSILON) { int nextElementIndex = minIndex(bufferValues, numberOfPairsOfSeries(series)); if (bufferValues[nextElementIndex] != DBL_MAX) { return (m_data[series][0][sortedElementIndex] + m_data[series][0][nextElementIndex]) / 2.0; } } return m_data[series][0][sortedElementIndex]; } int Store::minIndex(double * bufferValues, int bufferLength) const { int index = 0; for (int i = 1; i < bufferLength; i++) { if (bufferValues[index] > bufferValues[i]) { index = i; } } return index; } }