/// <summary>Try to remove extraneous items from the set of sampled items.</summary> /// <remarks> /// Try to remove extraneous items from the set of sampled items. This checks /// if an item is unnecessary based on the desired error bounds, and merges it /// with the adjacent item if it is. /// </remarks> private void Compress() { if (samples.Count < 2) { return; } ListIterator <SampleQuantiles.SampleItem> it = samples.ListIterator(); SampleQuantiles.SampleItem prev = null; SampleQuantiles.SampleItem next = it.Next(); while (it.HasNext()) { prev = next; next = it.Next(); if (prev.g + next.g + next.delta <= AllowableError(it.PreviousIndex())) { next.g += prev.g; // Remove prev. it.remove() kills the last thing returned. it.Previous(); it.Previous(); it.Remove(); // it.next() is now equal to next, skip it back forward again it.Next(); } } }
/// <summary>Merges items from buffer into the samples array in one pass.</summary> /// <remarks> /// Merges items from buffer into the samples array in one pass. /// This is more efficient than doing an insert on every item. /// </remarks> private void InsertBatch() { if (bufferCount == 0) { return; } Arrays.Sort(buffer, 0, bufferCount); // Base case: no samples int start = 0; if (samples.Count == 0) { SampleQuantiles.SampleItem newItem = new SampleQuantiles.SampleItem(buffer[0], 1, 0); samples.AddItem(newItem); start++; } ListIterator <SampleQuantiles.SampleItem> it = samples.ListIterator(); SampleQuantiles.SampleItem item = it.Next(); for (int i = start; i < bufferCount; i++) { long v = buffer[i]; while (it.NextIndex() < samples.Count && item.value < v) { item = it.Next(); } // If we found that bigger item, back up so we insert ourselves before it if (item.value > v) { it.Previous(); } // We use different indexes for the edge comparisons, because of the above // if statement that adjusts the iterator int delta; if (it.PreviousIndex() == 0 || it.NextIndex() == samples.Count) { delta = 0; } else { delta = ((int)Math.Floor(AllowableError(it.NextIndex()))) - 1; } SampleQuantiles.SampleItem newItem = new SampleQuantiles.SampleItem(v, 1, delta); it.Add(newItem); item = newItem; } bufferCount = 0; }
/// <summary>Get the estimated value at the specified quantile.</summary> /// <param name="quantile">Queried quantile, e.g. 0.50 or 0.99.</param> /// <returns>Estimated value at that quantile.</returns> private long Query(double quantile) { Preconditions.CheckState(!samples.IsEmpty(), "no data in estimator"); int rankMin = 0; int desired = (int)(quantile * count); ListIterator <SampleQuantiles.SampleItem> it = samples.ListIterator(); SampleQuantiles.SampleItem prev = null; SampleQuantiles.SampleItem cur = it.Next(); for (int i = 1; i < samples.Count; i++) { prev = cur; cur = it.Next(); rankMin += prev.g; if (rankMin + cur.g + cur.delta > desired + (AllowableError(i) / 2)) { return(prev.value); } } // edge case of wanting max value return(samples[samples.Count - 1].value); }