summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2019-12-10 18:55:51 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2019-12-10 18:55:51 +0000
commitd013b7e32f484a054607193705a21890082bd79e (patch)
tree7825936591dedce43e91e6a895e37799e446f9a0
parentb55d1a801204295e9b979a2a358bf306190bf4e6 (diff)
parent1ccc4e1729bd04c7537093bc002e0135bdd7fb3a (diff)
Merge "Add quantileFunction method to IntHistogram"
-rw-r--r--service/java/com/android/server/wifi/util/IntHistogram.java41
-rw-r--r--tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java41
2 files changed, 82 insertions, 0 deletions
diff --git a/service/java/com/android/server/wifi/util/IntHistogram.java b/service/java/com/android/server/wifi/util/IntHistogram.java
index 83a491355..8e8700a8c 100644
--- a/service/java/com/android/server/wifi/util/IntHistogram.java
+++ b/service/java/com/android/server/wifi/util/IntHistogram.java
@@ -142,6 +142,47 @@ public class IntHistogram implements Iterable<IntHistogram.Bucket> {
mBuckets.put(bucketKey, curBucketValue + count);
}
+
+ /**
+ * Computes the inverse of the cumulative probability distribution for the histogram.
+ *
+ * This is the value v such that the probability of a randomly selected datum being
+ * less than or equal to v is the provided probability. The answer is constrained to
+ * lie in the interval [minimum..maximum].
+ */
+ public double quantileFunction(double probability, int minimum, int maximum) {
+ if (minimum > maximum) {
+ throw new IllegalArgumentException("bad bounds");
+ }
+ if (probability < 0.0 || probability > 1.0) {
+ throw new IllegalArgumentException("bad roll, try again");
+ }
+ long sum = 0;
+ for (Bucket bucket : this) {
+ sum += bucket.count;
+ }
+ final double target = sum * probability;
+ double partialSum = 0.0;
+ Bucket hitBucket = null;
+ for (Bucket bucket : this) {
+ if (partialSum + bucket.count >= target) {
+ hitBucket = bucket;
+ break;
+ }
+ partialSum += bucket.count;
+ }
+ if (hitBucket == null) {
+ // No data at all; assume uniform between given limits
+ return minimum + probability * (maximum - minimum);
+ }
+ double highValue = Math.min(hitBucket.end, maximum);
+ double value = Math.max(hitBucket.start, minimum);
+ if (value >= highValue - 1.0 || hitBucket.count == 0) return Math.min(value, highValue);
+ // interpolate to estimate the value
+ value += (highValue - value) * (target - partialSum) / hitBucket.count;
+ return Math.min(Math.max(value, minimum), maximum);
+ }
+
/**
* Given a value, returns the key of the bucket where it should fall into.
*/
diff --git a/tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java b/tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java
index 9e291af16..193f011a1 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/IntHistogramTest.java
@@ -20,6 +20,7 @@ import static com.android.server.wifi.WifiMetricsTestUtil.assertHistogramBuckets
import static com.android.server.wifi.WifiMetricsTestUtil.buildHistogramBucketInt32;
import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertEquals;
import androidx.test.filters.SmallTest;
@@ -263,4 +264,44 @@ public class IntHistogramTest extends WifiBaseTest {
public void testNonMonotonicBucketBoundaries() {
new IntHistogram(new int[] {1, 2, 3, 3});
}
+
+ @Test
+ public void testEmptyQuantile() {
+ mHistogram = new IntHistogram(TEST_BUCKET_BOUNDARIES);
+ double q = mHistogram.quantileFunction(0.2, 0, 50);
+ assertEquals(q, 10.0, 0.01);
+ }
+
+ @Test
+ public void testQuantileUniformDistribution() {
+ mHistogram = new IntHistogram(TEST_BUCKET_BOUNDARIES);
+ for (int i = 0; i < 111; i++) {
+ mHistogram.increment(i);
+ }
+ String diagnose = mHistogram.toString();
+ double q;
+
+ for (double i = 0.0; i <= 111.0; i++) {
+ q = mHistogram.quantileFunction(i / 111.0, 0, 111);
+ assertEquals(diagnose, i, q, 0.01);
+ }
+
+ q = mHistogram.quantileFunction(.8, 0, 42);
+ assertEquals(42.0, q, 0.01);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void backwardsBoundsShouldFail() {
+ new IntHistogram(TEST_BUCKET_BOUNDARIES).quantileFunction(0.5, 80, 40);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void negativeProbablilityShouldFail() {
+ new IntHistogram(TEST_BUCKET_BOUNDARIES).quantileFunction(-0.1, 0, 50);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void largerThanUnityProbablilityShouldFail() {
+ new IntHistogram(TEST_BUCKET_BOUNDARIES).quantileFunction(1.1, 0, 50);
+ }
}