/* * 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.facebook.presto.util; import com.google.common.collect.Iterables; import org.testng.annotations.Test; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertTrue; public class TestDisjointSet { @Test public void testInitial() throws Exception { DisjointSet<Integer> disjoint = new DisjointSet<>(); // assert that every node is considered its own group for (int i = 0; i < 100; i++) { assertEquals(disjoint.find(i).intValue(), i); } assertEquals(disjoint.getEquivalentClasses().size(), 100); } @Test public void testMergeAllSequentially() throws Exception { DisjointSet<Integer> disjoint = new DisjointSet<>(); // insert pair (i, i+1); assert all inserts are considered new for (int i = 0; i < 100; i++) { assertTrue(disjoint.findAndUnion(i, i + 1)); if (i != 0) { assertEquals(disjoint.find(i - 1), disjoint.find(i)); } if (i != 99) { assertNotEquals(disjoint.find(i + 1), disjoint.find(i + 2)); } } // assert every pair (i, j) is in the same set for (int i = 0; i <= 100; i++) { for (int j = 0; j <= 100; j++) { assertEquals(disjoint.find(i), disjoint.find(j)); assertFalse(disjoint.findAndUnion(i, j)); } } Collection<Set<Integer>> equivalentClasses = disjoint.getEquivalentClasses(); assertEquals(equivalentClasses.size(), 1); assertEquals(Iterables.getOnlyElement(equivalentClasses).size(), 101); } @Test public void testMergeAllBackwardsSequentially() throws Exception { DisjointSet<Integer> disjoint = new DisjointSet<>(); // insert pair (i, i+1); assert all inserts are considered new for (int i = 100; i > 0; i--) { assertTrue(disjoint.findAndUnion(i, i - 1)); if (i != 100) { assertEquals(disjoint.find(i + 1), disjoint.find(i)); } if (i != 1) { assertNotEquals(disjoint.find(i - 1), disjoint.find(i - 2)); } } // assert every pair (i, j) is in the same set for (int i = 0; i <= 100; i++) { for (int j = 0; j <= 100; j++) { assertEquals(disjoint.find(i), disjoint.find(j)); assertFalse(disjoint.findAndUnion(i, j)); } } Collection<Set<Integer>> equivalentClasses = disjoint.getEquivalentClasses(); assertEquals(equivalentClasses.size(), 1); assertEquals(Iterables.getOnlyElement(equivalentClasses).size(), 101); } @Test public void testMergeFourGroups() throws Exception { DisjointSet<Integer> disjoint = new DisjointSet<>(); // insert pair (i, i+1); assert all inserts are considered new List<Integer> inputs = IntStream.range(0, 96).boxed().collect(Collectors.toList()); Collections.shuffle(inputs); for (int i : inputs) { assertTrue(disjoint.findAndUnion(i, i + 4)); } // assert every pair (i, j) is in the same set for (int i = 0; i < 100; i++) { for (int j = 0; j < 100; j++) { if ((i - j) % 4 == 0) { assertEquals(disjoint.find(i), disjoint.find(j)); assertFalse(disjoint.findAndUnion(i, j)); } else { assertNotEquals(disjoint.find(i), disjoint.find(j)); } } } Collection<Set<Integer>> equivalentClasses = disjoint.getEquivalentClasses(); assertEquals(equivalentClasses.size(), 4); equivalentClasses.stream() .forEach(equivalentClass -> assertEquals(equivalentClass.size(), 25)); } @Test public void testMergeRandomly() throws Exception { DisjointSet<Double> disjoint = new DisjointSet<>(); Random rand = new Random(); // Don't use List here. That will box the primitive early. // Boxing need to happen right before calls to DisjointSet to test that correct equality check is used for comparisons. double[] numbers = new double[100]; for (int i = 0; i < 100; i++) { numbers[i] = rand.nextDouble(); disjoint.find(numbers[i]); } int groupCount = 100; while (groupCount > 1) { // this loop roughly runs 180-400 times boolean newEquivalence = disjoint.findAndUnion(numbers[rand.nextInt(100)], numbers[rand.nextInt(100)]); if (newEquivalence) { groupCount--; } assertEquals(disjoint.getEquivalentClasses().size(), groupCount); } } }