/*
 *  @author Ranides Atterwim {@literal <ranides@gmail.com>}
 *  @copyright Ranides Atterwim
 *  @license WTFPL
 *  @url http://ranides.net/projects/assira
 */

package net.ranides.assira.collection.sets;

import net.ranides.assira.collection.maps.MultiMap;
import net.ranides.assira.generic.Pair;

import java.util.Set;
import java.util.function.BiConsumer;

/**
 * two-dimensional set.
 *
 * Its behaviour is similar to Set, but it takes two keys instead of just one.
 * Most of the time is equivalent to the Set with composite key.
 *
 * It offers additional reduction methods, which returns one-dimensional MultiSet.
 *
 * @param <K1> K1
 * @param <K2> K2
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 */
public interface CrossSet<K1, K2> extends Set<Pair<K1, K2>> {


    /**
     * Checks if set contains provided key pair
     *
     * @param key1 key1
     * @param key2 key2
     * @return bool
     */
    boolean contains(K1 key1, K2 key2);

    boolean add(K1 key1, K2 key2);

    boolean remove(K1 key1, K2 key2);

    /**
     * Creates new MultiSet which contains all entries without first key.
     * Returns MultiSet because this operation can lead to duplicated K2 keys.
     *
     * @return new collection
     */
    MultiSet<K2> reduceFirst();

    /**
     * Creates new MultiSet which contains all entries without second key.
     * Returns MultiSet because this operation can lead to duplicated K1 keys.
     *
     * @return new collection
     */
    MultiSet<K1> reduceSecond();

    /**
     * Creates new MultiMap with content of set.
     * Each K1 key is mapped to K2 accordingly, if (K1,K2) tuple is stored inside.
     * Returns MultiMap because this operation can lead to duplicated K1 keys.
     *
     * @return new collection
     */
    MultiMap<K1, K2> asMap();

    /**
     * Iterates sequentially through all entries inside map.
     * It passed to "action" three arguments: key1, key2, and value.
     *
     * @param action action
     */
    default void forEach(BiConsumer<K1, K2> action) {
        forEach(pair -> action.accept(pair.getFirst(), pair.getSecond()));
    }

}
