/*
 * JBoss, Home of Professional Open Source.
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */

package org.teiid.core.util;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.RandomAccess;

/**
 * <P>This class provides utility functions for generating good
 * hash codes.  Hash codes generated with these methods should
 * have a reasonably good distribution when placed in a hash
 * structure such as Hashtable, HashSet, or HashMap.</P>
 *
 * <P>General usage is something like:</P>
 * <PRE>
 * public int hashCode() {
 *     int hc = 0;	// or = super.hashCode();
 *     hc = HashCodeUtil.hashCode(hc, intField);
 *     hc = HashCodeUtil.hashCode(hc, objectField);
 *     // etc, etc
 *     return hc;
 * }
 * </PRE>
 */
public final class HashCodeUtil {

	// Prime number used in improving distribution: 1,000,003
	private static final int PRIME = 1000003;
	
	public static final int hashCode(int previous, boolean x) {
		return (PRIME*previous) + (x ? 1 : 0);		
	}

	public static final int hashCode(int previous, int x) {
		return (PRIME*previous) + x;		
	}

	public static final int hashCode(int previous, long x) {
		// convert to two ints
		return (PRIME*previous) +
			   (int) (PRIME*(x >>> 32) + (x & 0xFFFFFFFF));		
	}

	public static final int hashCode(int previous, float x) {
		return hashCode(previous, (x == 0.0F) ? 0 : Float.floatToIntBits(x));
	}

	public static final int hashCode(int previous, double x) {
		// convert to long
		return hashCode(previous, (x == 0.0) ? 0L : Double.doubleToLongBits(x));
	}

	public static final int hashCode(int previous, Object... x) {
		if(x == null) {
			return PRIME*previous;
		}
		int hc = previous;
		for(int i=0; i<x.length; i++) {
			hc = (x[i] == null) ? (PRIME*hc) : (PRIME*hc) + x[i].hashCode();
		}
		return hc;
	}

	/**
	 * Compute a hash code on a large array by walking the list
	 * and combining the hash code at every exponential index:
	 * 1, 2, 4, 8, ...  This has been shown to give a good hash
	 * for good time complexity.  
	 */
	public static final int expHashCode(int previous, Object[] x) {
		if(x == null) {
			return PRIME*previous;
		}
		int hc = (PRIME*previous) + x.length;
		int index = 1;
		int xlen = x.length+1;	// switch to 1-based
		while(index < xlen) {
			hc = hashCode(hc, x[index-1]);
			index = index << 1;		// left shift by 1 to double
		}
		return hc;
	}

	/**
	 * Compute a hash code on a large collection by walking the list
	 * and combining the hash code at every exponential index:
	 * 1, 2, 4, 8, ...  This has been shown to give a good hash
	 * for good time complexity. 	 
	 */
	public static final int expHashCode(int previous, Collection<?> x) {
		if(x == null || x.size() == 0) {
			return PRIME*previous;
		}
		int size = x.size();				// size of collection
		int hc = (PRIME*previous) + size;	// hash code so far
		if (x instanceof RandomAccess && x instanceof List<?>) {
			List<?> l = List.class.cast(x);
			int index = 1;
			int xlen = x.size()+1;	// switch to 1-based
			while(index < xlen) {
				hc = hashCode(hc, l.get(index-1));
				index = index << 1;		// left shift by 1 to double
			}
		} else {
			int skip = 0;						// skip between samples
			int total = 0;						// collection examined already
			Iterator<?> iter = x.iterator();		// collection iterator
			Object obj = iter.next();			// last iterated object, primed at first
			while(total < size) {
				for(int i=0; i<skip; i++) {		// skip to next sample
					obj = iter.next();
				}
				hc = hashCode(hc, obj);			// add sample to hashcode
				skip = (skip == 0) ? 1 : skip << 1;		// left shift by 1 to double
				total += skip;					// update total
			}
		}
		return hc;
	}

	public static final int expHashCode(CharSequence x) {
		return expHashCode(x, true);
	}
	
	public static final int expHashCode(CharSequence x, boolean caseSensitive) {
		if(x == null) {
			return 0;
		}
		int hc = x.length();
		int index = 1;
		int xlen = x.length()+1;	// switch to 1-based
		while(index < xlen) {
			int charHash = x.charAt(index-1);
			if (!caseSensitive) {
				charHash = Character.toUpperCase(charHash);
			}
			hc = PRIME * hc + charHash;
			index = index << 1;		// left shift by 1 to double
		}
		return hc;
	}

}
