Skip to content

Commit 7573f30

Browse files
feat(#82): Subtract - additional method rather than issue (#134)
* plan(#82): initial brief from issue * plan(#82): review brief * plan(#82): apply brief review feedback * plan(#82): review brief * plan(#82): create task breakdown * feat(#82): implement tasks * feat(#82): complete implementation
1 parent 837f6b4 commit 7573f30

3 files changed

Lines changed: 83 additions & 2 deletions

File tree

HdrHistogram.UnitTests/HistogramTestBase.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,51 @@ public void Add_throws_if_other_has_a_larger_range()
259259
Assert.Throws<ArgumentOutOfRangeException>(() => { histogram.Add(biggerOther); });
260260
}
261261

262+
[Fact]
263+
public void Subtract_should_reduce_the_counts_from_two_histograms()
264+
{
265+
var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures);
266+
var other = Create(DefaultHighestTrackableValue, DefaultSignificantFigures);
267+
histogram.RecordValue(TestValueLevel);
268+
histogram.RecordValue(TestValueLevel);
269+
histogram.RecordValue(TestValueLevel * 1000);
270+
histogram.RecordValue(TestValueLevel * 1000);
271+
other.RecordValue(TestValueLevel);
272+
other.RecordValue(TestValueLevel * 1000);
273+
274+
histogram.Subtract(other);
275+
276+
Assert.Equal(1L, histogram.GetCountAtValue(TestValueLevel));
277+
Assert.Equal(1L, histogram.GetCountAtValue(TestValueLevel * 1000));
278+
Assert.Equal(2L, histogram.TotalCount);
279+
}
280+
281+
[Fact]
282+
public void Subtract_should_allow_small_range_histograms_to_be_subtracted()
283+
{
284+
var biggerOther = Create(DefaultHighestTrackableValue * 2, DefaultSignificantFigures);
285+
var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures);
286+
biggerOther.RecordValue(TestValueLevel);
287+
biggerOther.RecordValue(TestValueLevel * 1000);
288+
histogram.RecordValue(TestValueLevel);
289+
histogram.RecordValue(TestValueLevel * 1000);
290+
291+
biggerOther.Subtract(histogram);
292+
293+
Assert.Equal(0L, biggerOther.GetCountAtValue(TestValueLevel));
294+
Assert.Equal(0L, biggerOther.GetCountAtValue(TestValueLevel * 1000));
295+
Assert.Equal(0L, biggerOther.TotalCount);
296+
}
297+
298+
[Fact]
299+
public void Subtract_throws_if_other_has_a_larger_range()
300+
{
301+
var histogram = Create(DefaultHighestTrackableValue, DefaultSignificantFigures);
302+
var biggerOther = Create(DefaultHighestTrackableValue * 2, DefaultSignificantFigures);
303+
304+
Assert.Throws<ArgumentOutOfRangeException>(() => { histogram.Subtract(biggerOther); });
305+
}
306+
262307
[Theory]
263308
[InlineData(1, 1)]
264309
[InlineData(2, 2500)]

HdrHistogram/HistogramBase.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ public virtual void Add(HistogramBase fromHistogram)
280280
{
281281
if (HighestTrackableValue < fromHistogram.HighestTrackableValue)
282282
{
283-
throw new ArgumentOutOfRangeException(nameof(fromHistogram), $"The other histogram covers a wider range ({fromHistogram.HighestTrackableValue} than this one ({HighestTrackableValue}).");
283+
throw new ArgumentOutOfRangeException(nameof(fromHistogram), $"The other histogram covers a wider range ({fromHistogram.HighestTrackableValue}) than this one ({HighestTrackableValue}).");
284284
}
285285
if ((BucketCount == fromHistogram.BucketCount) &&
286286
(SubBucketCount == fromHistogram.SubBucketCount) &&
@@ -305,7 +305,42 @@ public virtual void Add(HistogramBase fromHistogram)
305305
}
306306

307307
/// <summary>
308-
/// Get the size (in value units) of the range of values that are equivalent to the given value within the histogram's resolution.
308+
/// Subtract the contents of another histogram from this one.
309+
/// </summary>
310+
/// <param name="fromHistogram">The other histogram.</param>
311+
/// <exception cref="System.ArgumentOutOfRangeException">if fromHistogram covers a wider range than this histogram.</exception>
312+
public virtual void Subtract(HistogramBase fromHistogram)
313+
{
314+
if (HighestTrackableValue < fromHistogram.HighestTrackableValue)
315+
{
316+
throw new ArgumentOutOfRangeException(
317+
nameof(fromHistogram),
318+
$"The other histogram covers a wider range ({fromHistogram.HighestTrackableValue}) than this one ({HighestTrackableValue}).");
319+
}
320+
if ((BucketCount == fromHistogram.BucketCount) &&
321+
(SubBucketCount == fromHistogram.SubBucketCount) &&
322+
(_unitMagnitude == fromHistogram._unitMagnitude))
323+
{
324+
// Counts arrays are of the same length and meaning, so we can just iterate and subtract directly:
325+
for (var i = 0; i < fromHistogram.CountsArrayLength; i++)
326+
{
327+
AddToCountAtIndex(i, -fromHistogram.GetCountAtIndex(i));
328+
}
329+
}
330+
else
331+
{
332+
// Arrays are not a direct match, so we can't just stream through and subtract them.
333+
// Instead, go through the array and subtract each non-zero value found at its proper value:
334+
for (var i = 0; i < fromHistogram.CountsArrayLength; i++)
335+
{
336+
var count = fromHistogram.GetCountAtIndex(i);
337+
RecordValueWithCount(fromHistogram.ValueFromIndex(i), -count);
338+
}
339+
}
340+
}
341+
342+
/// <summary>
343+
/// Get the size (in value units) of the range of values that are equivalent to the given value within the histogram's resolution.
309344
/// Where "equivalent" means that value samples recorded for any two equivalent values are counted in a common total count.
310345
/// </summary>
311346
/// <param name="value">The given value</param>

spec/tech-standards/api-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ long GetCountBetweenValues(long lowValue, long highValue)
138138

139139
```csharp
140140
void Add(HistogramBase other) // Merge histograms
141+
void Subtract(HistogramBase other) // Remove histogram values
141142
HistogramBase Copy() // Create a copy
142143
void Reset() // Clear all counts
143144
bool HasOverflowed() // Check for overflow

0 commit comments

Comments
 (0)