package relational;

import java.util.Hashtable;
import Tools.*;

/**
 * An ``optimistic'' hash table. Used to index for SQL-style ``LIKE''
 * queries (see {@link WhereLike}). If a row satisfies a given LIKE
 * query, then both will map to sets of buckets with a non-null
 * intersection. When we go sifting through the rows in the buckets
 * that the query mapped to, we're sure to find all of the rows
 * that match the query, and then some.
 */
class LikeHash {
	SmallHashset theLists[];
	static int maxLikeIndexLength = InternalDatabase.maxLikeIndexLength;

	final int likeIndexBits;	// differentiates letters, but not case
	final int likeKeepBits;
		// There will be max 2^likeKeepBits hashtables per Like-indexed field.
	final int likeShift;
		// rules:
		// likeIndexBits*maxLikeIndexLength > likeKeepBits
		//	(don't keep more bits than we generate)
		// gcd(likeShift,likeKeepBits) = 1
		//	spread bits out over keep-word
		// likeShift>=likeIndexBits
		//	no point in overlapping generated words to early
	final int likeIndexMask;
	final int likeKeepMask;
	final int skip;
	int offsets[];

	public LikeHash(int indexBits, int keepBits, int skip) {
		likeIndexBits = indexBits;
		likeKeepBits = keepBits;
		likeShift = indexBits;
		likeIndexMask = (1<<likeIndexBits)-1;
		likeKeepMask = (1<<likeKeepBits)-1;
		this.skip = skip;
			// not used here, but stored to indicate which substring alignments
			// have been hashed into this index

		theLists=new SmallHashset[1<<likeKeepBits];

		initOffsets();
	}

	public int getSkip() { return skip; }

	public void put(String target, Relational ro) {
		int hk = getHashKey(target);
		if (theLists[hk]==null) {
			theLists[hk] = new SmallHashset();
		}
		theLists[hk].put(ro);
	}

	public SmallHashset get(String target) {
		int hk = getHashKey(target);
		if (theLists[hk]!=null) {
			return theLists[hk];
		}
		return null;
	}

	protected int getHashKey(String target) {
		// hash on the low likeIndexBits bits of the first
		// maxLikeIndexLength chars of target
		int len = Math.min(target.length(), maxLikeIndexLength);
		int hashKey = 0;
		for (int i=0; i<len; i++) {
			hashKey ^= (((int)target.charAt(i)) & likeIndexMask)
				<<offsets[i];
		}
		// the offset table arranges for each bit string to appear
		// at the right offset modulo likeIndexBits in a bit string
		// twice as long. The last step is to fold the two halves
		// together, giving the equivalent XOR operation, but saving
		// the trouble of splitting each character into a right-end
		// and a left-end.
		hashKey = ((hashKey>>>likeKeepBits) ^ hashKey) & likeKeepMask;

		return hashKey;
	}

	protected void initOffsets() {
		Tools.Assert.assert(2*likeKeepBits <= 32);
			// make sure the hashKey working buffer will have enough space.

		offsets = new int[maxLikeIndexLength];
		for (int i=0; i<maxLikeIndexLength; i++) {
			offsets[i] = 2*likeKeepBits-likeIndexBits-
				((i*likeShift)%likeKeepBits);
			// System.out.println("offsets["+i+"] = "+offsets[i]);
		}
	}
}
