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

/**
 * This class represents immutable tuple, which can be used as key in HashMap.
 * It uses identity instead of "equals" to compare values:
 * tuples are equal only if each corresponding object inside two tuples are identical.
 *
 * Because we use identity (for both equals and hashCode), tuple values could change state
 * and it won't be noticed by IdentTuple.
 */
public final class IdentTuple {

    private final Object[] values;

    private final int hash;

    /**
     * Creates new tuple
     *
     * @param values values
     */
    public IdentTuple(Object... values) {
        this.values = values;
        this.hash = hashCode0(values);
    }

    @Override
    public boolean equals(Object value) {
        return this == value || ((value!=null && IdentTuple.class.equals(value.getClass())) && equals0(values, ((IdentTuple)value).values));
    }

    @Override
    public int hashCode() {
        return hash;
    }

    private static int hashCode0(Object[] values) {
        int hash = 0;
        for(Object v : values) {
            hash ^= HashUtils.murmurHash3(System.identityHashCode(v));
        }
        return hash;
    }

    private static boolean equals0(Object[] a, Object[] b) {
        if(a.length != b.length) {
            return false;
        }
        for(int i=0, n=a.length; i<n; i++) {
            if(a[i] != b[i]) {
                return false;
            }
        }
        return true;
    }

}
