@Test
 public void properSubsetEqual() {
   MutableSet<String> setA = UnifiedSet.newSetWith("Bertha", null, "Myra");
   MutableSet<String> setB = UnifiedSet.newSetWith("Myra", "Bertha", null);
   Assert.assertFalse(Sets.isProperSubsetOf(setA, setB));
   Assert.assertFalse(Sets.isProperSubsetOf(setB, setA));
 }
 @Test
 public void properSubsetNotEmpty() {
   MutableSet<String> singletonSet = UnifiedSet.newSetWith("Bertha");
   MutableSet<String> doubletonSet = UnifiedSet.newSetWith("Bertha", "Myra");
   Assert.assertTrue(Sets.isProperSubsetOf(singletonSet, doubletonSet));
   Assert.assertFalse(Sets.isProperSubsetOf(doubletonSet, singletonSet));
 }
 @Test
 public void flatCollect() {
   Function<Integer, MutableSet<String>> function =
       object -> UnifiedSet.newSetWith(object.toString());
   Verify.assertListsEqual(
       FastList.newListWith("1"), SingletonListTest.newWith(1).flatCollect(function));
   Verify.assertSetsEqual(
       UnifiedSet.newSetWith("1"),
       SingletonListTest.newWith(1).flatCollect(function, UnifiedSet.<String>newSet()));
 }
 @Test
 public void cartesianProduct() {
   MutableSet<Integer> set1 = UnifiedSet.newSetWith(1, 2);
   MutableSet<Integer> set2 = UnifiedSet.newSetWith(2, 3, 4);
   MutableBag<Pair<Integer, Integer>> expectedCartesianProduct =
       Bags.mutable.of(
           Tuples.pair(1, 2),
           Tuples.pair(2, 2),
           Tuples.pair(1, 3),
           Tuples.pair(2, 3),
           Tuples.pair(1, 4),
           Tuples.pair(2, 4));
   Assert.assertEquals(expectedCartesianProduct, Sets.cartesianProduct(set1, set2).toBag());
 }
 @Test
 public void fixedSize() {
   FixedSizeSetFactory setFactory = Sets.fixedSize;
   Assert.assertEquals(UnifiedSet.newSet(), setFactory.of());
   Verify.assertInstanceOf(FixedSizeSet.class, setFactory.of());
   Assert.assertEquals(UnifiedSet.newSetWith(1), setFactory.of(1));
   Verify.assertInstanceOf(FixedSizeSet.class, setFactory.of(1));
   Assert.assertEquals(UnifiedSet.newSetWith(1, 2), setFactory.of(1, 2));
   Verify.assertInstanceOf(FixedSizeSet.class, setFactory.of(1, 2));
   Assert.assertEquals(UnifiedSet.newSetWith(1, 2, 3), setFactory.of(1, 2, 3));
   Verify.assertInstanceOf(FixedSizeSet.class, setFactory.of(1, 2, 3));
   Assert.assertEquals(UnifiedSet.newSetWith(1, 2, 3, 4), setFactory.of(1, 2, 3, 4));
   Verify.assertInstanceOf(FixedSizeSet.class, setFactory.of(1, 2, 3, 4));
 }
 @Test
 public void powerSet() {
   MutableSet<Integer> set = UnifiedSet.newSetWith(1, 2, 3);
   MutableSet<MutableSet<Integer>> expectedPowerSet =
       UnifiedSet.<MutableSet<Integer>>newSetWith(
           UnifiedSet.<Integer>newSet(),
           UnifiedSet.newSetWith(1),
           UnifiedSet.newSetWith(2),
           UnifiedSet.newSetWith(3),
           UnifiedSet.newSetWith(1, 2),
           UnifiedSet.newSetWith(1, 3),
           UnifiedSet.newSetWith(2, 3),
           UnifiedSet.newSetWith(1, 2, 3));
   Assert.assertEquals(expectedPowerSet, Sets.powerSet(set));
 }
  @Test
  public void newListWithCollection() {
    Verify.assertEmpty(MultiReaderFastList.newList(Lists.fixedSize.of()));
    Verify.assertEmpty(MultiReaderFastList.newList(Sets.fixedSize.of()));
    Verify.assertEmpty(MultiReaderFastList.newList(FastList.newList()));
    Verify.assertEmpty(MultiReaderFastList.newList(FastList.newList(4)));

    MutableList<Integer> setToList =
        MultiReaderFastList.newList(UnifiedSet.newSetWith(1, 2, 3, 4, 5));
    Verify.assertNotEmpty(setToList);
    Verify.assertSize(5, setToList);
    Verify.assertContainsAll(setToList, 1, 2, 3, 4, 5);

    MutableList<Integer> arrayListToList =
        MultiReaderFastList.newList(Lists.fixedSize.of(1, 2, 3, 4, 5));
    Verify.assertNotEmpty(arrayListToList);
    Verify.assertSize(5, arrayListToList);
    Verify.assertStartsWith(arrayListToList, 1, 2, 3, 4, 5);

    MutableList<Integer> fastListToList =
        MultiReaderFastList.newList(FastList.<Integer>newList().with(1, 2, 3, 4, 5));
    Verify.assertNotEmpty(fastListToList);
    Verify.assertSize(5, fastListToList);
    Verify.assertStartsWith(fastListToList, 1, 2, 3, 4, 5);
  }
 @Override
 @Test
 public void reject() {
   Verify.assertContainsAll(this.newWith(1, 2, 3, 4).reject(Predicates.lessThan(3)), 3, 4);
   Verify.assertContainsAll(
       this.newWith(1, 2, 3, 4).reject(Predicates.lessThan(3), UnifiedSet.newSet()), 3, 4);
 }
 @Test
 public void properSubsetEmpty() {
   MutableSet<String> emptySet = mSet();
   MutableSet<String> singletonSet = UnifiedSet.newSetWith("Bertha");
   Assert.assertTrue(Sets.isProperSubsetOf(emptySet, singletonSet));
   Assert.assertFalse(Sets.isProperSubsetOf(singletonSet, emptySet));
 }
 @Test
 public void newSet() {
   for (int i = 1; i <= 5; i++) {
     Interval interval = Interval.oneTo(i);
     Verify.assertEqualsAndHashCode(UnifiedSet.newSet(interval), Sets.immutable.ofAll(interval));
   }
 }
 @Test
 public void intersectAllIdentical() {
   MutableSet<String> names =
       Sets.intersectAll(
           this.identicalSets.get(0), this.identicalSets.get(1), this.identicalSets.get(2));
   Assert.assertEquals(UnifiedSet.newSetWith("Tom", "Dick", "Harry", null), names);
 }
 @Test
 public void intersectAllOverlapping() {
   MutableSet<String> names =
       Sets.intersectAll(
           this.overlappingSets.get(0), this.overlappingSets.get(1), this.overlappingSets.get(2));
   Assert.assertEquals(UnifiedSet.newSetWith("Dick"), names);
 }
 @Test
 public void rejectWith() {
   Verify.assertEmpty(SingletonListTest.newWith(1).rejectWith(Predicates2.<Integer>lessThan(), 3));
   Verify.assertContainsAll(
       SingletonListTest.newWith(1)
           .rejectWith(Predicates2.<Integer>greaterThan(), 3, UnifiedSet.<Integer>newSet()),
       1);
 }
 @Test
 public void unionAllOverlapping() {
   MutableSet<String> names =
       Sets.unionAll(
           this.overlappingSets.get(0), this.overlappingSets.get(1), this.overlappingSets.get(2));
   Assert.assertEquals(
       UnifiedSet.newSetWith("Tom", "Dick", "Harry", "Larry", "Paul", null), names);
 }
 @Test
 public void unionAllUnique() {
   MutableSet<String> names =
       Sets.unionAll(this.uniqueSets.get(0), this.uniqueSets.get(1), this.uniqueSets.get(2));
   Assert.assertEquals(
       UnifiedSet.newSetWith(
           "Tom", "Dick", "Harry", "Jane", "Sarah", "Mary", "Fido", "Spike", "Spuds", null),
       names);
 }
 @Test
 public void copySet() {
   Verify.assertInstanceOf(ImmutableSet.class, Sets.immutable.ofAll(Sets.fixedSize.of()));
   MutableSet<Integer> set = Sets.fixedSize.of(1);
   ImmutableSet<Integer> immutableSet = set.toImmutable();
   Verify.assertInstanceOf(ImmutableSet.class, Sets.immutable.ofAll(set));
   Verify.assertInstanceOf(
       ImmutableSet.class, Sets.immutable.ofAll(UnifiedSet.newSetWith(1, 2, 3, 4, 5)));
   Assert.assertSame(Sets.immutable.ofAll(immutableSet.castToSet()), immutableSet);
 }
 @Test
 public void differenceAllUnique() {
   MutableSet<String> names =
       Sets.differenceAll(this.uniqueSets.get(0), this.uniqueSets.get(1), this.uniqueSets.get(2));
   Assert.assertEquals(UnifiedSet.newSetWith("Harry", "Tom", "Dick", null), names);
   Verify.assertSetsEqual(
       names,
       Sets.difference(
           Sets.difference(this.uniqueSets.get(0), this.uniqueSets.get(1)),
           this.uniqueSets.get(2)));
 }
 @Test
 public void differenceAllOverlapping() {
   MutableSet<String> names =
       Sets.differenceAll(
           this.overlappingSets.get(0), this.overlappingSets.get(1), this.overlappingSets.get(2));
   Assert.assertEquals(UnifiedSet.newSetWith("Harry"), names);
   Verify.assertSetsEqual(
       names,
       Sets.difference(
           Sets.difference(this.overlappingSets.get(0), this.overlappingSets.get(1)),
           this.overlappingSets.get(2)));
 }
 @Test
 public void differenceAllIdentical() {
   MutableSet<String> names =
       Sets.differenceAll(
           this.identicalSets.get(0), this.identicalSets.get(1), this.identicalSets.get(2));
   Assert.assertEquals(UnifiedSet.newSetWith(), names);
   Verify.assertSetsEqual(
       names,
       Sets.difference(
           Sets.difference(this.identicalSets.get(0), this.identicalSets.get(1)),
           this.identicalSets.get(2)));
 }
 @Test
 public void mutables() {
   MutableSetFactory setFactory = Sets.mutable;
   Assert.assertEquals(UnifiedSet.newSet(), setFactory.of());
   Verify.assertInstanceOf(MutableSet.class, setFactory.of());
   Assert.assertEquals(UnifiedSet.newSetWith(1), setFactory.of(1));
   Verify.assertInstanceOf(MutableSet.class, setFactory.of(1));
   Assert.assertEquals(UnifiedSet.newSetWith(1, 2), setFactory.of(1, 2));
   Verify.assertInstanceOf(MutableSet.class, setFactory.of(1, 2));
   Assert.assertEquals(UnifiedSet.newSetWith(1, 2, 3), setFactory.of(1, 2, 3));
   Verify.assertInstanceOf(MutableSet.class, setFactory.of(1, 2, 3));
   Assert.assertEquals(UnifiedSet.newSetWith(1, 2, 3, 4), setFactory.of(1, 2, 3, 4));
   Verify.assertInstanceOf(MutableSet.class, setFactory.of(1, 2, 3, 4));
   Assert.assertEquals(UnifiedSet.newSetWith(1, 2, 3, 4, 5), setFactory.of(1, 2, 3, 4, 5));
   Verify.assertInstanceOf(MutableSet.class, setFactory.of(1, 2, 3, 4, 5));
   Assert.assertEquals(
       UnifiedSet.newSetWith(1, 2, 3, 4, 5),
       setFactory.ofAll(UnifiedSet.newSetWith(1, 2, 3, 4, 5)));
   Verify.assertInstanceOf(
       MutableSet.class, setFactory.ofAll(UnifiedSet.newSetWith(1, 2, 3, 4, 5)));
 }
 @Test
 public void collect() {
   Verify.assertContainsAll(SingletonListTest.newWith(1).collect(String::valueOf), "1");
   Verify.assertContainsAll(
       SingletonListTest.newWith(1).collect(String::valueOf, UnifiedSet.<String>newSet()), "1");
 }
 @Test
 public void cartesianProduct_empty() {
   Assert.assertEquals(
       Bags.mutable.of(),
       HashBag.newBag(Sets.cartesianProduct(UnifiedSet.newSetWith(1, 2), UnifiedSet.newSet())));
 }
 private <E> TreeSet<E> newSortedSet(E... elements) {
   return new TreeSet<>(UnifiedSet.newSetWith(elements));
 }
 @Test
 public void powerSet_empty() {
   Assert.assertEquals(
       UnifiedSet.newSetWith(UnifiedSet.newSet()), Sets.powerSet(UnifiedSet.newSet()));
 }
 private <E> TreeSet<E> newReverseSortedSet(E... elements) {
   TreeSet<E> set = new TreeSet<>(Collections.reverseOrder());
   set.addAll(UnifiedSet.newSetWith(elements));
   return set;
 }
 @Test
 public void intersectAllUnique() {
   MutableSet<String> names =
       Sets.intersectAll(this.uniqueSets.get(0), this.uniqueSets.get(1), this.uniqueSets.get(2));
   Assert.assertEquals(UnifiedSet.newSetWith(), names);
 }
 private <E> Procedure2<Set<E>, E[]> containsExactlyProcedure() {
   return (set, elements) -> Assert.assertEquals(UnifiedSet.newSetWith(elements), set);
 }
 private <E> UnifiedSet<E> newUnsortedSet(E... elements) {
   return UnifiedSet.newSetWith(elements);
 }
@State(Scope.Thread)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public class AnagramSetTest extends AbstractJMHTestRunner {
  private static final int SIZE = 1_000_000;
  private static final int BATCH_SIZE = 10_000;

  private static final int SIZE_THRESHOLD = 10;
  private final UnifiedSet<String> ecWords =
      UnifiedSet.newSet(
          FastList.newWithNValues(SIZE, () -> RandomStringUtils.randomAlphabetic(5).toUpperCase()));
  private final Set<String> jdkWords = new HashSet<>(this.ecWords);

  private ExecutorService executorService;

  @Setup
  public void setUp() {
    this.executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  }

  @TearDown
  public void tearDown() throws InterruptedException {
    this.executorService.shutdownNow();
    this.executorService.awaitTermination(1L, TimeUnit.SECONDS);
  }

  @Benchmark
  public void serial_eager_scala() {
    AnagramSetScalaTest.serial_eager_scala();
  }

  @Benchmark
  public void serial_lazy_scala() {
    AnagramSetScalaTest.serial_lazy_scala();
  }

  @Benchmark
  public void parallel_lazy_scala() {
    AnagramSetScalaTest.parallel_lazy_scala();
  }

  @Benchmark
  public void serial_eager_ec() {
    MutableSetMultimap<Alphagram, String> groupBy = this.ecWords.groupBy(Alphagram::new);
    groupBy
        .multiValuesView()
        .select(iterable -> iterable.size() >= SIZE_THRESHOLD)
        .toSortedList(Comparators.<RichIterable<String>>byIntFunction(RichIterable::size))
        .asReversed()
        .collect(iterable -> iterable.size() + ": " + iterable)
        .forEach(Procedures.cast(e -> Assert.assertFalse(e.isEmpty())));
  }

  @Benchmark
  public void parallel_eager_ec() {
    MutableMultimap<Alphagram, String> groupBy =
        ParallelIterate.groupBy(this.ecWords, Alphagram::new);
    CompositeFastList<RichIterable<String>> select =
        ParallelIterate.select(
            groupBy.multiValuesView(),
            iterable -> iterable.size() >= SIZE_THRESHOLD,
            new CompositeFastList<>(),
            false);
    Collection<String> collect =
        ParallelIterate.collect(
            select
                .toSortedList(Comparators.<RichIterable<String>>byIntFunction(RichIterable::size))
                .asReversed(),
            iterable -> iterable.size() + ": " + iterable);
    ParallelIterate.forEach(collect, Procedures.cast(e -> Assert.assertFalse(e.isEmpty())));
  }

  @Benchmark
  public void parallel_lazy_ec() {
    UnsortedSetMultimap<Alphagram, String> multimap =
        this.ecWords.asParallel(this.executorService, BATCH_SIZE).groupBy(Alphagram::new);
    FastList<Pair<Integer, String>> pairs =
        (FastList<Pair<Integer, String>>)
            FastList.newList(multimap.multiValuesView())
                .asParallel(this.executorService, BATCH_SIZE)
                .select(iterable -> iterable.size() >= SIZE_THRESHOLD)
                .collect(
                    iterable -> Tuples.pair(iterable.size(), iterable.size() + ": " + iterable))
                .toSortedList((pair1, pair2) -> Integer.compare(pair2.getOne(), pair1.getOne()));
    pairs
        .asParallel(this.executorService, BATCH_SIZE)
        .collect(Pair::getTwo)
        .forEach(Procedures.cast(e -> Assert.assertFalse(e.isEmpty())));
  }

  @Benchmark
  public void parallel_eager_forkjoin_ec() {
    MutableMultimap<Alphagram, String> groupBy = FJIterate.groupBy(this.ecWords, Alphagram::new);
    CompositeFastList<RichIterable<String>> select =
        FJIterate.select(
            groupBy.multiValuesView(),
            iterable -> iterable.size() >= SIZE_THRESHOLD,
            new CompositeFastList<>(),
            false);
    Collection<String> collect =
        FJIterate.collect(
            select
                .toSortedList(Comparators.<RichIterable<String>>byIntFunction(RichIterable::size))
                .asReversed(),
            iterable -> iterable.size() + ": " + iterable);
    FJIterate.forEach(collect, Procedures.cast(e -> Assert.assertFalse(e.isEmpty())));
  }

  @Benchmark
  public void serial_lazy_jdk() {
    Map<Alphagram, Set<String>> groupBy =
        this.jdkWords
            .stream()
            .collect(Collectors.groupingBy(Alphagram::new, Collectors.<String>toSet()));
    groupBy
        .entrySet()
        .stream()
        .map(Map.Entry::getValue)
        .filter(list -> list.size() >= SIZE_THRESHOLD)
        .sorted(Comparator.<Set<String>>comparingInt(Set::size).reversed())
        .map(list -> list.size() + ": " + list)
        .forEach(e -> Assert.assertFalse(e.isEmpty()));
  }

  @Benchmark
  public void serial_lazy_streams_ec() {
    Map<Alphagram, Set<String>> groupBy =
        this.ecWords
            .stream()
            .collect(Collectors.groupingBy(Alphagram::new, Collectors.<String>toSet()));
    groupBy
        .entrySet()
        .stream()
        .map(Map.Entry::getValue)
        .filter(list -> list.size() >= SIZE_THRESHOLD)
        .sorted(Comparator.<Set<String>>comparingInt(Set::size).reversed())
        .map(list -> list.size() + ": " + list)
        .forEach(e -> Assert.assertFalse(e.isEmpty()));
  }

  @Benchmark
  public void parallel_lazy_jdk() {
    Map<Alphagram, Set<String>> groupBy =
        this.jdkWords
            .parallelStream()
            .collect(Collectors.groupingBy(Alphagram::new, Collectors.<String>toSet()));
    groupBy
        .entrySet()
        .parallelStream()
        .map(Map.Entry::getValue)
        .filter(list -> list.size() >= SIZE_THRESHOLD)
        .sorted(Comparator.<Set<String>>comparingInt(Set::size).reversed())
        .parallel()
        .map(list -> list.size() + ": " + list)
        .forEach(e -> Assert.assertFalse(e.isEmpty()));
  }

  @Benchmark
  public void parallel_lazy_streams_ec() {
    Map<Alphagram, Set<String>> groupBy =
        this.ecWords
            .parallelStream()
            .collect(Collectors.groupingBy(Alphagram::new, Collectors.<String>toSet()));
    groupBy
        .entrySet()
        .parallelStream()
        .map(Map.Entry::getValue)
        .filter(list -> list.size() >= SIZE_THRESHOLD)
        .sorted(Comparator.<Set<String>>comparingInt(Set::size).reversed())
        .parallel()
        .map(list -> list.size() + ": " + list)
        .forEach(e -> Assert.assertFalse(e.isEmpty()));
  }

  private static final class Alphagram {
    private final char[] key;

    private Alphagram(String string) {
      this.key = string.toCharArray();
      Arrays.sort(this.key);
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || this.getClass() != o.getClass()) {
        return false;
      }
      Alphagram alphagram = (Alphagram) o;
      return Arrays.equals(this.key, alphagram.key);
    }

    @Override
    public int hashCode() {
      return Arrays.hashCode(this.key);
    }

    @Override
    public String toString() {
      return new String(this.key);
    }
  }
}
 @SafeVarargs
 @Override
 protected final <V> UnifiedSet<V> createCollection(V... args) {
   return UnifiedSet.newSetWith(args);
 }