001package org.clafer.collection;
002
003import java.lang.reflect.Array;
004import java.util.ArrayList;
005import java.util.List;
006import org.clafer.common.Check;
007
008/**
009 * A 2-tuple.
010 *
011 *
012 * @param <A> the type of fst
013 * @param <B> the type of snd
014 * @author jimmy
015 */
016public class Pair<A, B> {
017
018    private final A fst;
019    private final B snd;
020
021    /**
022     * Construct a tuple.
023     *
024     * @param fst
025     * @param snd
026     */
027    public Pair(A fst, B snd) {
028        this.fst = Check.notNull(fst);
029        this.snd = Check.notNull(snd);
030    }
031
032    public Pair(Pair<? extends A, ? extends B> pair) {
033        this(pair.getFst(), pair.getSnd());
034    }
035
036    public A getFst() {
037        return fst;
038    }
039
040    public B getSnd() {
041        return snd;
042    }
043
044    /**
045     * Returns the first element of the tuples in the same order. Equivalent to
046     * the Haskell code {@code map fst}.
047     *
048     * @param <A> the type of first element in the pairs
049     * @param <B> the type of second element in the pairs
050     * @param pairs the tuples
051     * @return the first element of the tuples
052     */
053    public static <A, B> List<A> mapFst(List<Pair<A, B>> pairs) {
054        List<A> fsts = new ArrayList<>(pairs.size());
055        for (Pair<A, B> pair : pairs) {
056            fsts.add(pair.getFst());
057        }
058        return fsts;
059    }
060
061    /**
062     * Returns the first element of the tuples in the same order. Equivalent to
063     * the Haskell code {@code map fst}.
064     *
065     * @param <A> the type of first element in the pairs
066     * @param <B> the type of second element in the pairs
067     * @param pairs the tuples
068     * @param array the array to write to if the size is correct
069     * @return the first element of the tuples
070     */
071    @SafeVarargs
072    public static <A, B> A[] mapFst(Pair<A, B>[] pairs, A... array) {
073        @SuppressWarnings("unchecked")
074        A[] to = array.length == pairs.length
075                ? array
076                : (A[]) Array.newInstance(array.getClass().getComponentType(), pairs.length);
077        for (int i = 0; i < to.length; i++) {
078            to[i] = pairs[i].getFst();
079        }
080        return to;
081    }
082
083    /**
084     * Returns the second element of the tuples in the same order. Equivalent to
085     * the Haskell code {@code map snd}.
086     *
087     * @param <A> the type of first element in the pairs
088     * @param <B> the type of second element in the pairs
089     * @param pairs the tuples
090     * @return the second element of the tuples
091     */
092    public static <A, B> List<B> mapSnd(List<Pair<A, B>> pairs) {
093        List<B> snds = new ArrayList<>(pairs.size());
094        for (Pair<A, B> pair : pairs) {
095            snds.add(pair.getSnd());
096        }
097        return snds;
098    }
099
100    /**
101     * Returns the second element of the tuples in the same order. Equivalent to
102     * the Haskell code {@code map snd}.
103     *
104     * @param <A> the type of first element in the pairs
105     * @param <B> the type of second element in the pairs
106     * @param pairs the tuples
107     * @param array the array to write to if the size is correct
108     * @return the second element of the tuples
109     */
110    @SafeVarargs
111    public static <A, B> B[] mapSnd(Pair<A, B>[] pairs, B... array) {
112        @SuppressWarnings("unchecked")
113        B[] to = array.length == pairs.length
114                ? array
115                : (B[]) Array.newInstance(array.getClass().getComponentType(), pairs.length);
116        for (int i = 0; i < to.length; i++) {
117            to[i] = pairs[i].getSnd();
118        }
119        return to;
120    }
121
122    @Override
123    public boolean equals(Object obj) {
124        if (obj instanceof Pair<?, ?>) {
125            Pair<?, ?> other = (Pair<?, ?>) obj;
126            return fst.equals(other.fst) && snd.equals(other.snd);
127        }
128        return false;
129    }
130
131    @Override
132    public int hashCode() {
133        return fst.hashCode() ^ snd.hashCode();
134    }
135
136    @Override
137    public String toString() {
138        return "(" + fst + ", " + snd + ")";
139    }
140}