
package io.dmitryivanov.crdt.helpers;

import java.util.*;

public final class Operations {
    private Operations() {
        // No instances
    }

    public static <E> Set<E> diff(Set<E> firstSet, final Set<E> secondSet) {
        return filtered(firstSet, new Predicate<E>() {
            @Override
            public boolean call(E element) {
                return !secondSet.contains(element);
            }
        });
    }

    public static <E> Set<E> union(Set<E> firstSet, final Set<E> secondSet) {
        return filtered(firstSet, new Predicate<E>() {
            @Override
            public boolean call(E element) {
                return secondSet.contains(element);
            }
        });
    }

    public static <E> Set<E> filtered(Set<E> set, Predicate<E> predicate) {
        final Set<E> newSet = new HashSet<>();
        for (E element : set) {
            if (predicate.call(element)) {
                newSet.add(element);
            }
        }
        return newSet;
    }

    public static <E, R> Set<R> filteredAndMapped(Set<E> set, Predicate<E> predicate,
            Mapper<E, R> mapper) {
        final Set<R> newSet = new HashSet<>();
        for (E element : set) {
            if (predicate.call(element)) {
                newSet.add(mapper.call(element));
            }
        }
        return newSet;
    }

    public static <E> E select(Set<E> set, Predicate2<E, E> predicate) {
        if (set.isEmpty()) {
            throw new IllegalArgumentException("Empty set for select operation");
        }
        E winner = set.iterator().next();
        for (E element : set) {
            if (predicate.call(winner, element)) {
                winner = element;
            }
        }
        return winner;
    }

    public static <K, V> Map<K, Collection<V>> toListMap(Set<V> map, Mapper<V, K> mapper) {
        final Map<K, Collection<V>> newMap = new HashMap<>(map.size());
        for (V element : map) {
            K key = mapper.call(element);
            Collection<V> list = newMap.get(key);
            if (null == list) {
                list = new ArrayList<>();
            }
            list.add(element);
            newMap.put(key, list);
        }
        return newMap;
    }

    public static <K, V, R> Map<K, R> mapValues(Map<K, V> map, Mapper<V, R> mapper) {
        final Map<K, R> newMap = new HashMap<>(map.size());
        for (Map.Entry<K, V> entry : map.entrySet()) {
            newMap.put(entry.getKey(), mapper.call(entry.getValue()));
        }
        return newMap;
    }

    public interface Predicate<E> {
        boolean call(E element);
    }

    public interface Predicate2<E, F> {
        boolean call(E first, F second);
    }

    public interface Mapper<E, R> {
        R call(E element);
    }
}

@@ -519,54 +519,46 @@
     
-return filtered(firstSet,
+final Set%3CE%3E newSet =
  new 
-Predicate%3CE%3E() %7B
+HashSet%3C%3E();
 %0A   
@@ -566,79 +566,75 @@
     
-    @Override%0A            public boolean call(E element) %7B%0A    
+newSet.addAll(firstSet);%0A        newSet.addAll(secondSet);%0A
         
     
@@ -633,72 +633,21 @@
     
-    
 return 
-secondSet.contains(element);%0A            %7D%0A        %7D)
+newSet
 ;%0A  

Fix union of sets