/*
* Licensed to the Ted Dunning under one or more contributor license
* agreements. See the NOTICE file that may be
* distributed with this work for additional information
* regarding copyright ownership. Ted Dunning licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.mapr.stats;
import org.apache.mahout.common.RandomUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Random;
public class UpperQuantileTest {
Logger log = LoggerFactory.getLogger(this.getClass());
@Rule
public ExpectedException exception = ExpectedException.none();
private double[] data;
private UpperQuantile uq;
@Test()
public void testEmptyData() {
exception.expect(IllegalStateException.class);
exception.expectMessage("no data");
new UpperQuantile(1000).quantile(0.5);
}
@Test()
public void testGoofyQuantileNegative() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage(">= 0");
uq.quantile(-1);
}
@Test()
public void testGoofyQuantileGreaterThan1() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("<= 1");
uq.quantile(1.2);
}
@Test
public void testGoofyQuantileTooLow() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("only retained");
uq.quantile(0.5);
}
@Test
public void testQuantiles() {
Assert.assertEquals(data[900], uq.quantile(0.9), 0);
Assert.assertEquals(data[1000], uq.quantile(1), 0);
Assert.assertEquals(data[950], uq.quantile(0.95), 0);
Assert.assertEquals(data[950] * 0.8 + data[951] * 0.2, uq.quantile(0.9502), 0);
}
@Test
public void testSpeed() {
long total = 0;
UpperQuantile data = new UpperQuantile(5000);
Random gen = RandomUtils.getRandom();
for (int i = 0; i < 10000; i++) {
data.add(gen.nextDouble());
}
data.clear();
int n = 100000;
for (int i = 0; i < n; i++) {
double x = gen.nextDouble();
long t0 = System.nanoTime();
data.add(x);
long t1 = System.nanoTime();
total += t1 - t0;
}
// time per insert should be less than a micro-second. Typically this actually comes out ~300 ns
log.debug("t = {} us", total / 1e9 / n / 1e-6);
Assert.assertTrue(total / 1e9 / n < 100e-6);
total = 0;
for (int i = 0; i < 10; i++) {
double q = gen.nextDouble() * 0.01 + 0.99;
long t0 = System.nanoTime();
double r = data.quantile(q);
long t1 = System.nanoTime();
Assert.assertEquals(String.format("q=%.3f r=%.3f i=%d", q, r, i), q, r, 0.01);
total += t1 - t0;
}
log.debug("t = {} us", total / 1e9 / 10 / 1e-6);
}
@Before
public void generate() {
RandomUtils.useTestSeed();
uq = new UpperQuantile(101);
data = new double[1001];
Random gen = RandomUtils.getRandom();
for (int i = 0; i < 1001; i++) {
double x = gen.nextDouble();
data[i] = x;
uq.add(x);
}
Arrays.sort(data);
}
}