com.google.uzaygezen.core.BoundedRollupTest.java Source code

Java tutorial

Introduction

Here is the source code for com.google.uzaygezen.core.BoundedRollupTest.java

Source

/*
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed 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.google.uzaygezen.core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.junit.Assert;
import org.junit.Test;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;
import com.google.uzaygezen.core.TestUtils.IntArrayCallback;
import com.google.uzaygezen.core.TestUtils.IntArrayComparator;

/**
 * @author Daniel Aioanei
 */
public class BoundedRollupTest {

    @Test
    public void empty() {
        BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                Integer.MAX_VALUE);
        Assert.assertNull(rollup.finish());
    }

    @Test
    public void rootOnly() {
        BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                Integer.MAX_VALUE);
        rollup.feedRow(Iterators.<Integer>emptyIterator(), TestUtils.ONE_LONG_CONTENT);
        MapNode<Integer, LongContent> actual = rollup.finish();
        MapNode<Integer, LongContent> expected = leaf();
        Assert.assertEquals(expected, actual);
    }

    @Test
    public void rootOnlyAddition() {
        BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                Integer.MAX_VALUE);
        int n = 10;
        for (int i = 0; i < n; ++i) {
            rollup.feedRow(Iterators.<Integer>emptyIterator(), TestUtils.ONE_LONG_CONTENT);
        }
        MapNode<Integer, LongContent> actual = rollup.finish();
        MapNode<Integer, LongContent> expected = leaf(n);
        Assert.assertEquals(expected, actual);
    }

    private MapNode<Integer, LongContent> leaf() {
        MapNode<Integer, LongContent> expected = MapNode.create(TestUtils.ONE_LONG_CONTENT,
                ImmutableMap.<Integer, MapNode<Integer, LongContent>>of());
        return expected;
    }

    private MapNode<Integer, LongContent> leaf(int count) {
        MapNode<Integer, LongContent> expected = MapNode.create(newCountingArray(count),
                ImmutableMap.<Integer, MapNode<Integer, LongContent>>of());
        return expected;
    }

    @Test
    public void oneRealNode() {
        BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                Integer.MAX_VALUE);
        rollup.feedRow(ImmutableList.of(4).iterator(), TestUtils.ONE_LONG_CONTENT);
        MapNode<Integer, LongContent> actual = rollup.finish();
        MapNode<Integer, LongContent> expected = MapNode.create(TestUtils.ONE_LONG_CONTENT,
                ImmutableMap.<Integer, MapNode<Integer, LongContent>>of(4, leaf()));
        Assert.assertEquals(expected, actual);
    }

    @Test
    public void twinPairAtLevel1() {
        BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                Integer.MAX_VALUE);
        rollup.feedRow(ImmutableList.of(5).iterator(), TestUtils.ONE_LONG_CONTENT);
        rollup.feedRow(ImmutableList.of(10).iterator(), TestUtils.ONE_LONG_CONTENT);
        MapNode<Integer, LongContent> actual = rollup.finish();
        MapNode<Integer, LongContent> expected = MapNode.create(newCountingArray(2),
                ImmutableMap.<Integer, MapNode<Integer, LongContent>>of(10, leaf(), 5, leaf()));
        Assert.assertEquals(expected, actual);
    }

    @Test
    public void twinPairAtLevel1RepeatLastNode() {
        BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                Integer.MAX_VALUE);
        rollup.feedRow(ImmutableList.of(5).iterator(), TestUtils.ONE_LONG_CONTENT);
        rollup.feedRow(ImmutableList.of(10).iterator(), TestUtils.ONE_LONG_CONTENT);
        rollup.feedRow(ImmutableList.of(10).iterator(), TestUtils.ONE_LONG_CONTENT);
        MapNode<Integer, LongContent> actual = rollup.finish();
        MapNode<Integer, LongContent> expected = MapNode.create(newCountingArray(3),
                ImmutableMap.<Integer, MapNode<Integer, LongContent>>of(10, leaf(2), 5, leaf()));
        Assert.assertEquals(expected, actual);
    }

    @Test
    public void twinPairAtLevel1ButGoingBackToFirstChildFails() {
        BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                Integer.MAX_VALUE);
        rollup.feedRow(ImmutableList.of(5).iterator(), TestUtils.ONE_LONG_CONTENT);
        rollup.feedRow(ImmutableList.of(10).iterator(), TestUtils.ONE_LONG_CONTENT);
        try {
            rollup.feedRow(ImmutableList.of(5).iterator(), TestUtils.ONE_LONG_CONTENT);
            Assert.fail("IllegalArgumentException expected");
        } catch (IllegalArgumentException ex) {
            MoreAsserts.assertContainsRegex("Node already there.", ex.getMessage());
        }
    }

    @Test
    public void twinPairAtLevel2() {
        BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                Integer.MAX_VALUE);
        rollup.feedRow(ImmutableList.of(100, 5).iterator(), TestUtils.ONE_LONG_CONTENT);
        rollup.feedRow(ImmutableList.of(100, 10).iterator(), TestUtils.ONE_LONG_CONTENT);
        MapNode<Integer, LongContent> actual = rollup.finish();
        MapNode<Integer, LongContent> expected = MapNode.create(newCountingArray(2),
                ImmutableMap.<Integer, MapNode<Integer, LongContent>>of(100, MapNode.create(newCountingArray(2),
                        ImmutableMap.<Integer, MapNode<Integer, LongContent>>of(10, leaf(), 5, leaf()))));
        Assert.assertEquals(expected, actual);
    }

    @Test
    public void nodeAndLeafCount() {
        final List<int[]> list = Lists.newArrayList();
        IntArrayCallback callback = new ListCollector(list);
        for (int n = 0; n < 8; ++n) {
            list.clear();
            // Exact sum means that no array will be a prefix of another one.
            TestUtils.generateSpecWithExactSum(n, 2 * n, callback);
            Collections.sort(list, IntArrayComparator.INSTANCE);
            BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                    Integer.MAX_VALUE);
            MapNode<Integer, LongContent> actual = createTree(list, rollup);
            checkTreeIsComplete(list, n, actual);
        }
    }

    private void checkTreeIsComplete(final List<int[]> list, int n, MapNode<Integer, LongContent> actual) {
        Assert.assertEquals(list.size(), actual.getValue().value());
        int[] subtreeSizeAndLeafCount = actual.subtreeSizeAndLeafCount();
        Assert.assertEquals(n == 0 ? 1 : 2 * list.size(), subtreeSizeAndLeafCount[0]);
        Assert.assertEquals(list.size(), subtreeSizeAndLeafCount[1]);
    }

    @Test
    public void oneNodeAtMost() {
        final List<int[]> list = Lists.newArrayList();
        IntArrayCallback callback = new ListCollector(list);
        for (int n = 0; n < 8; ++n) {
            list.clear();
            // Exact sum means that no array will be a prefix of another one.
            TestUtils.generateSpecWithExactSum(n, 2 * n, callback);
            Collections.sort(list, IntArrayComparator.INSTANCE);
            BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT, 1);
            MapNode<Integer, LongContent> actual = createTree(list, rollup);
            Assert.assertEquals(MapNode.create(new LongContent(list.size()),
                    ImmutableMap.<Integer, MapNode<Integer, LongContent>>of()), actual);
        }
    }

    @Test
    public void subtreeIsOptimalWithinConstraints() {
        final List<int[]> list = Lists.newArrayList();
        IntArrayCallback callback = new ListCollector(list);
        for (int n = 0; n < 3; ++n) {
            list.clear();
            // Exact sum means that no array will be a prefix of another one.
            TestUtils.generateSpecWithExactSum(n, 2 * n, callback);
            Collections.sort(list, IntArrayComparator.INSTANCE);
            BoundedRollup<Integer, LongContent> rollup = BoundedRollup.create(TestUtils.ZERO_LONG_CONTENT,
                    Integer.MAX_VALUE);
            MapNode<Integer, LongContent> fullTreeRoot = createTree(list, rollup);
            checkTreeIsComplete(list, n, fullTreeRoot);
            int fullTreeSize = fullTreeRoot.subtreeSizeAndLeafCount()[0];
            for (int i = 1; i <= fullTreeSize + 5; ++i) {
                List<List<MapNode<Integer, LongContent>>> allPossibleExpansions = allPossibleExpansions(
                        fullTreeRoot, i);
                BoundedRollup<Integer, LongContent> constrainedRollup = BoundedRollup
                        .create(TestUtils.ZERO_LONG_CONTENT, i);
                MapNode<Integer, LongContent> constrainedTreeRoot = createTree(list, constrainedRollup);
                List<MapNode<Integer, LongContent>> actual = constrainedTreeRoot.preorder();
                List<List<MapNode<Integer, LongContent>>> all = toSortedValueListList(allPossibleExpansions);
                List<MapNode<Integer, LongContent>> constrained = toSortedValueList(actual);
                List<List<MapNode<Integer, LongContent>>> allSameSize = Lists.newArrayList();
                for (List<MapNode<Integer, LongContent>> row : all) {
                    Assert.assertTrue(row.size() <= constrained.size());
                    if (row.size() == constrained.size()) {
                        allSameSize.add(row);
                    }
                }
                Assert.assertEquals(Collections.max(allSameSize, new ListComparator<MapNode<Integer, LongContent>>(
                        new MapNodeValueComparator<Integer, LongContent>())), constrained);
            }
        }
    }

    private MapNode<Integer, LongContent> createTree(final List<int[]> list,
            BoundedRollup<Integer, LongContent> rollup) {
        for (int[] array : list) {
            rollup.feedRow(Ints.asList(array).iterator(), TestUtils.ONE_LONG_CONTENT);
        }
        MapNode<Integer, LongContent> root = rollup.finish();
        return root;
    }

    /**
     * Test the test helper method {@link #allPossibleExpansions}.
     */
    @Test
    public void allPossibleExpansionsForRootOnly() {
        MapNode<Integer, String> root = MapNode.create("x", ImmutableMap.<Integer, MapNode<Integer, String>>of());
        Assert.assertTrue(allPossibleExpansions(root, 0).isEmpty());
        for (int i = 1; i < 3; ++i) {
            MoreAsserts.assertContentsAnyOrder(allPossibleExpansions(root, i), ImmutableList.of(root));
        }
    }

    /**
     * Test the test helper method {@link #allPossibleExpansions}.
     */
    @Test
    public void allPossibleExpansionsForThreeNodes() {
        MapNode<Integer, String> rightGrandchild = MapNode.create("d",
                ImmutableMap.<Integer, MapNode<Integer, String>>of());
        MapNode<Integer, String> rightChild = MapNode.create("c",
                ImmutableMap.<Integer, MapNode<Integer, String>>of(2, rightGrandchild));
        MapNode<Integer, String> leftChild = MapNode.create("b",
                ImmutableMap.<Integer, MapNode<Integer, String>>of());
        MapNode<Integer, String> root = MapNode.create("a",
                ImmutableMap.<Integer, MapNode<Integer, String>>of(0, leftChild, 1, rightChild));
        Assert.assertTrue(allPossibleExpansions(root, 0).isEmpty());
        MoreAsserts.assertContentsAnyOrder(allPossibleExpansions(root, 1), ImmutableList.of(root));
        MoreAsserts.assertContentsAnyOrder(allPossibleExpansions(root, 2), ImmutableList.of(root));
        MoreAsserts.assertContentsAnyOrder(toIdentitySetList(allPossibleExpansions(root, 3)),
                toIdentitySet(ImmutableList.of(root)),
                toIdentitySet(ImmutableList.of(root, leftChild, rightChild)));
        List<Set<MapNode<Integer, String>>> actual = toIdentitySetList(allPossibleExpansions(root, 4));
        MoreAsserts.assertContentsAnyOrder(actual, toIdentitySet(ImmutableList.of(root)),
                toIdentitySet(ImmutableList.of(root, leftChild, rightChild)),
                toIdentitySet(ImmutableList.of(root, leftChild, rightChild, rightGrandchild)));
    }

    private static <K, V> List<Set<MapNode<K, V>>> toIdentitySetList(List<List<MapNode<K, V>>> listList) {
        List<Set<MapNode<K, V>>> setList = new ArrayList<Set<MapNode<K, V>>>(listList.size());
        for (List<MapNode<K, V>> list : listList) {
            Set<MapNode<K, V>> set = toIdentitySet(list);
            setList.add(set);
        }
        return setList;
    }

    private static <V, K> Set<MapNode<K, V>> toIdentitySet(List<MapNode<K, V>> list) {
        Set<MapNode<K, V>> set = Collections.newSetFromMap(new IdentityHashMap<MapNode<K, V>, Boolean>());
        set.addAll(list);
        Assert.assertEquals(list.size(), set.size());
        return set;
    }

    private static <K, V extends Comparable<V>> List<List<MapNode<K, V>>> toSortedValueListList(
            List<List<MapNode<K, V>>> listList) {
        List<List<MapNode<K, V>>> result = new ArrayList<List<MapNode<K, V>>>(listList.size());
        for (List<MapNode<K, V>> list : listList) {
            List<MapNode<K, V>> set = toSortedValueList(list);
            result.add(set);
        }
        return result;
    }

    private static <K, V extends Comparable<V>> List<MapNode<K, V>> toSortedValueList(List<MapNode<K, V>> list) {
        List<MapNode<K, V>> result = new ArrayList<MapNode<K, V>>(list.size());
        for (MapNode<K, V> node : list) {
            result.add(MapNode.create(node.getValue(), ImmutableMap.<K, MapNode<K, V>>of()));
        }
        Collections.sort(result, new MapNodeValueComparator<K, V>());
        return result;
    }

    private static class MapNodeValueComparator<K, V extends Comparable<V>> implements Comparator<MapNode<K, V>> {
        @Override
        public int compare(MapNode<K, V> o1, MapNode<K, V> o2) {
            return o1.getValue().compareTo(o2.getValue());
        }
    }

    private static <K, V> List<List<MapNode<K, V>>> allPossibleExpansions(final MapNode<K, V> node, int maxNodes) {
        List<List<MapNode<K, V>>> result = Lists.newArrayList();
        for (int i = 1; i <= maxNodes; ++i) {
            List<List<MapNode<K, V>>> expansions = allExpansionsWithExactNodeCount(node, i);
            result.addAll(expansions);
        }
        return result;
    }

    private static <K, V> List<List<MapNode<K, V>>> allExpansionsWithExactNodeCount(final MapNode<K, V> node,
            final int exactNodes) {
        if (exactNodes == 1) {
            return ImmutableList.of((List<MapNode<K, V>>) ImmutableList.of(node));
        }
        List<List<MapNode<K, V>>> result = Lists.newArrayList();
        assert exactNodes > 1;
        if (exactNodes > node.getChildren().size()) {
            final List<MapNode<K, V>> children = ImmutableList.copyOf(node.getChildren().values());
            final List<List<List<List<MapNode<K, V>>>>> childrenExpansions = Lists.newArrayList();
            IntArrayCallback callback = new IntArrayCallback() {
                @Override
                public void call(int[] m) {
                    assert m.length <= children.size();
                    if (m.length == children.size()) {
                        List<List<List<MapNode<K, V>>>> expansionSet = Lists.newArrayList();
                        for (int i = 0; i < m.length; ++i) {
                            List<List<MapNode<K, V>>> childExpansions = allExpansionsWithExactNodeCount(
                                    children.get(i), m[i] + 1);
                            for (List<MapNode<K, V>> childExpansion : childExpansions) {
                                Assert.assertEquals(m[i] + 1, childExpansion.size());
                            }
                            if (childExpansions.isEmpty()) {
                                return;
                            } else {
                                expansionSet.add(childExpansions);
                            }
                        }
                        Assert.assertEquals(m.length, expansionSet.size());
                        childrenExpansions.add(expansionSet);
                    }
                }
            };
            TestUtils.generateSpecWithExactSum(node.getChildren().size(), exactNodes - 1 - children.size(),
                    callback);
            int[] childOffset = new int[node.getChildren().size()];
            for (List<List<List<MapNode<K, V>>>> childrenExpansion : childrenExpansions) {
                assert childrenExpansion.size() == children.size();
                Arrays.fill(childOffset, -1);
                int k = 0;
                while (k >= 0) {
                    if (k == childrenExpansion.size()) {
                        List<MapNode<K, V>> expansion = Lists.newArrayList();
                        for (int i = 0; i < k; ++i) {
                            expansion.addAll(childrenExpansion.get(i).get(childOffset[i]));
                        }
                        assert expansion.size() == exactNodes - 1;
                        expansion.add(node);
                        result.add(expansion);
                        k--;
                    } else if (childOffset[k] < childrenExpansion.get(k).size() - 1) {
                        childOffset[k++]++;
                    } else {
                        childOffset[k--] = -1;
                    }
                }
            }
        }
        for (List<MapNode<K, V>> list : result) {
            Assert.assertEquals(exactNodes, list.size());
        }
        return result;
    }

    private LongContent newCountingArray(long count) {
        return new LongContent(count);
    }

    /**
     * Will put all array callbacks which don't have zeroes into the supplied
     * list.
     */
    private static class ListCollector implements IntArrayCallback {

        private final List<int[]> list;

        public ListCollector(List<int[]> list) {
            this.list = list;
        }

        @Override
        public void call(int[] m) {
            if (!ArrayUtils.contains(m, 0)) {
                list.add(Arrays.copyOf(m, m.length));
            }
        }
    }

    private static class ListComparator<T> implements Comparator<Iterable<T>> {

        private final Comparator<T> delegate;

        public ListComparator(Comparator<T> delegate) {
            this.delegate = delegate;
        }

        @Override
        public int compare(Iterable<T> o1, Iterable<T> o2) {
            Iterator<T> it1 = o1.iterator();
            Iterator<T> it2 = o2.iterator();
            int result = 0;
            while (it1.hasNext()) {
                if (!it2.hasNext()) {
                    result = +1;
                    break;
                }
                T t1 = it1.next();
                T t2 = it2.next();
                int cmp = delegate.compare(t1, t2);
                if (cmp != 0) {
                    result = cmp;
                    break;
                }
            }
            if (result == 0) {
                result = it2.hasNext() ? -1 : 0;
            }
            return result;
        }
    }
}