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    protected final T1 a;
042    protected final T2 b;
043    protected final T3 c;
044
045    private final int cachedHash;
046
047    public CachedTriple(T1 a, T2 b, T3 c) {
048        this.a = a;
049        this.b = b;
050        this.c = c;
051        this.ab = new CachedPair<>(a,b);
052        this.ac = new CachedPair<>(a,c);
053        this.bc = new CachedPair<>(b,c);
054        this.cachedHash = calculateHashCode();
055    }
056
057    public T1 getA() {
058        return a;
059    }
060
061    public T2 getB() {
062        return b;
063    }
064    
065    public T3 getC() {
066        return c;
067    }
068
069    public CachedPair<T1,T2> getAB() {
070        return ab;
071    }
072
073    public CachedPair<T1,T3> getAC() {
074        return ac;
075    }
076
077    public CachedPair<T2,T3> getBC() {
078        return bc;
079    }
080
081    /**
082     * Used to mix the integers in hashcode.
083     * Returns the 32 high bits of Stafford variant 4 mix64 function as int.
084     */
085    private static int mix32(long z) {
086        z *= 0x62a9d9ed799705f5L;
087        return (int)(((z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L) >>> 32);
088    }
089
090    @Override
091    public int hashCode() {
092        return cachedHash;
093    }
094    
095    /**
096     * Overridden hashcode. Checks to see if the types are ints or longs, and
097     * runs them through the mixing function if
098     * they are. Then XORs the two hashcodes together.
099     * @return A 32-bit integer.
100     */
101    public int calculateHashCode() {
102        int aCode, bCode, cCode;
103        
104        if (a instanceof Integer) {
105            aCode = mix32((Integer) a);
106        } else if (a instanceof Long) {
107            aCode = mix32((Long) a);
108        } else {
109            aCode = a.hashCode();
110        }
111
112        if (b instanceof Integer) {
113            bCode = mix32((Integer) b);
114        } else if (b instanceof Long) {
115            bCode = mix32((Long) b);
116        } else {
117            bCode = b.hashCode();
118        }
119
120        if (c instanceof Integer) {
121            cCode = mix32((Integer) c);
122        } else if (c instanceof Long) {
123            cCode = mix32((Long) c);
124        } else {
125            cCode = c.hashCode();
126        }
127        
128        return (aCode ^ bCode) ^ cCode;
129    }
130
131    @Override
132    public boolean equals(Object obj) {
133        if(obj == null) {
134            return false;
135        }
136        if(!(obj instanceof CachedTriple)) {
137            return false;
138        }
139        final CachedTriple<?,?,?> other = (CachedTriple<?,?,?>) obj;
140        if(!Objects.equals(this.a, other.a)) {
141            return false;
142        }
143        if(!Objects.equals(this.b, other.b)) {
144            return false;
145        }
146        return Objects.equals(this.c, other.c);
147    }
148
149    @Override
150    public String toString() {
151        return "Triple{" + "a=" + a + ", b=" + b + ", c=" + c + '}';
152    }
153}