class SpookyHash64 {

	public static long hash(byte[] src, int start, int length, long seed) {
		if (length < SMALLHASH_LIMIT_BYTES) {
			return smallHash(src, start, length, seed);
		}
		
		long h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11;
		h0 = h3 = h6 = h9 = seed;
		h1 = h4 = h7 = h10 = seed;
		h2 = h5 = h8 = h11 = ARBITRARY_BITS;
		
		int remaining = length;
		int pos = start;
		while (remaining >= BLOCK_SIZE_BYTES) {		
			h0 += packLong(src, pos); 
			h2 ^= h10;     
			h11 ^= h0;   
			h0 = (h0 << 11) | (h0 >>> 53);     
			h11 += h1;
			
			h1 += packLong(src, pos+8); 
			h3 ^= h11;     
			h0 ^= h1;     
			h1 = (h1 << 32) | (h1 >>> 32);     
			h0 += h2;

			h2 += packLong(src, pos+16); 
			h4 ^= h0;      
			h1 ^= h2;     
			h2 = (h2 << 43) | (h2 >>> 21);     
			h1 += h3;
		
			h3 += packLong(src, pos+24); 
			h5 ^= h1;      
			h2 ^= h3;     
			h3 = (h3 << 31) | (h3 >>> 33);     
			h2 += h4;

			h4 += packLong(src, pos+32); 
			h6 ^= h2;      
			h3 ^= h4;     
			h4 = (h4 << 17) | (h4 >>> 47);     
			h3 += h5;

			h5 += packLong(src, pos+40); 
			h7 ^= h3;      
			h4 ^= h5;     
			h5 = (h5 << 28) | (h5 >>> 36);     
			h4 += h6;

			h6 += packLong(src, pos+48); 
			h8 ^= h4;      
			h5 ^= h6;     
			h6 = (h6 << 39) | (h6 >>> 25);     
			h5 += h7;

			h7 += packLong(src, pos+56); 
			h9 ^= h5;      
			h6 ^= h7;     
			h7 = (h7 << 57) | (h7 >>> 7);     
			h6 += h8;

			h8 += packLong(src, pos+64); 
			h10 ^= h6;     
			h7 ^= h8;     
			h8 = (h8 << 55) | (h8 >>> 9);     
			h7 += h9;

			h9 += packLong(src, pos+72); 
			h11 ^= h7;     
			h8 ^= h9;     
			h9 = (h9 << 54) | (h9 >>> 10);     
			h8 += h10;

			h10 += packLong(src, pos+80); 
			h0 ^= h8;      
			h9 ^= h10;    
			h10 = (h10 << 22) | (h10 >>> 42);  
			h9 += h11;

			h11 += packLong(src, pos+88); 
			h1 ^= h9;      
			h10 ^= h11;   
			h11 = (h11 << 46) | (h11 >>> 18);  
			h10 += h0;
			
			remaining -= BLOCK_SIZE_BYTES;
			pos += BLOCK_SIZE_BYTES;
		}
					
		// assert remaining < BLOCK_SIZE_BYTES;
		int partialSize = remaining & 7;
		int wholeWords = remaining >>> 3;
		if (partialSize > 0) {
			long partial = packPartial(src, pos + (wholeWords << 3), partialSize);
			switch (wholeWords) {
			case 0:
				h0 += partial;
				break;
			case 1:
				h1 += partial;
				break;
			case 2:
				h2 += partial;
				break;
			case 3:
				h3 += partial;
				break;
			case 4:
				h4 += partial;
				break;
			case 5:
				h5 += partial;
				break;
			case 6:
				h6 += partial;
				break;
			case 7:
				h7 += partial;
				break;
			case 8:
				h8 += partial;
				break;
			case 9:
				h9 += partial;
				break;
			case 10:
				h10 += partial;
				break;
			case 11:
				h11 += partial;
				break;
			}
		}
		switch (wholeWords) { // fall-through is intentional
		case 11:
			h10 += packLong(src, pos+80);
		case 10:
			h9 += packLong(src, pos+72);
		case 9:
			h8 += packLong(src, pos+64);
		case 8:
			h7 += packLong(src, pos+56);
		case 7:
			h6 += packLong(src, pos+48);
		case 6:
			h5 += packLong(src, pos+40);
		case 5:
			h4 += packLong(src, pos+32);
		case 4:
			h3 += packLong(src, pos+24);
		case 3:
			h2 += packLong(src, pos+16);
		case 2:
			h1 += packLong(src, pos+8);
		case 1:
			h0 += packLong(src, pos);
		default:
			break;
		}

		h11 += ((long)remaining) << 56;

		for (int i = 0; i < 3; i++) {
	        h11 += h1;   
	        h2 ^= h11;   
	        h1 = (h1 << 44)  | (h1 >>> 20);
	        h0 += h2;    
	        h3 ^= h0;    
	        h2 = (h2 << 15)  | (h2 >>> 49);
	        h1 += h3;    
	        h4 ^= h1;    
	        h3 = (h3 << 34)  | (h3 >>> 30);
	        h2 += h4;    
	        h5 ^= h2;    
	        h4 = (h4 << 21)  | (h4 >>> 43);
	        h3 += h5;    
	        h6 ^= h3;    
	        h5 = (h5 << 38)  | (h5 >>> 26);
	        h4 += h6;    
	        h7 ^= h4;    
	        h6 = (h6 << 33)  | (h6 >>> 31);
	        h5 += h7;    
	        h8 ^= h5;    
	        h7 = (h7 << 10)  | (h7 >>> 54);
	        h6 += h8;    
	        h9 ^= h6;    
	        h8 = (h8 << 13)  | (h8 >>> 51);
	        h7 += h9;    
	        h10 ^= h7;    
	        h9 = (h9 << 38)  | (h9 >>> 26);
	        h8 += h10;   
	        h11 ^= h8;    
	        h10 = (h10 << 53) | (h10 >>> 11);
	        h9 += h11;   
	        h0 ^= h9;    
	        h11 = (h11 << 42) | (h11 >>> 22);
	        h10 += h0;   
	        h1 ^= h10;   
	        h0 = (h0 << 54)  | (h0 >>> 10);
		}
		return h0;
	}

	public  SpookyHash64();
	public  SpookyHash64(long seed);

}