001/*
002 * Copyright (c) 2015-2020, Oracle and/or its affiliates. All rights reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.tribuo.util.infotheory.impl;
018
019import java.io.Serializable;
020import java.util.Objects;
021import java.util.logging.Logger;
022
023/**
024 * A triple of things. The inner pairs are cached, as is the hashcode.
025 * <p>
026 * The cache is calculated on construction, and the objects inside the triple are thus expected to be immutable.
027 * If they aren't then the behaviour is undefined (and you shouldn't use this class).
028 * @param <T1> The type of the first object.
029 * @param <T2> The type of the second object.
030 * @param <T3> The type of the third object.
031 */
032public class CachedTriple<T1, T2, T3> implements Serializable {
033    private static final long serialVersionUID = 1L;
034
035    private static final Logger logger = Logger.getLogger(CachedTriple.class.getName());
036
037    private final CachedPair<T1,T2> ab;
038    private final CachedPair<T1,T3> ac;
039    private final CachedPair<T2,T3> bc;
040
041    /**
042     * The first element.
043     */
044    protected final T1 a;
045    /**
046     * The second element.
047     */
048    protected final T2 b;
049    /**
050     * The third element.
051     */
052    protected final T3 c;
053
054    private final int cachedHash;
055
056    /**
057     * Constructs a CachedTriple.
058     * @param a The first element.
059     * @param b The second element.
060     * @param c The third element.
061     */
062    public CachedTriple(T1 a, T2 b, T3 c) {
063        this.a = a;
064        this.b = b;
065        this.c = c;
066        this.ab = new CachedPair<>(a,b);
067        this.ac = new CachedPair<>(a,c);
068        this.bc = new CachedPair<>(b,c);
069        this.cachedHash = calculateHashCode();
070    }
071
072    /**
073     * Gets the first element.
074     * @return The first element.
075     */
076    public T1 getA() {
077        return a;
078    }
079
080    /**
081     * Gets the second element.
082     * @return The second element.
083     */
084    public T2 getB() {
085        return b;
086    }
087
088    /**
089     * Gets the third element.
090     * @return The third element.
091     */
092    public T3 getC() {
093        return c;
094    }
095
096    /**
097     * Gets the pair of the first and second elements.
098     * @return A pair of the first and second elements.
099     */
100    public CachedPair<T1,T2> getAB() {
101        return ab;
102    }
103
104    /**
105     * Gets the pair of the first and third elements.
106     * @return A pair of the first and third elements.
107     */
108    public CachedPair<T1,T3> getAC() {
109        return ac;
110    }
111
112    /**
113     * Gets the pair of the second and third elements.
114     * @return A pair of the second and third elements.
115     */
116    public CachedPair<T2,T3> getBC() {
117        return bc;
118    }
119
120    /**
121     * Used to mix the integers in hashcode.
122     * Returns the 32 high bits of Stafford variant 4 mix64 function as int.
123     */
124    private static int mix32(long z) {
125        z *= 0x62a9d9ed799705f5L;
126        return (int)(((z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L) >>> 32);
127    }
128
129    @Override
130    public int hashCode() {
131        return cachedHash;
132    }
133    
134    /**
135     * Overridden hashcode. Checks to see if the types are ints or longs, and
136     * runs them through the mixing function if
137     * they are. Then XORs the two hashcodes together.
138     * @return A 32-bit integer.
139     */
140    public int calculateHashCode() {
141        int aCode, bCode, cCode;
142        
143        if (a instanceof Integer) {
144            aCode = mix32((Integer) a);
145        } else if (a instanceof Long) {
146            aCode = mix32((Long) a);
147        } else {
148            aCode = a.hashCode();
149        }
150
151        if (b instanceof Integer) {
152            bCode = mix32((Integer) b);
153        } else if (b instanceof Long) {
154            bCode = mix32((Long) b);
155        } else {
156            bCode = b.hashCode();
157        }
158
159        if (c instanceof Integer) {
160            cCode = mix32((Integer) c);
161        } else if (c instanceof Long) {
162            cCode = mix32((Long) c);
163        } else {
164            cCode = c.hashCode();
165        }
166        
167        return (aCode ^ bCode) ^ cCode;
168    }
169
170    @Override
171    public boolean equals(Object obj) {
172        if(obj == null) {
173            return false;
174        }
175        if(!(obj instanceof CachedTriple)) {
176            return false;
177        }
178        final CachedTriple<?,?,?> other = (CachedTriple<?,?,?>) obj;
179        if(!Objects.equals(this.a, other.a)) {
180            return false;
181        }
182        if(!Objects.equals(this.b, other.b)) {
183            return false;
184        }
185        return Objects.equals(this.c, other.c);
186    }
187
188    @Override
189    public String toString() {
190        return "Triple{" + "a=" + a + ", b=" + b + ", c=" + c + '}';
191    }
192}