package org.aanguita.jacuzzi.maps;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * This class implements a two-direction map. It thus implements a functional and inverse functional relation of
 * elements (bijective function)
 */
public class DoubleMap<K, V> implements Serializable {

    private Map<K, V> directMap;

    private Map<V, K> reverseMap;

    public DoubleMap() {
        directMap = new HashMap<>();
        reverseMap = new HashMap<>();
    }

    public DoubleMap(int initialCapacity) {
        directMap = new HashMap<>(initialCapacity);
        reverseMap = new HashMap<>(initialCapacity);
    }

    public DoubleMap(int initialCapacity, float loadFactor) {
        directMap = new HashMap<>(initialCapacity, loadFactor);
        reverseMap = new HashMap<>(initialCapacity, loadFactor);
    }

    public DoubleMap(DoubleMap<K, V> doubleMap) {
        directMap = new HashMap<>(doubleMap.directMap);
        reverseMap = new HashMap<>(doubleMap.reverseMap);
    }

    public void put(K k, V v) {
        if (directMap.containsKey(k)) {
            remove(k);
        }
        if (reverseMap.containsKey(v)) {
            removeReverse(v);
        }
        directMap.put(k, v);
        reverseMap.put(v, k);
    }

    public V get(K k) {
        return directMap.get(k);
    }

    public K getReverse(V v) {
        return reverseMap.get(v);
    }

    public boolean contains(K k) {
        return directMap.containsKey(k);
    }

    public boolean containsReverse(V v) {
        return reverseMap.containsKey(v);
    }

    public V remove(K k) {
        V v = directMap.remove(k);
        if (v != null) {
            reverseMap.remove(v);
        }
        return v;
    }

    public K removeReverse(V v) {
        K k = reverseMap.remove(v);
        if (k != null) {
            directMap.remove(k);
        }
        return k;
    }

    public boolean isEmpty() {
        return size() == 0;
    }

    public void clear() {
        directMap.clear();
        reverseMap.clear();
    }

    public int size() {
        return directMap.size();
    }

    public Set<K> keySet() {
        return directMap.keySet();
    }

    public Collection<V> values() {
        return directMap.values();
    }

    public Set<Map.Entry<K, V>> entrySet() {
        return directMap.entrySet();
    }
}

@@ -1313,32 +1313,184 @@
 t(v, k);%0A    %7D%0A%0A
+    public void putAll(Map%3C? extends K, ? extends V%3E m) %7B%0A        m.entrySet().stream().forEach(entry -%3E put(entry.getKey(), entry.getValue()));%0A    %7D%0A%0A
     public V get
@@ -1636,16 +1636,19 @@
 contains
+Key
 (K k) %7B%0A
@@ -1722,22 +1722,20 @@
 contains
-Revers
+Valu
 e(V v) %7B
@@ -2418,144 +2418,768 @@
 ion%3C
-V%3E values() %7B%0A        return directMap.values();%0A    %7D%0A%0A    public Set%3CMap.Entry%3CK, V%3E%3E entrySet() %7B%0A        return directMap.entrySet()
+K%3E keys() %7B%0A        return reverseMap.values();%0A    %7D%0A%0A    public Set%3CV%3E valueSet() %7B%0A        return reverseMap.keySet();%0A    %7D%0A%0A    public Collection%3CV%3E values() %7B%0A        return directMap.values();%0A    %7D%0A%0A    public Set%3CMap.Entry%3CK, V%3E%3E entrySet() %7B%0A        return directMap.entrySet();%0A    %7D%0A%0A    @Override%0A    public boolean equals(Object o) %7B%0A        if (this == o) return true;%0A        if (!(o instanceof DoubleMap)) return false;%0A%0A        DoubleMap%3C?, ?%3E doubleMap = (DoubleMap%3C?, ?%3E) o;%0A%0A        return directMap.equals(doubleMap.directMap) && reverseMap.equals(doubleMap.reverseMap);%0A    %7D%0A%0A    @Override%0A    public int hashCode() %7B%0A        int result = directMap.hashCode();%0A        result = 31 * result + reverseMap.hashCode();%0A        return result
 ;%0A  

Add new operations