/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF 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 org.streaminer.stream.quantile; import com.google.common.collect.Lists; import org.apache.mahout.common.RandomUtils; import org.junit.Before; import org.junit.Test; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Random; import static org.junit.Assert.*; public class GroupTreeTest { @Test public void testSimpleAdds() { GroupTree x = new GroupTree(); assertNull(x.floor(new TDigest.Group(34))); assertNull(x.ceiling(new TDigest.Group(34))); assertEquals(0, x.size()); assertEquals(0, x.sum()); x.add(new TDigest.Group(1)); TDigest.Group group = new TDigest.Group(2); group.add(3, 1); group.add(4, 1); x.add(group); assertEquals(2, x.size()); assertEquals(4, x.sum()); } @Test public void testBalancing() { GroupTree x = new GroupTree(); for (int i = 0; i < 101; i++) { x.add(new TDigest.Group(i)); } assertEquals(101, x.sum()); assertEquals(101, x.size()); x.checkBalance(); } @Test public void testIterators() { GroupTree x = new GroupTree(); for (int i = 0; i < 101; i++) { x.add(new TDigest.Group(i / 2)); } assertEquals(0, x.first().mean(), 0); assertEquals(50, x.last().mean(), 0); Iterator<TDigest.Group> ix = x.iterator(); for (int i = 0; i < 101; i++) { assertTrue(ix.hasNext()); TDigest.Group z = ix.next(); assertEquals(i / 2, z.mean(), 0); } assertFalse(ix.hasNext()); // 34 is special since it is the smallest element of the right hand sub-tree Iterable<TDigest.Group> z = x.tailSet(new TDigest.Group(34, 0)); ix = z.iterator(); for (int i = 68; i < 101; i++) { assertTrue(ix.hasNext()); TDigest.Group v = ix.next(); assertEquals(i / 2, v.mean(), 0); } assertFalse(ix.hasNext()); ix = z.iterator(); for (int i = 68; i < 101; i++) { TDigest.Group v = ix.next(); assertEquals(i / 2, v.mean(), 0); } z = x.tailSet(new TDigest.Group(33, 0)); ix = z.iterator(); for (int i = 66; i < 101; i++) { assertTrue(ix.hasNext()); TDigest.Group v = ix.next(); assertEquals(i / 2, v.mean(), 0); } assertFalse(ix.hasNext()); z = x.tailSet(x.ceiling(new TDigest.Group(34, 0))); ix = z.iterator(); for (int i = 68; i < 101; i++) { assertTrue(ix.hasNext()); TDigest.Group v = ix.next(); assertEquals(i / 2, v.mean(), 0); } assertFalse(ix.hasNext()); z = x.tailSet(x.floor(new TDigest.Group(34, 0))); ix = z.iterator(); for (int i = 67; i < 101; i++) { assertTrue(ix.hasNext()); TDigest.Group v = ix.next(); assertEquals(i / 2, v.mean(), 0); } assertFalse(ix.hasNext()); } @Test public void testFloor() { // mostly tested in other tests GroupTree x = new GroupTree(); for (int i = 0; i < 101; i++) { x.add(new TDigest.Group(i / 2)); } assertNull(x.floor(new TDigest.Group(-30))); } @Test public void testRemoveAndSums() { GroupTree x = new GroupTree(); for (int i = 0; i < 101; i++) { x.add(new TDigest.Group(i / 2)); } TDigest.Group g = x.ceiling(new TDigest.Group(2, 0)); x.remove(g); g.add(3, 1); x.add(g); assertEquals(0, x.headCount(new TDigest.Group(-1))); assertEquals(0, x.headSum(new TDigest.Group(-1))); assertEquals(0, x.headCount(new TDigest.Group(0, 0))); assertEquals(0, x.headSum(new TDigest.Group(0, 0))); assertEquals(0, x.headCount(x.ceiling(new TDigest.Group(0, 0)))); assertEquals(0, x.headSum(x.ceiling(new TDigest.Group(0, 0)))); assertEquals(2, x.headCount(new TDigest.Group(1, 0))); assertEquals(2, x.headSum(new TDigest.Group(1, 0))); g = x.tailSet(new TDigest.Group(2.1)).iterator().next(); assertEquals(2.5, g.mean(), 1e-9); int i = 0; for (TDigest.Group gx : x) { if (i > 10) { break; } System.out.printf("%d:%.1f(%d)\t", i++, gx.mean(), gx.count()); } assertEquals(5, x.headCount(new TDigest.Group(2.1, 0))); assertEquals(5, x.headSum(new TDigest.Group(2.1, 0))); assertEquals(6, x.headCount(new TDigest.Group(2.7, 0))); assertEquals(7, x.headSum(new TDigest.Group(2.7, 0))); assertEquals(101, x.headCount(new TDigest.Group(200))); assertEquals(102, x.headSum(new TDigest.Group(200))); } @Before public void setUp() { RandomUtils.useTestSeed(); } @Test public void testRandomRebalance() { Random gen = RandomUtils.getRandom(); GroupTree x = new GroupTree(); List<Double> y = Lists.newArrayList(); for (int i = 0; i < 1000; i++) { double v = gen.nextDouble(); x.add(new TDigest.Group(v)); y.add(v); x.checkBalance(); } Collections.sort(y); Iterator<Double> i = y.iterator(); for (TDigest.Group group : x) { assertEquals(i.next(), group.mean(), 0.0); } for (int j = 0; j < 100; j++) { double v = y.get(gen.nextInt(y.size())); y.remove(v); x.remove(x.floor(new TDigest.Group(v))); } Collections.sort(y); i = y.iterator(); for (TDigest.Group group : x) { assertEquals(i.next(), group.mean(), 0.0); } for (int j = 0; j < y.size(); j++) { double v = y.get(j); y.set(j, v + 10); TDigest.Group g = x.floor(new TDigest.Group(v)); x.remove(g); x.checkBalance(); g.add(g.mean() + 20, 1); x.add(g); x.checkBalance(); } i = y.iterator(); for (TDigest.Group group : x) { assertEquals(i.next(), group.mean(), 0.0); } } }