From ccae3ccdddd0da14a391edd4285f660686cb5753 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 7 Mar 2023 07:13:53 -0800 Subject: [PATCH 1/3] adding Hashable conformance to IterationValue --- Sources/Histogram/Histogram.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Histogram/Histogram.swift b/Sources/Histogram/Histogram.swift index bdba8a9..b2471bb 100644 --- a/Sources/Histogram/Histogram.swift +++ b/Sources/Histogram/Histogram.swift @@ -516,7 +516,7 @@ public struct Histogram: Codable { /** * Represents a value point iterated through in a Histogram, with associated stats. */ - public struct IterationValue: Equatable { + public struct IterationValue: Equatable, Hashable { /** * The actual value level that was iterated to by the iterator. */ From cec3193e43afe2715745fde36bd751284e1c82de Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 7 Mar 2023 07:22:36 -0800 Subject: [PATCH 2/3] adding test that uses hashable iterationValue --- Tests/HistogramTests/HistogramTests.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/HistogramTests/HistogramTests.swift b/Tests/HistogramTests/HistogramTests.swift index e5fe5d7..4eb176d 100644 --- a/Tests/HistogramTests/HistogramTests.swift +++ b/Tests/HistogramTests/HistogramTests.swift @@ -242,6 +242,20 @@ final class HistogramTests: XCTestCase { } } + func testIterationValueHashable() throws { + var h = Histogram(lowestDiscernibleValue: 1, highestTrackableValue: UInt64.max, numberOfSignificantValueDigits: .two) + + for value in 1...50 { + h.record(UInt64(value)) + } + + var cached:[Histogram.IterationValue:UInt64] = [:] + for iv in h.linearBucketValues(valueUnitsPerBucket: 5) { + cached[iv] = iv.count + } + XCTAssertEqual(cached.count, 11) + } + func testRecordValueWithExpectedInterval() throws { var histogram = Histogram(highestTrackableValue: Self.highestTrackableValue, numberOfSignificantValueDigits: Self.numberOfSignificantValueDigits) From 0e53eb5470c0d7a95b3c502cb8c2e79e865ce1d8 Mon Sep 17 00:00:00 2001 From: Joe Heck Date: Tue, 7 Mar 2023 07:29:39 -0800 Subject: [PATCH 3/3] swiftformat . && swiftlint - resolved all warnings --- Package.swift | 25 +- Sources/Histogram/Histogram.swift | 146 +++++------ .../HistogramExample/HistogramExample.swift | 10 +- .../HistogramAutosizingTests.swift | 16 +- .../HistogramDataAccessTests.swift | 174 ++++++------- Tests/HistogramTests/HistogramTests.swift | 228 +++++++++--------- 6 files changed, 306 insertions(+), 293 deletions(-) diff --git a/Package.swift b/Package.swift index 12248a2..b13b77a 100644 --- a/Package.swift +++ b/Package.swift @@ -7,20 +7,22 @@ let package = Package( name: "package-histogram", platforms: [ // specify each minimum deployment requirement, - //otherwise the platform default minimum is used. - .macOS(.v10_15), - .iOS(.v13), - .tvOS(.v13), - .watchOS(.v6) + // otherwise the platform default minimum is used. + .macOS(.v10_15), + .iOS(.v13), + .tvOS(.v13), + .watchOS(.v6) ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "Histogram", - targets: ["Histogram"]), + targets: ["Histogram"] + ), .executable( name: "HistogramExample", - targets: ["HistogramExample"]), + targets: ["HistogramExample"] + ), ], dependencies: [ // Dependencies declare other packages that this package depends on. @@ -34,15 +36,18 @@ let package = Package( name: "Histogram", dependencies: [ .product(name: "Numerics", package: "swift-numerics"), - ]), + ] + ), .executableTarget( name: "HistogramExample", - dependencies: ["Histogram"]), + dependencies: ["Histogram"] + ), .testTarget( name: "HistogramTests", dependencies: [ "Histogram", .product(name: "Numerics", package: "swift-numerics"), - ]), + ] + ), ] ) diff --git a/Sources/Histogram/Histogram.swift b/Sources/Histogram/Histogram.swift index b2471bb..e1f05c2 100644 --- a/Sources/Histogram/Histogram.swift +++ b/Sources/Histogram/Histogram.swift @@ -109,7 +109,7 @@ public struct Histogram: Codable { */ public init(numberOfSignificantValueDigits: SignificantDigits = .three) { self.init(lowestDiscernibleValue: 1, highestTrackableValue: 2, numberOfSignificantValueDigits: numberOfSignificantValueDigits) - self.autoResize = true + autoResize = true } /** @@ -202,7 +202,7 @@ public struct Histogram: Codable { @inlinable @inline(__always) public mutating func record(_ value: UInt64) -> Bool { - return record(value, count: 1) + record(value, count: 1) } /** @@ -257,7 +257,7 @@ public struct Histogram: Codable { @inlinable @inline(__always) public mutating func recordCorrectedValue(_ value: UInt64, expectedInterval: UInt64) -> Bool { - return recordCorrectedValue(value, count: 1, expectedInterval: expectedInterval) + recordCorrectedValue(value, count: 1, expectedInterval: expectedInterval) } /** @@ -300,7 +300,7 @@ public struct Histogram: Codable { * Reset the contents and stats of this histogram */ public mutating func reset() { - for i in 0..: Codable { let countAtPercentile = Swift.max(1, UInt64(fpCountAtPercentile.rounded(.up))) var totalToCurrentIndex: UInt64 = 0 - for i in 0..= countAtPercentile { let valueAtIndex = valueFromIndex(i) return (percentile == 0.0) ? - lowestEquivalentForValue(valueAtIndex) : - highestEquivalentForValue(valueAtIndex) + lowestEquivalentForValue(valueAtIndex) : + highestEquivalentForValue(valueAtIndex) } } return 0 @@ -369,7 +369,7 @@ public struct Histogram: Codable { } let targetIndex = Swift.min(countsIndexForValue(value), counts.count - 1) var totalToCurrentIndex: UInt64 = 0 - for i in 0...targetIndex { + for i in 0 ... targetIndex { totalToCurrentIndex += UInt64(counts[i]) } return (100.0 * Double(totalToCurrentIndex)) / Double(totalCount) @@ -390,7 +390,7 @@ public struct Histogram: Codable { let lowIndex = Swift.max(0, countsIndexForValue(range.lowerBound)) let highIndex = Swift.min(countsIndexForValue(range.upperBound), counts.count - 1) var count: UInt64 = 0 - for i in lowIndex...highIndex { + for i in lowIndex ... highIndex { count += UInt64(counts[i]) } return count @@ -408,7 +408,7 @@ public struct Histogram: Codable { * - Returns: `true` if values are equivalent with the histogram's resolution, `false` otherwise. */ public func valuesAreEquivalent(_ value1: UInt64, _ value2: UInt64) -> Bool { - return lowestEquivalentForValue(value1) == lowestEquivalentForValue(value2) + lowestEquivalentForValue(value1) == lowestEquivalentForValue(value2) } /** @@ -418,7 +418,7 @@ public struct Histogram: Codable { */ public var estimatedFootprintInBytes: Int { // Estimate overhead as 512 bytes - return 512 + counts.capacity * MemoryLayout.stride + 512 + counts.capacity * MemoryLayout.stride } /** @@ -431,7 +431,7 @@ public struct Histogram: Codable { * `>= lowestEquivalentForValue(value)` and `<= `highestEquivalentForValue(value)`. */ public func countForValue(_ value: UInt64) -> Count { - return counts[countsIndexForValue(value)] + counts[countsIndexForValue(value)] } /** @@ -442,7 +442,7 @@ public struct Histogram: Codable { * - Returns: The minimum value recorded in the histogram. */ public var min: UInt64 { - return (counts[0] > 0 || totalCount == 0) ? 0 : minNonZeroValue + (counts[0] > 0 || totalCount == 0) ? 0 : minNonZeroValue } /** @@ -453,7 +453,7 @@ public struct Histogram: Codable { * - Returns: The maximum value recorded in the histogram. */ public var max: UInt64 { - return maxValue == 0 ? 0 : highestEquivalentForValue(maxValue) + maxValue == 0 ? 0 : highestEquivalentForValue(maxValue) } /** @@ -464,7 +464,7 @@ public struct Histogram: Codable { * - Returns: The lowest recorded non-zero value level in the histogram. */ public var minNonZero: UInt64 { - return minNonZeroValue == UInt64.max ? UInt64.max : lowestEquivalentForValue(minNonZeroValue) + minNonZeroValue == UInt64.max ? UInt64.max : lowestEquivalentForValue(minNonZeroValue) } /** @@ -493,7 +493,7 @@ public struct Histogram: Codable { return 0.0 } - let mean = self.mean + let mean = mean var geometricDeviationTotal = 0.0 for iv in recordedValues() { @@ -596,7 +596,7 @@ public struct Histogram: Codable { } var hasNext: Bool { - return totalCountToCurrentIndex < arrayTotalCount + totalCountToCurrentIndex < arrayTotalCount } mutating func moveNext() { @@ -611,7 +611,7 @@ public struct Histogram: Codable { } mutating func makeIterationValueAndUpdatePrev(value: UInt64? = nil, percentileIteratedTo: Double? = nil) -> IterationValue { - let valueIteratedTo = value ?? self.valueIteratedTo + let valueIteratedTo = value ?? valueIteratedTo defer { prevValueIteratedTo = valueIteratedTo @@ -621,10 +621,11 @@ public struct Histogram: Codable { let percentile = (100.0 * Double(totalCountToCurrentIndex)) / Double(arrayTotalCount) return IterationValue( - value: valueIteratedTo, prevValue: prevValueIteratedTo, count: countAtThisValue, - percentile: percentile, percentileLevelIteratedTo: percentileIteratedTo ?? percentile, - countAddedInThisIterationStep: totalCountToCurrentIndex - totalCountToPrevIndex, - totalCountToThisValue: totalCountToCurrentIndex, totalValueToThisValue: totalValueToCurrentIndex) + value: valueIteratedTo, prevValue: prevValueIteratedTo, count: countAtThisValue, + percentile: percentile, percentileLevelIteratedTo: percentileIteratedTo ?? percentile, + countAddedInThisIterationStep: totalCountToCurrentIndex - totalCountToPrevIndex, + totalCountToThisValue: totalCountToCurrentIndex, totalValueToThisValue: totalValueToCurrentIndex + ) } var exhaustedSubBuckets: Bool { currentIndex >= histogram.counts.count } @@ -714,7 +715,7 @@ public struct Histogram: Codable { } let percentileReportingTicks = UInt64(percentileTicksPerHalfDistance) * - UInt64(.pow(2.0, Int(.log2(100.0 / (100.0 - percentileLevelToIterateTo))) + 1)) + UInt64(.pow(2.0, Int(.log2(100.0 / (100.0 - percentileLevelToIterateTo))) + 1)) percentileLevelToIterateTo += 100.0 / Double(percentileReportingTicks) } } @@ -773,8 +774,8 @@ public struct Histogram: Codable { } private var reachedIterationLevel: Bool { - return impl.currentValueAtIndex >= currentStepLowestValueReportingLevel || - impl.currentIndex >= impl.histogram.counts.count - 1 + impl.currentValueAtIndex >= currentStepLowestValueReportingLevel || + impl.currentIndex >= impl.histogram.counts.count - 1 } private mutating func incrementIterationLevel() { @@ -836,8 +837,8 @@ public struct Histogram: Codable { } private var reachedIterationLevel: Bool { - return impl.currentValueAtIndex >= currentStepLowestValueReportingLevel || - impl.currentIndex >= impl.histogram.counts.count - 1 + impl.currentValueAtIndex >= currentStepLowestValueReportingLevel || + impl.currentIndex >= impl.histogram.counts.count - 1 } private mutating func incrementIterationLevel() { @@ -924,11 +925,11 @@ public struct Histogram: Codable { private var hasNext: Bool { // Unlike other iterators AllValuesIterator is only done when we've exhausted the indices: - return impl.currentIndex < impl.histogram.counts.count - 1 + impl.currentIndex < impl.histogram.counts.count - 1 } private var reachedIterationLevel: Bool { - return visitedIndex != impl.currentIndex + visitedIndex != impl.currentIndex } private mutating func incrementIterationLevel() { @@ -949,7 +950,7 @@ public struct Histogram: Codable { * - Returns: An object implementing `Sequence` protocol over ``IterationValue``. */ public func percentiles(ticksPerHalfDistance: Int) -> Percentiles { - return Percentiles(histogram: self, percentileTicksPerHalfDistance: ticksPerHalfDistance) + Percentiles(histogram: self, percentileTicksPerHalfDistance: ticksPerHalfDistance) } /** @@ -964,7 +965,7 @@ public struct Histogram: Codable { * - Returns: An object implementing `Sequence` protocol over ``IterationValue``. */ public func linearBucketValues(valueUnitsPerBucket: UInt64) -> LinearBucketValues { - return LinearBucketValues(histogram: self, valueUnitsPerBucket: valueUnitsPerBucket) + LinearBucketValues(histogram: self, valueUnitsPerBucket: valueUnitsPerBucket) } /** @@ -980,7 +981,7 @@ public struct Histogram: Codable { * - Returns: An object implementing `Sequence` protocol over ``IterationValue``. */ public func logarithmicBucketValues(valueUnitsInFirstBucket: UInt64, logBase: Double) -> LogarithmicBucketValues { - return LogarithmicBucketValues(histogram: self, valueUnitsInFirstBucket: valueUnitsInFirstBucket, logBase: logBase) + LogarithmicBucketValues(histogram: self, valueUnitsInFirstBucket: valueUnitsInFirstBucket, logBase: logBase) } /** @@ -991,7 +992,7 @@ public struct Histogram: Codable { * - Returns: An object implementing `Sequence` protocol over ``IterationValue``. */ public func recordedValues() -> RecordedValues { - return RecordedValues(histogram: self) + RecordedValues(histogram: self) } /** @@ -1003,7 +1004,7 @@ public struct Histogram: Codable { * - Returns: An object implementing `Sequence` protocol over ``IterationValue``. */ public func allValues() -> AllValues { - return AllValues(histogram: self) + AllValues(histogram: self) } // MARK: Textual percentile output support. @@ -1020,11 +1021,12 @@ public struct Histogram: Codable { * - outputValueUnitScalingRatio: The scaling factor by which to divide histogram recorded values units in output. * - format: The output format. */ - public func outputPercentileDistribution( - to stream: inout Stream, - outputValueUnitScalingRatio: Double, - percentileTicksPerHalfDistance ticks: Int = 5, - format: HistogramOutputFormat = .plainText) { + public func outputPercentileDistribution( + to stream: inout some TextOutputStream, + outputValueUnitScalingRatio: Double, + percentileTicksPerHalfDistance ticks: Int = 5, + format: HistogramOutputFormat = .plainText + ) { // small helper to pad strings to specific widths, for some reason "%10s"/"%10@" doesn't work in String.init(format:) func padded(_ s: String, to: Int) -> String { if s.count < to { @@ -1040,27 +1042,29 @@ public struct Histogram: Codable { } let percentileFormatString = format == .csv ? - "%.\(numberOfSignificantValueDigits.rawValue)f,%.12f,%d,%.2f\n" : - "%12.\(numberOfSignificantValueDigits.rawValue)f %2.12f %10d %14.2f\n" + "%.\(numberOfSignificantValueDigits.rawValue)f,%.12f,%d,%.2f\n" : + "%12.\(numberOfSignificantValueDigits.rawValue)f %2.12f %10d %14.2f\n" let lastLinePercentileFormatString = format == .csv ? - "%.\(numberOfSignificantValueDigits.rawValue)f,%.12f,%d,Infinity\n" : - "%12.\(numberOfSignificantValueDigits.rawValue)f %2.12f %10d\n" + "%.\(numberOfSignificantValueDigits.rawValue)f,%.12f,%d,Infinity\n" : + "%12.\(numberOfSignificantValueDigits.rawValue)f %2.12f %10d\n" for iv in percentiles(ticksPerHalfDistance: ticks) { if iv.percentileLevelIteratedTo != 100.0 { stream.write(String( - format: percentileFormatString, - Double(iv.value) / outputValueUnitScalingRatio, - iv.percentileLevelIteratedTo / 100.0, - iv.totalCountToThisValue, - 1.0 / (1.0 - (iv.percentileLevelIteratedTo / 100.0)))) + format: percentileFormatString, + Double(iv.value) / outputValueUnitScalingRatio, + iv.percentileLevelIteratedTo / 100.0, + iv.totalCountToThisValue, + 1.0 / (1.0 - (iv.percentileLevelIteratedTo / 100.0)) + )) } else { stream.write(String( - format: lastLinePercentileFormatString, - Double(iv.value) / outputValueUnitScalingRatio, - iv.percentileLevelIteratedTo / 100.0, - iv.totalCountToThisValue)) + format: lastLinePercentileFormatString, + Double(iv.value) / outputValueUnitScalingRatio, + iv.percentileLevelIteratedTo / 100.0, + iv.totalCountToThisValue + )) } } @@ -1077,12 +1081,12 @@ public struct Histogram: Codable { // deviation metric is a useless indicator. // - let mean = self.mean / outputValueUnitScalingRatio - let stdDeviation = self.stdDeviation / outputValueUnitScalingRatio + let mean = mean / outputValueUnitScalingRatio + let stdDeviation = stdDeviation / outputValueUnitScalingRatio stream.write(String(format: "#[Mean = %12.\(numberOfSignificantValueDigits.rawValue)f," + - " StdDeviation = %12.\(numberOfSignificantValueDigits.rawValue)f]\n", mean, stdDeviation)) + " StdDeviation = %12.\(numberOfSignificantValueDigits.rawValue)f]\n", mean, stdDeviation)) stream.write(String(format: "#[Max = %12.\(numberOfSignificantValueDigits.rawValue)f," + - " Total count = %12d]\n", Double(max) / outputValueUnitScalingRatio, totalCount)) + " Total count = %12d]\n", Double(max) / outputValueUnitScalingRatio, totalCount)) stream.write(String(format: "#[Buckets = %12d, SubBuckets = %12d]\n", bucketCount, subBucketCount)) } } @@ -1131,7 +1135,7 @@ public struct Histogram: Codable { * - Returns: The highest value that is equivalent to the given value within the histogram's resolution. */ public func highestEquivalentForValue(_ value: UInt64) -> UInt64 { - return nextNonEquivalentForValue(value) - 1 + nextNonEquivalentForValue(value) - 1 } /** @@ -1161,7 +1165,7 @@ public struct Histogram: Codable { * - Returns: The value lies in the middle (rounded up) of the range of values equivalent the given value. */ public func medianEquivalentForValue(_ value: UInt64) -> UInt64 { - return lowestEquivalentForValue(value) + (sizeOfEquivalentRangeForValue(value) >> 1) + lowestEquivalentForValue(value) + (sizeOfEquivalentRangeForValue(value) >> 1) } /** @@ -1173,7 +1177,7 @@ public struct Histogram: Codable { * - Returns: The next value that is not equivalent to the given value within the histogram's resolution. */ public func nextNonEquivalentForValue(_ value: UInt64) -> UInt64 { - return lowestEquivalentForValue(value) + sizeOfEquivalentRangeForValue(value) + lowestEquivalentForValue(value) + sizeOfEquivalentRangeForValue(value) } func valueFromIndex(_ index: Int) -> UInt64 { @@ -1189,7 +1193,7 @@ public struct Histogram: Codable { } private func valueFrom(bucketIndex: Int, subBucketIndex: Int) -> UInt64 { - return UInt64(subBucketIndex) << (bucketIndex + Int(unitMagnitude)) + UInt64(subBucketIndex) << (bucketIndex + Int(unitMagnitude)) } @usableFromInline @@ -1206,17 +1210,17 @@ public struct Histogram: Codable { // Calculates the number of powers of two by which the value is greater than the biggest value that fits in // bucket 0. This is the bucket index since each successive bucket can hold a value 2x greater. // The mask maps small values to bucket 0 - return Int(leadingZeroCountBase) - (value | subBucketMask).leadingZeroBitCount + Int(leadingZeroCountBase) - (value | subBucketMask).leadingZeroBitCount } - func subBucketIndexForValue(_ value: UInt64, bucketIndex: Int) -> Int { + func subBucketIndexForValue(_ value: UInt64, bucketIndex: Int) -> Int { // For ``bucketIndex`` 0, this is just value, so it may be anywhere in 0 to ``subBucketCount``. // For other bucketIndex, this will always end up in the top half of subBucketCount: assume that for some bucket // `k` > 0, this calculation will yield a value in the bottom half of 0 to ``subBucketCount``. Then, because of how // buckets overlap, it would have also been in the top half of bucket `k-1`, and therefore would have // returned `k-1` in ``bucketIndexForValue()``. Since we would then shift it one fewer bits here, it would be twice as big, // and therefore in the top half of ``subBucketCount``. - return Int(value >> (bucketIndex + Int(unitMagnitude))) + Int(value >> (bucketIndex + Int(unitMagnitude))) } private func countsIndexFor(bucketIndex: Int, subBucketIndex: Int) -> Int { @@ -1247,7 +1251,7 @@ public struct Histogram: Codable { if value > maxValue { maxValue = value } - if value < minNonZeroValue && value != 0 { + if value < minNonZeroValue, value != 0 { minNonZeroValue = value } } @@ -1285,7 +1289,7 @@ public struct Histogram: Codable { * value if we consider the sub-bucket length to be halved. */ static func countsArrayLengthFor(bucketCount: Int, subBucketHalfCount: Int) -> Int { - return (bucketCount + 1) * subBucketHalfCount + (bucketCount + 1) * subBucketHalfCount } } @@ -1300,17 +1304,17 @@ extension Histogram: Equatable { */ public static func == (lhs: Histogram, rhs: Histogram) -> Bool { if lhs.lowestDiscernibleValue != rhs.lowestDiscernibleValue || - lhs.numberOfSignificantValueDigits != rhs.numberOfSignificantValueDigits || - lhs.totalCount != rhs.totalCount || - lhs.max != rhs.max || - lhs.minNonZero != rhs.minNonZero { + lhs.numberOfSignificantValueDigits != rhs.numberOfSignificantValueDigits || + lhs.totalCount != rhs.totalCount || + lhs.max != rhs.max || + lhs.minNonZero != rhs.minNonZero { return false } // 2 histograms may be equal but have different underlying array sizes. This can happen for instance due to // resizing. if lhs.counts.count == rhs.counts.count { - for i in 0..(to: inout Target) { + public func write(to: inout some TextOutputStream) { outputPercentileDistribution(to: &to, outputValueUnitScalingRatio: 1.0) } } diff --git a/Sources/HistogramExample/HistogramExample.swift b/Sources/HistogramExample/HistogramExample.swift index bf1495d..2d85d13 100644 --- a/Sources/HistogramExample/HistogramExample.swift +++ b/Sources/HistogramExample/HistogramExample.swift @@ -12,19 +12,19 @@ import Histogram @main -struct HistogramExample { +enum HistogramExample { static func main() { let maxValue: UInt64 = 3_600_000_000 // e.g. for 1 hr in usec units var histogram = Histogram(highestTrackableValue: maxValue, numberOfSignificantValueDigits: .three) // record some random values - for _ in 1...100 { - histogram.record(UInt64.random(in: 10...1_000)) + for _ in 1 ... 100 { + histogram.record(UInt64.random(in: 10 ... 1_000)) } // record value n times - histogram.record(UInt64.random(in: 50...200), count: 10) + histogram.record(UInt64.random(in: 50 ... 200), count: 10) // record value with correction for co-ordinated omission histogram.recordCorrectedValue(1_000, expectedInterval: 100) @@ -37,7 +37,7 @@ struct HistogramExample { print(String(repeating: "-", count: 80)) // print values for interesting percentiles - let percentiles = [ 0.0, 50.0, 80.0, 95.0, 99.0, 99.9, 99.99, 99.999, 100.0 ] + let percentiles = [0.0, 50.0, 80.0, 95.0, 99.0, 99.9, 99.99, 99.999, 100.0] for p in percentiles { print("Percentile: \(p), Value: \(histogram.valueAtPercentile(p))") } diff --git a/Tests/HistogramTests/HistogramAutosizingTests.swift b/Tests/HistogramTests/HistogramAutosizingTests.swift index cc9a57c..77621c6 100644 --- a/Tests/HistogramTests/HistogramAutosizingTests.swift +++ b/Tests/HistogramTests/HistogramAutosizingTests.swift @@ -8,7 +8,7 @@ // http://www.apache.org/licenses/LICENSE-2.0 // -// swiftlint:disable identifier_name +// swiftlint:disable identifier_name todo @testable import Histogram import XCTest @@ -55,7 +55,7 @@ final class HistogramAutosizingTests: XCTestCase { func testHistogramAutoSizing() { var histogram = Histogram(numberOfSignificantValueDigits: .three) - for i in 0..<63 { + for i in 0 ..< 63 { histogram.record(UInt64(1) << i) } @@ -65,23 +65,23 @@ final class HistogramAutosizingTests: XCTestCase { func testAutoSizingAdd() throws { var histogram1 = Histogram(numberOfSignificantValueDigits: .two) - // let histogram2 = Histogram(numberOfSignificantValueDigits: .two) + // let histogram2 = Histogram(numberOfSignificantValueDigits: .two) histogram1.record(1_000) histogram1.record(1_000_000_000) - // FIXME + // FIXME: throw XCTSkip("Histogram.add() is not implemented yet") - //histogram2.add(histogram1) + // histogram2.add(histogram1) - // XCTAssert(histogram2.valuesAreEquivalent(histogram2.max, 1_000_000_000), - // "Max should be equivalent to 1_000_000_000") + // XCTAssert(histogram2.valuesAreEquivalent(histogram2.max, 1_000_000_000), + // "Max should be equivalent to 1_000_000_000") } func testAutoSizingAcrossContinuousRange() { var histogram = Histogram(numberOfSignificantValueDigits: .two) - for i: UInt64 in 0..<10_000_000 { + for i: UInt64 in 0 ..< 10_000_000 { histogram.record(i) } } diff --git a/Tests/HistogramTests/HistogramDataAccessTests.swift b/Tests/HistogramTests/HistogramDataAccessTests.swift index c77d29e..f98258b 100644 --- a/Tests/HistogramTests/HistogramDataAccessTests.swift +++ b/Tests/HistogramTests/HistogramDataAccessTests.swift @@ -8,7 +8,7 @@ // http://www.apache.org/licenses/LICENSE-2.0 // -// swiftlint:disable file_length identifier_name line_length number_separator trailing_comma +// swiftlint:disable file_length identifier_name line_length trailing_comma @testable import Histogram import Numerics @@ -16,39 +16,41 @@ import XCTest // swiftlint:disable:next type_body_length final class HistogramDataAccessTests: XCTestCase { - private static let highestTrackableValue = UInt64(3600) * 1000 * 1000 // e.g. for 1 hr in usec units + private static let highestTrackableValue = UInt64(3_600) * 1_000 * 1_000 // e.g. for 1 hr in usec units private static let numberOfSignificantValueDigits = SignificantDigits.three private static let value: UInt64 = 4 private static var histogram = Histogram(highestTrackableValue: highestTrackableValue, numberOfSignificantValueDigits: numberOfSignificantValueDigits) private static var scaledHistogram = Histogram( - lowestDiscernibleValue: 1000, - highestTrackableValue: highestTrackableValue * 512, - numberOfSignificantValueDigits: numberOfSignificantValueDigits) + lowestDiscernibleValue: 1_000, + highestTrackableValue: highestTrackableValue * 512, + numberOfSignificantValueDigits: numberOfSignificantValueDigits + ) private static var rawHistogram = Histogram(highestTrackableValue: highestTrackableValue, numberOfSignificantValueDigits: numberOfSignificantValueDigits) private static var scaledRawHistogram = Histogram( - lowestDiscernibleValue: 1000, - highestTrackableValue: highestTrackableValue * 512, - numberOfSignificantValueDigits: numberOfSignificantValueDigits) + lowestDiscernibleValue: 1_000, + highestTrackableValue: highestTrackableValue * 512, + numberOfSignificantValueDigits: numberOfSignificantValueDigits + ) override class func setUp() { // Log hypothetical scenario: 100 seconds of "perfect" 1msec results, sampled // 100 times per second (10,000 results), followed by a 100 second pause with // a single (100 second) recorded result. Recording is done indicating an expected // interval between samples of 10 msec: - for _ in 0..<10_000 { - histogram.recordCorrectedValue(1000 /* 1 msec */, expectedInterval: 10_000 /* 10 msec expected interval */) - scaledHistogram.recordCorrectedValue(1000 * 512 /* 1 msec */, expectedInterval: 10_000 * 512 /* 10 msec expected interval */) - rawHistogram.record(1000 /* 1 msec */) - scaledRawHistogram.record(1000 * 512/* 1 msec */) + for _ in 0 ..< 10_000 { + histogram.recordCorrectedValue(1_000 /* 1 msec */, expectedInterval: 10_000 /* 10 msec expected interval */ ) + scaledHistogram.recordCorrectedValue(1_000 * 512 /* 1 msec */, expectedInterval: 10_000 * 512 /* 10 msec expected interval */ ) + rawHistogram.record(1_000 /* 1 msec */ ) + scaledRawHistogram.record(1_000 * 512 /* 1 msec */ ) } - histogram.recordCorrectedValue(100_000_000 /* 100 sec */, expectedInterval: 10_000 /* 10 msec expected interval */) - scaledHistogram.recordCorrectedValue(100_000_000 * 512 /* 100 sec */, expectedInterval: 10_000 * 512 /* 10 msec expected interval */) - rawHistogram.record(100_000_000 /* 100 sec */) - scaledRawHistogram.record(100_000_000 * 512 /* 100 sec */) + histogram.recordCorrectedValue(100_000_000 /* 100 sec */, expectedInterval: 10_000 /* 10 msec expected interval */ ) + scaledHistogram.recordCorrectedValue(100_000_000 * 512 /* 100 sec */, expectedInterval: 10_000 * 512 /* 10 msec expected interval */ ) + rawHistogram.record(100_000_000 /* 100 sec */ ) + scaledRawHistogram.record(100_000_000 * 512 /* 100 sec */ ) } func testScalingEquivalence() { @@ -66,30 +68,30 @@ final class HistogramDataAccessTests: XCTestCase { } func testMax() { - XCTAssertTrue(Self.histogram.valuesAreEquivalent(100 * 1000 * 1000, Self.histogram.max)) + XCTAssertTrue(Self.histogram.valuesAreEquivalent(100 * 1_000 * 1_000, Self.histogram.max)) } func testMin() { - XCTAssertTrue(Self.histogram.valuesAreEquivalent(1000, Self.histogram.min)) + XCTAssertTrue(Self.histogram.valuesAreEquivalent(1_000, Self.histogram.min)) } func testMean() { - let expectedRawMean = ((10_000.0 * 1000) + (1.0 * 100_000_000)) / 10_001 // direct avg. of raw results - let expectedMean = (1000.0 + 50_000_000.0) / 2 // avg. 1 msec for half the time, and 50 sec for other half + let expectedRawMean = ((10_000.0 * 1_000) + (1.0 * 100_000_000)) / 10_001 // direct avg. of raw results + let expectedMean = (1_000.0 + 50_000_000.0) / 2 // avg. 1 msec for half the time, and 50 sec for other half // We expect to see the mean to be accurate to ~3 decimal points (~0.1%): XCTAssertEqual(expectedRawMean, Self.rawHistogram.mean, accuracy: expectedRawMean * 0.001, "Raw mean is \(expectedRawMean) +/- 0.1%") XCTAssertEqual(expectedMean, Self.histogram.mean, accuracy: expectedMean * 0.001, "Mean is \(expectedMean) +/- 0.1%") } func testStdDeviation() { - let expectedRawMean = ((10_000.0 * 1000) + (1.0 * 100_000_000)) / 10_001 // direct avg. of raw results - let expectedRawStdDev = (((10_000.0 * .pow((1000.0 - expectedRawMean), 2)) + .pow((100_000_000.0 - expectedRawMean), 2)) / 10_001).squareRoot() + let expectedRawMean = ((10_000.0 * 1_000) + (1.0 * 100_000_000)) / 10_001 // direct avg. of raw results + let expectedRawStdDev = (((10_000.0 * .pow(1_000.0 - expectedRawMean, 2)) + .pow(100_000_000.0 - expectedRawMean, 2)) / 10_001).squareRoot() - let expectedMean = (1000.0 + 50_000_000.0) / 2 // avg. 1 msec for half the time, and 50 sec for other half - var expectedSquareDeviationSum = 10_000 * .pow((1000.0 - expectedMean), 2) + let expectedMean = (1_000.0 + 50_000_000.0) / 2 // avg. 1 msec for half the time, and 50 sec for other half + var expectedSquareDeviationSum = 10_000 * .pow(1_000.0 - expectedMean, 2) for value in stride(from: 10_000, through: 100_000_000, by: 10_000) { - expectedSquareDeviationSum += .pow((Double(value) - expectedMean), 2) + expectedSquareDeviationSum += .pow(Double(value) - expectedMean, 2) } let expectedStdDev = (expectedSquareDeviationSum / 20_000).squareRoot() @@ -120,7 +122,7 @@ final class HistogramDataAccessTests: XCTestCase { } func testValueAtPercentileExamples() { - var hist = Histogram(highestTrackableValue: 3600_000_000, numberOfSignificantValueDigits: .three) + var hist = Histogram(highestTrackableValue: 3_600_000_000, numberOfSignificantValueDigits: .three) hist.record(1) hist.record(2) @@ -138,22 +140,22 @@ final class HistogramDataAccessTests: XCTestCase { } func testValueAtPercentile() { - XCTAssertEqual(1000.0, Double(Self.rawHistogram.valueAtPercentile(30.0)), - accuracy: 1000.0 * 0.001, "raw 30%'ile is 1 msec +/- 0.1%") - XCTAssertEqual(1000.0, Double(Self.rawHistogram.valueAtPercentile(99.0)), - accuracy: 1000.0 * 0.001, "raw 99%'ile is 1 msec +/- 0.1%") - XCTAssertEqual(1000.0, Double(Self.rawHistogram.valueAtPercentile(99.99)), - accuracy: 1000.0 * 0.001, "raw 99.99%'ile is 1 msec +/- 0.1%") + XCTAssertEqual(1_000.0, Double(Self.rawHistogram.valueAtPercentile(30.0)), + accuracy: 1_000.0 * 0.001, "raw 30%'ile is 1 msec +/- 0.1%") + XCTAssertEqual(1_000.0, Double(Self.rawHistogram.valueAtPercentile(99.0)), + accuracy: 1_000.0 * 0.001, "raw 99%'ile is 1 msec +/- 0.1%") + XCTAssertEqual(1_000.0, Double(Self.rawHistogram.valueAtPercentile(99.99)), + accuracy: 1_000.0 * 0.001, "raw 99.99%'ile is 1 msec +/- 0.1%") XCTAssertEqual(100_000_000.0, Double(Self.rawHistogram.valueAtPercentile(99.999)), accuracy: 100_000_000.0 * 0.001, "raw 99.999%'ile is 100 sec +/- 0.1%") XCTAssertEqual(100_000_000.0, Double(Self.rawHistogram.valueAtPercentile(100.0)), accuracy: 100_000_000.0 * 0.001, "raw 100%'ile is 100 sec +/- 0.1%") - XCTAssertEqual(1000.0, Double(Self.histogram.valueAtPercentile(30.0)), - accuracy: 1000.0 * 0.001, "30%'ile is 1 msec +/- 0.1%") - XCTAssertEqual(1000.0, Double(Self.histogram.valueAtPercentile(50.0)), - accuracy: 1000.0 * 0.001, "50%'ile is 1 msec +/- 0.1%") + XCTAssertEqual(1_000.0, Double(Self.histogram.valueAtPercentile(30.0)), + accuracy: 1_000.0 * 0.001, "30%'ile is 1 msec +/- 0.1%") + XCTAssertEqual(1_000.0, Double(Self.histogram.valueAtPercentile(50.0)), + accuracy: 1_000.0 * 0.001, "50%'ile is 1 msec +/- 0.1%") XCTAssertEqual(50_000_000.0, Double(Self.histogram.valueAtPercentile(75.0)), accuracy: 50_000_000.0 * 0.001, "75%'ile is 50 sec +/- 0.1%") @@ -179,41 +181,42 @@ final class HistogramDataAccessTests: XCTestCase { } func testPercentileAtOrBelowValue() { - XCTAssertEqual(99.99, Self.rawHistogram.percentileAtOrBelowValue(5000), + XCTAssertEqual(99.99, Self.rawHistogram.percentileAtOrBelowValue(5_000), accuracy: 0.0001, "Raw percentile at or below 5 msec is 99.99% +/- 0.0001") - XCTAssertEqual(50.0, Self.histogram.percentileAtOrBelowValue(5000), + XCTAssertEqual(50.0, Self.histogram.percentileAtOrBelowValue(5_000), accuracy: 0.0001, "Percentile at or below 5 msec is 50% +/- 0.0001%") XCTAssertEqual(100.0, Self.histogram.percentileAtOrBelowValue(100_000_000), accuracy: 0.0001, "Percentile at or below 100 sec is 100% +/- 0.0001%") } func testCountWithinRange() { - XCTAssertEqual(10_000, Self.rawHistogram.count(within: 1000...1000), + XCTAssertEqual(10_000, Self.rawHistogram.count(within: 1_000 ... 1_000), "Count of raw values between 1 msec and 1 msec is 1") - XCTAssertEqual(1, Self.rawHistogram.count(within: 5000...150_000_000), + XCTAssertEqual(1, Self.rawHistogram.count(within: 5_000 ... 150_000_000), "Count of raw values between 5 msec and 150 sec is 1") - XCTAssertEqual(10_000, Self.histogram.count(within: 5000...150_000_000), + XCTAssertEqual(10_000, Self.histogram.count(within: 5_000 ... 150_000_000), "Count of values between 5 msec and 150 sec is 10,000") } func testCountForValue() { - XCTAssertEqual(0, Self.rawHistogram.count(within: 10_000...10_010), + XCTAssertEqual(0, Self.rawHistogram.count(within: 10_000 ... 10_010), "Count of raw values at 10 msec is 0") - XCTAssertEqual(1, Self.histogram.count(within: 10_000...10_010), + XCTAssertEqual(1, Self.histogram.count(within: 10_000 ... 10_010), "Count of values at 10 msec is 0") - XCTAssertEqual(10_000, Self.rawHistogram.countForValue(1000), + XCTAssertEqual(10_000, Self.rawHistogram.countForValue(1_000), "Count of raw values at 1 msec is 10,000") - XCTAssertEqual(10_000, Self.histogram.countForValue(1000), + XCTAssertEqual(10_000, Self.histogram.countForValue(1_000), "Count of values at 1 msec is 10,000") } func testPercentiles() { for iv in Self.histogram.percentiles(ticksPerHalfDistance: 5) { XCTAssertEqual( - iv.value, Self.histogram.highestEquivalentForValue(Self.histogram.valueAtPercentile(iv.percentile)), - "Iterator value: \(iv.value), count: \(iv.count), percentile: \(iv.percentile)\n" + + iv.value, Self.histogram.highestEquivalentForValue(Self.histogram.valueAtPercentile(iv.percentile)), + "Iterator value: \(iv.value), count: \(iv.count), percentile: \(iv.percentile)\n" + "histogram valueAtPercentile(\(iv.percentile)): \(Self.histogram.valueAtPercentile(iv.percentile)), " + - "highest equivalent value: \(Self.histogram.highestEquivalentForValue(Self.histogram.valueAtPercentile(iv.percentile)))") + "highest equivalent value: \(Self.histogram.highestEquivalentForValue(Self.histogram.valueAtPercentile(iv.percentile)))" + ) } } @@ -222,7 +225,7 @@ final class HistogramDataAccessTests: XCTestCase { var histogram = H(highestTrackableValue: 10_000, numberOfSignificantValueDigits: .three) - for i in 1...10 { + for i in 1 ... 10 { histogram.record(UInt64(i)) } @@ -252,23 +255,24 @@ final class HistogramDataAccessTests: XCTestCase { // Iterate data using linear buckets of 100 msec each. var index = 0 - for iv in Self.rawHistogram.linearBucketValues(valueUnitsPerBucket: 100_000 /* 100 msec */) { + for iv in Self.rawHistogram.linearBucketValues(valueUnitsPerBucket: 100_000 /* 100 msec */ ) { let countAddedInThisBucket = iv.countAddedInThisIterationStep if index == 0 { XCTAssertEqual(10_000, countAddedInThisBucket, "Raw Linear 100 msec bucket # 0 added a count of 10000") } else if index == 999 { - XCTAssertEqual(1, countAddedInThisBucket, "Raw Linear 100 msec bucket # 999 added a count of 1") } else { + XCTAssertEqual(1, countAddedInThisBucket, "Raw Linear 100 msec bucket # 999 added a count of 1") + } else { XCTAssertEqual(0, countAddedInThisBucket, "Raw Linear 100 msec bucket # \(index) added a count of 0") } index += 1 } - XCTAssertEqual(1000, index) + XCTAssertEqual(1_000, index) // Iterate data using linear buckets of 10 msec each. index = 0 var totalAddedCounts: UInt64 = 0 - for iv in Self.histogram.linearBucketValues(valueUnitsPerBucket: 10_000 /* 10 msec */) { + for iv in Self.histogram.linearBucketValues(valueUnitsPerBucket: 10_000 /* 10 msec */ ) { let countAddedInThisBucket = iv.countAddedInThisIterationStep if index == 0 { XCTAssertEqual(10_000, countAddedInThisBucket, "Linear 1 sec bucket # 0 [\(iv.prevValue)..\(iv.value)] added a count of 10000") @@ -288,7 +292,7 @@ final class HistogramDataAccessTests: XCTestCase { // Iterate data using linear buckets of 1 msec each. index = 0 totalAddedCounts = 0 - for iv in Self.histogram.linearBucketValues(valueUnitsPerBucket: 1000 /* 1 msec */) { + for iv in Self.histogram.linearBucketValues(valueUnitsPerBucket: 1_000 /* 1 msec */ ) { let countAddedInThisBucket = iv.countAddedInThisIterationStep if index == 1 { XCTAssertEqual(10_000, countAddedInThisBucket, "Linear 1 sec bucket # 0 [\(iv.prevValue)..\(iv.value)] added a count of 10000") @@ -368,7 +372,7 @@ final class HistogramDataAccessTests: XCTestCase { XCTAssertNotEqual(iv.count, 0, "The count in recorded bucket #\(index) is not 0") XCTAssertEqual(iv.count, countAddedInThisBucket, "The count in recorded bucket # \(index)" + - " is exactly the amount added since the last iteration") + " is exactly the amount added since the last iteration") totalAddedCounts += countAddedInThisBucket index += 1 } @@ -383,7 +387,7 @@ final class HistogramDataAccessTests: XCTestCase { // Iterate raw data by stepping through every value that has a count recorded: for v in Self.rawHistogram.allValues() { let countAddedInThisBucket = v.countAddedInThisIterationStep - if index == 1000 { + if index == 1_000 { XCTAssertEqual(10_000, countAddedInThisBucket, "Raw allValues bucket # 0 added a count of 10000") } else if Self.histogram.valuesAreEquivalent(v.value, 100_000_000) { XCTAssertEqual(1, countAddedInThisBucket, "Raw allValues value bucket # \(index) added a count of 1") @@ -403,13 +407,13 @@ final class HistogramDataAccessTests: XCTestCase { for v in Self.histogram.allValues() { let countAddedInThisBucket = v.countAddedInThisIterationStep - if index == 1000 { + if index == 1_000 { XCTAssertEqual(10_000, countAddedInThisBucket, "AllValues bucket # 0 [\(v.prevValue)..\(v.value)] added a count of 10000") } XCTAssertEqual(v.count, countAddedInThisBucket, "The count in AllValues bucket # \(index)" + - " is exactly the amount added since the last iteration") + " is exactly the amount added since the last iteration") totalAddedCounts += countAddedInThisBucket XCTAssertTrue(Self.histogram.valuesAreEquivalent(Self.histogram.valueFromIndex(index), v.value), "valueFromIndex() should be equal to value") index += 1 @@ -437,18 +441,18 @@ final class HistogramDataAccessTests: XCTestCase { var h = Histogram(lowestDiscernibleValue: 1, highestTrackableValue: UInt64.max, numberOfSignificantValueDigits: .three) h.record(1) - h.record(2047) + h.record(2_047) // bucket size 2 - h.record(2048) - h.record(2049) - h.record(4095) + h.record(2_048) + h.record(2_049) + h.record(4_095) // bucket size 4 - h.record(4096) - h.record(4097) - h.record(4098) - h.record(4099) + h.record(4_096) + h.record(4_097) + h.record(4_098) + h.record(4_099) // 2nd bucket in size 4 - h.record(4100) + h.record(4_100) struct IteratorValueSnapshot: Equatable { let value: UInt64 @@ -464,26 +468,26 @@ final class HistogramDataAccessTests: XCTestCase { // bucket size 1 XCTAssertEqual(IteratorValueSnapshot(value: 0, count: 0), snapshots[0]) XCTAssertEqual(IteratorValueSnapshot(value: 1, count: 1), snapshots[1]) - XCTAssertEqual(IteratorValueSnapshot(value: 2046, count: 0), snapshots[2046]) - XCTAssertEqual(IteratorValueSnapshot(value: 2047, count: 1), snapshots[2047]) + XCTAssertEqual(IteratorValueSnapshot(value: 2_046, count: 0), snapshots[2_046]) + XCTAssertEqual(IteratorValueSnapshot(value: 2_047, count: 1), snapshots[2_047]) // bucket size 2 - XCTAssertEqual(IteratorValueSnapshot(value: 2048, count: 2), snapshots[2048]) - XCTAssertEqual(IteratorValueSnapshot(value: 2049, count: 0), snapshots[2049]) - XCTAssertEqual(IteratorValueSnapshot(value: 2050, count: 0), snapshots[2050]) - XCTAssertEqual(IteratorValueSnapshot(value: 2051, count: 0), snapshots[2051]) - XCTAssertEqual(IteratorValueSnapshot(value: 4094, count: 1), snapshots[4094]) - XCTAssertEqual(IteratorValueSnapshot(value: 4095, count: 0), snapshots[4095]) + XCTAssertEqual(IteratorValueSnapshot(value: 2_048, count: 2), snapshots[2_048]) + XCTAssertEqual(IteratorValueSnapshot(value: 2_049, count: 0), snapshots[2_049]) + XCTAssertEqual(IteratorValueSnapshot(value: 2_050, count: 0), snapshots[2_050]) + XCTAssertEqual(IteratorValueSnapshot(value: 2_051, count: 0), snapshots[2_051]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_094, count: 1), snapshots[4_094]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_095, count: 0), snapshots[4_095]) // bucket size 4 - XCTAssertEqual(IteratorValueSnapshot(value: 4096, count: 4), snapshots[4096]) - XCTAssertEqual(IteratorValueSnapshot(value: 4097, count: 0), snapshots[4097]) - XCTAssertEqual(IteratorValueSnapshot(value: 4098, count: 0), snapshots[4098]) - XCTAssertEqual(IteratorValueSnapshot(value: 4099, count: 0), snapshots[4099]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_096, count: 4), snapshots[4_096]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_097, count: 0), snapshots[4_097]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_098, count: 0), snapshots[4_098]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_099, count: 0), snapshots[4_099]) // also size 4, count: last bucket - XCTAssertEqual(IteratorValueSnapshot(value: 4100, count: 1), snapshots[4100]) - XCTAssertEqual(IteratorValueSnapshot(value: 4101, count: 0), snapshots[4101]) - XCTAssertEqual(IteratorValueSnapshot(value: 4102, count: 0), snapshots[4102]) - XCTAssertEqual(IteratorValueSnapshot(value: 4103, count: 0), snapshots[4103]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_100, count: 1), snapshots[4_100]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_101, count: 0), snapshots[4_101]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_102, count: 0), snapshots[4_102]) + XCTAssertEqual(IteratorValueSnapshot(value: 4_103, count: 0), snapshots[4_103]) - XCTAssertEqual(4104, snapshots.count) + XCTAssertEqual(4_104, snapshots.count) } } diff --git a/Tests/HistogramTests/HistogramTests.swift b/Tests/HistogramTests/HistogramTests.swift index 4eb176d..cd32462 100644 --- a/Tests/HistogramTests/HistogramTests.swift +++ b/Tests/HistogramTests/HistogramTests.swift @@ -8,7 +8,7 @@ // http://www.apache.org/licenses/LICENSE-2.0 // -// swiftlint:disable file_length identifier_name line_length number_separator +// swiftlint:disable file_length identifier_name line_length @testable import Histogram import Numerics @@ -28,7 +28,7 @@ final class HistogramTests: XCTestCase { func testUnitMagnitude0IndexCalculations() throws { let h = Histogram(lowestDiscernibleValue: 1, highestTrackableValue: UInt64(1) << 32, numberOfSignificantValueDigits: .three) - XCTAssertEqual(2048, h.subBucketCount) + XCTAssertEqual(2_048, h.subBucketCount) XCTAssertEqual(0, h.unitMagnitude) // subBucketCount = 2^11, so 2^11 << 22 is > the max of 2^32 for 23 buckets total XCTAssertEqual(23, h.bucketCount) @@ -38,28 +38,28 @@ final class HistogramTests: XCTestCase { XCTAssertEqual(3, h.subBucketIndexForValue(3, bucketIndex: 0)) // second half of first bucket - XCTAssertEqual(0, h.bucketIndexForValue(1024 + 3)) - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue(1024 + 3, bucketIndex: 0)) + XCTAssertEqual(0, h.bucketIndexForValue(1_024 + 3)) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue(1_024 + 3, bucketIndex: 0)) // second bucket (top half) - XCTAssertEqual(1, h.bucketIndexForValue(2048 + 3 * 2)) + XCTAssertEqual(1, h.bucketIndexForValue(2_048 + 3 * 2)) // counting by 2s, starting at halfway through the bucket - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue(2048 + 3 * 2, bucketIndex: 1)) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue(2_048 + 3 * 2, bucketIndex: 1)) // third bucket (top half) - XCTAssertEqual(2, h.bucketIndexForValue((2048 << 1) + 3 * 4)) + XCTAssertEqual(2, h.bucketIndexForValue((2_048 << 1) + 3 * 4)) // counting by 4s, starting at halfway through the bucket - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue((2048 << 1) + 3 * 4, bucketIndex: 2)) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue((2_048 << 1) + 3 * 4, bucketIndex: 2)) // past last bucket -- not near UInt64.max, so should still calculate ok. - XCTAssertEqual(23, h.bucketIndexForValue((UInt64(2048) << 22) + 3 * (1 << 23))) - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue((UInt64(2048) << 22) + 3 * (1 << 23), bucketIndex: 23)) + XCTAssertEqual(23, h.bucketIndexForValue((UInt64(2_048) << 22) + 3 * (1 << 23))) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue((UInt64(2_048) << 22) + 3 * (1 << 23), bucketIndex: 23)) } func testUnitMagnitude4IndexCalculations() throws { let h = Histogram(lowestDiscernibleValue: 1 << 12, highestTrackableValue: 1 << 32, numberOfSignificantValueDigits: .three) - XCTAssertEqual(2048, h.subBucketCount) + XCTAssertEqual(2_048, h.subBucketCount) XCTAssertEqual(12, h.unitMagnitude) // subBucketCount = 2^11. With unit magnitude shift, it's 2^23. 2^23 << 10 is > the max of 2^32 for 11 buckets total XCTAssertEqual(11, h.bucketCount) @@ -76,31 +76,31 @@ final class HistogramTests: XCTestCase { // second half of first bucket // subBucketHalfCount's worth of units, plus 3 more - XCTAssertEqual(0, h.bucketIndexForValue(unit * (1024 + 3))) - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue(unit * (1024 + 3), bucketIndex: 0)) + XCTAssertEqual(0, h.bucketIndexForValue(unit * (1_024 + 3))) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue(unit * (1_024 + 3), bucketIndex: 0)) // second bucket (top half), bucket scale = unit << 1. // Middle of bucket is (subBucketHalfCount = 2^10) of bucket scale, = unit << 11. // Add on 3 of bucket scale. XCTAssertEqual(1, h.bucketIndexForValue((unit << 11) + 3 * (unit << 1))) - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue((unit << 11) + 3 * (unit << 1), bucketIndex: 1)) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue((unit << 11) + 3 * (unit << 1), bucketIndex: 1)) // third bucket (top half), bucket scale = unit << 2. // Middle of bucket is (subBucketHalfCount = 2^10) of bucket scale, = unit << 12. // Add on 3 of bucket scale. XCTAssertEqual(2, h.bucketIndexForValue((unit << 12) + 3 * (unit << 2))) - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue((unit << 12) + 3 * (unit << 2), bucketIndex: 2)) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue((unit << 12) + 3 * (unit << 2), bucketIndex: 2)) // past last bucket -- not near UInt64.max, so should still calculate ok. XCTAssertEqual(11, h.bucketIndexForValue((unit << 21) + 3 * (unit << 11))) - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue((unit << 21) + 3 * (unit << 11), bucketIndex: 11)) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue((unit << 21) + 3 * (unit << 11), bucketIndex: 11)) } func testUnitMagnitude51SubBucketMagnitude11IndexCalculations() throws { // maximum unit magnitude for this precision let h = Histogram(lowestDiscernibleValue: UInt64(1) << 51, highestTrackableValue: UInt64(Int64.max), numberOfSignificantValueDigits: .three) - XCTAssertEqual(2048, h.subBucketCount) + XCTAssertEqual(2_048, h.subBucketCount) XCTAssertEqual(51, h.unitMagnitude) // subBucketCount = 2^11. With unit magnitude shift, it's 2^62. 1 more bucket to (almost) reach 2^63. XCTAssertEqual(2, h.bucketCount) @@ -118,22 +118,22 @@ final class HistogramTests: XCTestCase { // second half of first bucket // subBucketHalfCount's worth of units, plus 3 more - XCTAssertEqual(0, h.bucketIndexForValue(unit * (1024 + 3))) - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue(unit * (1024 + 3), bucketIndex: 0)) + XCTAssertEqual(0, h.bucketIndexForValue(unit * (1_024 + 3))) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue(unit * (1_024 + 3), bucketIndex: 0)) // end of second half - XCTAssertEqual(0, h.bucketIndexForValue(unit * 1024 + 1023 * unit)) - XCTAssertEqual(1024 + 1023, h.subBucketIndexForValue(unit * 1024 + 1023 * unit, bucketIndex: 0)) + XCTAssertEqual(0, h.bucketIndexForValue(unit * 1_024 + 1_023 * unit)) + XCTAssertEqual(1_024 + 1_023, h.subBucketIndexForValue(unit * 1_024 + 1_023 * unit, bucketIndex: 0)) // second bucket (top half), bucket scale = unit << 1. // Middle of bucket is (subBucketHalfCount = 2^10) of bucket scale, = unit << 11. // Add on 3 of bucket scale. XCTAssertEqual(1, h.bucketIndexForValue((unit << 11) + 3 * (unit << 1))) - XCTAssertEqual(1024 + 3, h.subBucketIndexForValue((unit << 11) + 3 * (unit << 1), bucketIndex: 1)) + XCTAssertEqual(1_024 + 3, h.subBucketIndexForValue((unit << 11) + 3 * (unit << 1), bucketIndex: 1)) // upper half of second bucket, last slot XCTAssertEqual(1, h.bucketIndexForValue(UInt64(Int64.max))) - XCTAssertEqual(1024 + 1023, h.subBucketIndexForValue(UInt64(Int64.max), bucketIndex: 1)) + XCTAssertEqual(1_024 + 1_023, h.subBucketIndexForValue(UInt64(Int64.max), bucketIndex: 1)) } func testUnitMagnitude54SubBucketMagnitude8Ok() throws { @@ -184,7 +184,7 @@ final class HistogramTests: XCTestCase { XCTAssertEqual(1, h.countForValue(Self.value)) XCTAssertEqual(1, h.totalCount) - self.verifyMaxValue(histogram: h) + verifyMaxValue(histogram: h) } func testConstructionWithLargeNumbers() throws { @@ -202,12 +202,12 @@ final class HistogramTests: XCTestCase { } func testValueAtPercentileMatchesPercentile() throws { - let lengths: [UInt64] = [ 1, 5, 10, 50, 100, 500, 1000, 5000, 10_000, 50_000, 100_000 ] + let lengths: [UInt64] = [1, 5, 10, 50, 100, 500, 1_000, 5_000, 10_000, 50_000, 100_000] for length in lengths { var h = Histogram(lowestDiscernibleValue: 1, highestTrackableValue: UInt64.max, numberOfSignificantValueDigits: .two) - for value in 1...length { + for value in 1 ... length { h.record(value) } @@ -223,16 +223,16 @@ final class HistogramTests: XCTestCase { } func testValueAtPercentileMatchesPercentileIter() throws { - let lengths: [UInt64] = [ 1, 5, 10, 50, 100, 500, 1000, 5000, 10_000, 50_000, 100_000 ] + let lengths: [UInt64] = [1, 5, 10, 50, 100, 500, 1_000, 5_000, 10_000, 50_000, 100_000] for length in lengths { var h = Histogram(lowestDiscernibleValue: 1, highestTrackableValue: UInt64.max, numberOfSignificantValueDigits: .two) - for value in 1...length { + for value in 1 ... length { h.record(value) } - for v in h.percentiles(ticksPerHalfDistance: 1000) { + for v in h.percentiles(ticksPerHalfDistance: 1_000) { let calculatedValue = h.valueAtPercentile(v.percentile) let iterValue = v.value @@ -245,11 +245,11 @@ final class HistogramTests: XCTestCase { func testIterationValueHashable() throws { var h = Histogram(lowestDiscernibleValue: 1, highestTrackableValue: UInt64.max, numberOfSignificantValueDigits: .two) - for value in 1...50 { + for value in 1 ... 50 { h.record(UInt64(value)) } - var cached:[Histogram.IterationValue:UInt64] = [:] + var cached: [Histogram.IterationValue: UInt64] = [:] for iv in h.linearBucketValues(valueUnitsPerBucket: 5) { cached[iv] = iv.count } @@ -280,7 +280,7 @@ final class HistogramTests: XCTestCase { XCTAssertEqual(1, rawHistogram.countForValue((value * 4) / 4)) XCTAssertEqual(1, rawHistogram.totalCount) - self.verifyMaxValue(histogram: histogram) + verifyMaxValue(histogram: histogram) } func testReset() { @@ -309,17 +309,17 @@ final class HistogramTests: XCTestCase { } func testScaledSizeOfEquivalentValueRange() throws { - let histogram = Histogram(lowestDiscernibleValue: 1024, + let histogram = Histogram(lowestDiscernibleValue: 1_024, highestTrackableValue: Self.highestTrackableValue, numberOfSignificantValueDigits: Self.numberOfSignificantValueDigits) - XCTAssertEqual(1 * 1024, histogram.sizeOfEquivalentRangeForValue(1 * 1024)) - XCTAssertEqual(2 * 1024, histogram.sizeOfEquivalentRangeForValue(2500 * 1024)) - XCTAssertEqual(4 * 1024, histogram.sizeOfEquivalentRangeForValue(8191 * 1024)) - XCTAssertEqual(8 * 1024, histogram.sizeOfEquivalentRangeForValue(8192 * 1024)) - XCTAssertEqual(8 * 1024, histogram.sizeOfEquivalentRangeForValue(10_000 * 1024)) + XCTAssertEqual(1 * 1_024, histogram.sizeOfEquivalentRangeForValue(1 * 1_024)) + XCTAssertEqual(2 * 1_024, histogram.sizeOfEquivalentRangeForValue(2_500 * 1_024)) + XCTAssertEqual(4 * 1_024, histogram.sizeOfEquivalentRangeForValue(8_191 * 1_024)) + XCTAssertEqual(8 * 1_024, histogram.sizeOfEquivalentRangeForValue(8_192 * 1_024)) + XCTAssertEqual(8 * 1_024, histogram.sizeOfEquivalentRangeForValue(10_000 * 1_024)) - self.verifyMaxValue(histogram: histogram) + verifyMaxValue(histogram: histogram) } func testLowestEquivalentValue() throws { @@ -328,42 +328,42 @@ final class HistogramTests: XCTestCase { XCTAssertEqual(10_000, histogram.lowestEquivalentForValue(10_007)) XCTAssertEqual(10_008, histogram.lowestEquivalentForValue(10_009)) - self.verifyMaxValue(histogram: histogram) + verifyMaxValue(histogram: histogram) } func testScaledLowestEquivalentValue() throws { - let histogram = Histogram(lowestDiscernibleValue: 1024, highestTrackableValue: Self.highestTrackableValue, numberOfSignificantValueDigits: Self.numberOfSignificantValueDigits) + let histogram = Histogram(lowestDiscernibleValue: 1_024, highestTrackableValue: Self.highestTrackableValue, numberOfSignificantValueDigits: Self.numberOfSignificantValueDigits) - XCTAssertEqual(10_000 * 1024, histogram.lowestEquivalentForValue(10_007 * 1024)) - XCTAssertEqual(10_008 * 1024, histogram.lowestEquivalentForValue(10_009 * 1024)) + XCTAssertEqual(10_000 * 1_024, histogram.lowestEquivalentForValue(10_007 * 1_024)) + XCTAssertEqual(10_008 * 1_024, histogram.lowestEquivalentForValue(10_009 * 1_024)) - self.verifyMaxValue(histogram: histogram) + verifyMaxValue(histogram: histogram) } func testHighestEquivalentValue() throws { let histogram = Histogram(highestTrackableValue: Self.highestTrackableValue, numberOfSignificantValueDigits: Self.numberOfSignificantValueDigits) - XCTAssertEqual(8183, histogram.highestEquivalentForValue(8180)) - XCTAssertEqual(8191, histogram.highestEquivalentForValue(8191)) - XCTAssertEqual(8199, histogram.highestEquivalentForValue(8193)) - XCTAssertEqual(9999, histogram.highestEquivalentForValue(9995)) + XCTAssertEqual(8_183, histogram.highestEquivalentForValue(8_180)) + XCTAssertEqual(8_191, histogram.highestEquivalentForValue(8_191)) + XCTAssertEqual(8_199, histogram.highestEquivalentForValue(8_193)) + XCTAssertEqual(9_999, histogram.highestEquivalentForValue(9_995)) XCTAssertEqual(10_007, histogram.highestEquivalentForValue(10_007)) XCTAssertEqual(10_015, histogram.highestEquivalentForValue(10_008)) - self.verifyMaxValue(histogram: histogram) + verifyMaxValue(histogram: histogram) } func testScaledHighestEquivalentValue() throws { - let histogram = Histogram(lowestDiscernibleValue: 1024, highestTrackableValue: Self.highestTrackableValue, numberOfSignificantValueDigits: Self.numberOfSignificantValueDigits) + let histogram = Histogram(lowestDiscernibleValue: 1_024, highestTrackableValue: Self.highestTrackableValue, numberOfSignificantValueDigits: Self.numberOfSignificantValueDigits) - XCTAssertEqual(8183 * 1024 + 1023, histogram.highestEquivalentForValue(8180 * 1024)) - XCTAssertEqual(8191 * 1024 + 1023, histogram.highestEquivalentForValue(8191 * 1024)) - XCTAssertEqual(8199 * 1024 + 1023, histogram.highestEquivalentForValue(8193 * 1024)) - XCTAssertEqual(9999 * 1024 + 1023, histogram.highestEquivalentForValue(9995 * 1024)) - XCTAssertEqual(10_007 * 1024 + 1023, histogram.highestEquivalentForValue(10_007 * 1024)) - XCTAssertEqual(10_015 * 1024 + 1023, histogram.highestEquivalentForValue(10_008 * 1024)) + XCTAssertEqual(8_183 * 1_024 + 1_023, histogram.highestEquivalentForValue(8_180 * 1_024)) + XCTAssertEqual(8_191 * 1_024 + 1_023, histogram.highestEquivalentForValue(8_191 * 1_024)) + XCTAssertEqual(8_199 * 1_024 + 1_023, histogram.highestEquivalentForValue(8_193 * 1_024)) + XCTAssertEqual(9_999 * 1_024 + 1_023, histogram.highestEquivalentForValue(9_995 * 1_024)) + XCTAssertEqual(10_007 * 1_024 + 1_023, histogram.highestEquivalentForValue(10_007 * 1_024)) + XCTAssertEqual(10_015 * 1_024 + 1_023, histogram.highestEquivalentForValue(10_008 * 1_024)) - self.verifyMaxValue(histogram: histogram) + verifyMaxValue(histogram: histogram) } func testEquivalentRangeForValue() { @@ -378,11 +378,11 @@ final class HistogramTests: XCTestCase { XCTAssertEqual(4, histogram.medianEquivalentForValue(4)) XCTAssertEqual(5, histogram.medianEquivalentForValue(5)) - XCTAssertEqual(4001, histogram.medianEquivalentForValue(4000)) - XCTAssertEqual(8002, histogram.medianEquivalentForValue(8000)) + XCTAssertEqual(4_001, histogram.medianEquivalentForValue(4_000)) + XCTAssertEqual(8_002, histogram.medianEquivalentForValue(8_000)) XCTAssertEqual(10_004, histogram.medianEquivalentForValue(10_007)) - self.verifyMaxValue(histogram: histogram) + verifyMaxValue(histogram: histogram) } func testEstimatedFootprintInBytes() { @@ -408,7 +408,7 @@ final class HistogramTests: XCTestCase { XCTAssertEqual(bucketsCount, histogram.counts.count) let expectedSize = 512 + // histogram object size - histogram.counts.capacity * MemoryLayout.stride + histogram.counts.capacity * MemoryLayout.stride XCTAssertEqual(expectedSize, histogram.estimatedFootprintInBytes) @@ -418,7 +418,7 @@ final class HistogramTests: XCTestCase { func testOutputPercentileDistributionPlainText() { var histogram = Histogram(highestTrackableValue: 10_000, numberOfSignificantValueDigits: .three) - for i in 1...10 { + for i in 1 ... 10 { histogram.record(UInt64(i)) } @@ -426,33 +426,33 @@ final class HistogramTests: XCTestCase { histogram.outputPercentileDistribution(to: &output, outputValueUnitScalingRatio: 1.0, percentileTicksPerHalfDistance: 5, format: .plainText) let expectedOutput = """ - Value Percentile TotalCount 1/(1-Percentile) - - 1.000 0.000000000000 1 1.00 - 1.000 0.100000000000 1 1.11 - 2.000 0.200000000000 2 1.25 - 3.000 0.300000000000 3 1.43 - 4.000 0.400000000000 4 1.67 - 5.000 0.500000000000 5 2.00 - 6.000 0.550000000000 6 2.22 - 6.000 0.600000000000 6 2.50 - 7.000 0.650000000000 7 2.86 - 7.000 0.700000000000 7 3.33 - 8.000 0.750000000000 8 4.00 - 8.000 0.775000000000 8 4.44 - 8.000 0.800000000000 8 5.00 - 9.000 0.825000000000 9 5.71 - 9.000 0.850000000000 9 6.67 - 9.000 0.875000000000 9 8.00 - 9.000 0.887500000000 9 8.89 - 9.000 0.900000000000 9 10.00 - 10.000 0.912500000000 10 11.43 - 10.000 1.000000000000 10 -#[Mean = 5.500, StdDeviation = 2.872] -#[Max = 10.000, Total count = 10] -#[Buckets = 4, SubBuckets = 2048] - -""" + Value Percentile TotalCount 1/(1-Percentile) + + 1.000 0.000000000000 1 1.00 + 1.000 0.100000000000 1 1.11 + 2.000 0.200000000000 2 1.25 + 3.000 0.300000000000 3 1.43 + 4.000 0.400000000000 4 1.67 + 5.000 0.500000000000 5 2.00 + 6.000 0.550000000000 6 2.22 + 6.000 0.600000000000 6 2.50 + 7.000 0.650000000000 7 2.86 + 7.000 0.700000000000 7 3.33 + 8.000 0.750000000000 8 4.00 + 8.000 0.775000000000 8 4.44 + 8.000 0.800000000000 8 5.00 + 9.000 0.825000000000 9 5.71 + 9.000 0.850000000000 9 6.67 + 9.000 0.875000000000 9 8.00 + 9.000 0.887500000000 9 8.89 + 9.000 0.900000000000 9 10.00 + 10.000 0.912500000000 10 11.43 + 10.000 1.000000000000 10 + #[Mean = 5.500, StdDeviation = 2.872] + #[Max = 10.000, Total count = 10] + #[Buckets = 4, SubBuckets = 2048] + + """ XCTAssertEqual(output, expectedOutput) } @@ -460,7 +460,7 @@ final class HistogramTests: XCTestCase { func testOutputPercentileDistributionCsv() { var histogram = Histogram(highestTrackableValue: 10_000, numberOfSignificantValueDigits: .three) - for i in 1...10 { + for i in 1 ... 10 { histogram.record(UInt64(i)) } @@ -468,36 +468,36 @@ final class HistogramTests: XCTestCase { histogram.outputPercentileDistribution(to: &output, outputValueUnitScalingRatio: 1.0, percentileTicksPerHalfDistance: 5, format: .csv) let expectedOutput = """ -"Value","Percentile","TotalCount","1/(1-Percentile)" -1.000,0.000000000000,1,1.00 -1.000,0.100000000000,1,1.11 -2.000,0.200000000000,2,1.25 -3.000,0.300000000000,3,1.43 -4.000,0.400000000000,4,1.67 -5.000,0.500000000000,5,2.00 -6.000,0.550000000000,6,2.22 -6.000,0.600000000000,6,2.50 -7.000,0.650000000000,7,2.86 -7.000,0.700000000000,7,3.33 -8.000,0.750000000000,8,4.00 -8.000,0.775000000000,8,4.44 -8.000,0.800000000000,8,5.00 -9.000,0.825000000000,9,5.71 -9.000,0.850000000000,9,6.67 -9.000,0.875000000000,9,8.00 -9.000,0.887500000000,9,8.89 -9.000,0.900000000000,9,10.00 -10.000,0.912500000000,10,11.43 -10.000,1.000000000000,10,Infinity - -""" + "Value","Percentile","TotalCount","1/(1-Percentile)" + 1.000,0.000000000000,1,1.00 + 1.000,0.100000000000,1,1.11 + 2.000,0.200000000000,2,1.25 + 3.000,0.300000000000,3,1.43 + 4.000,0.400000000000,4,1.67 + 5.000,0.500000000000,5,2.00 + 6.000,0.550000000000,6,2.22 + 6.000,0.600000000000,6,2.50 + 7.000,0.650000000000,7,2.86 + 7.000,0.700000000000,7,3.33 + 8.000,0.750000000000,8,4.00 + 8.000,0.775000000000,8,4.44 + 8.000,0.800000000000,8,5.00 + 9.000,0.825000000000,9,5.71 + 9.000,0.850000000000,9,6.67 + 9.000,0.875000000000,9,8.00 + 9.000,0.887500000000,9,8.89 + 9.000,0.900000000000,9,10.00 + 10.000,0.912500000000,10,11.43 + 10.000,1.000000000000,10,Infinity + + """ XCTAssertEqual(output, expectedOutput) } private func verifyMaxValue(histogram h: Histogram) { var computedMaxValue: UInt64 = 0 - for i in 0.. 0 { + for i in 0 ..< h.counts.count where h.counts[i] > 0 { computedMaxValue = h.valueFromIndex(i) } computedMaxValue = (computedMaxValue == 0) ? 0 : h.highestEquivalentForValue(computedMaxValue)