/*	Author: Wang Yi <godspeed_china@yeah.net>	*/
#ifndef wyhash_version_2
#define wyhash_version_2
#include	<stdint.h>
#include	<string.h>
#include	<math.h>
#if defined(_MSC_VER) && defined(_M_X64)
	#include <intrin.h>
	#pragma	intrinsic(_umul128)
#endif
const	uint64_t	_wyp0=0xa0761d6478bd642full,	_wyp1=0xe7037ed1a0b428dbull,	_wyp2=0x8ebc6af09c88c6e3ull,	_wyp3=0x589965cc75374cc3ull,	_wyp4=0x1d8e4e27c47d124full;
static	inline	uint64_t	_wymum(uint64_t	A,	uint64_t	B){
#ifdef __SIZEOF_INT128__
	__uint128_t	r=A;	r*=B;	return	(r>>64)^r;
#elif	defined(_MSC_VER) && defined(_M_X64)
	A=_umul128(A, B, &B);	return	A^B;
#else
	uint64_t	ha=A>>32,	hb=B>>32,	la=(uint32_t)A,	lb=(uint32_t)B,	hi, lo;
	uint64_t	rh=ha*hb,	rm0=ha*lb,	rm1=hb*la,	rl=la*lb,	t=rl+(rm0<<32),	c=t<rl;
	lo=t+(rm1<<32);	c+=lo<t;	hi=rh+(rm0>>32)+(rm1>>32)+c;
	return hi^lo;
#endif
}
static	inline	uint64_t	_wymix0(uint64_t	A,	uint64_t	B,	uint64_t	seed){	return	_wymum(A^seed^_wyp0,	B^seed^_wyp1);	}
static	inline	uint64_t	_wymix1(uint64_t	A,	uint64_t	B,	uint64_t	seed){	return	_wymum(A^seed^_wyp2,	B^seed^_wyp3);	}
static	inline	uint64_t	_wyr08(const	uint8_t	*p){	uint8_t v;	memcpy(&v,	p,	1);	return	v;	}
static	inline	uint64_t	_wyr16(const	uint8_t	*p){	uint16_t v;	memcpy(&v,	p,	2);	return	v;	}
static	inline	uint64_t	_wyr32(const	uint8_t	*p){	uint32_t v;	memcpy(&v,	p,	4);	return	v;	}
static	inline	uint64_t	_wyr64(const	uint8_t	*p){	uint64_t v;	memcpy(&v,	p,	8);	return	v;	}
static	inline	uint64_t	__wyr64(const	uint8_t	*p){	return	(_wyr32(p)<<32)|_wyr32(p+4);	}
//to avoid attacks, seed should be initialized as a secret
static	inline	uint64_t	wyhash(const void* key,	uint64_t	len, uint64_t	seed){
	const	uint8_t	*p=(const	uint8_t*)key;	uint64_t i;
	uint64_t mix = len;
	for(i=0;	i+32<=len;	i+=32,	p+=32)	seed=_wymix0(_wyr64(p),_wyr64(p+8),seed)^_wymix1(_wyr64(p+16),_wyr64(p+24),seed);
	switch(len&31){
	case 	0:	mix=  len ? mix: _wymum(_wyp0,seed); break;
	case	1:	seed=_wymix0(_wyr08(p),_wyp4,seed);	break;
	case	2:	seed=_wymix0(_wyr16(p),_wyp4,seed);	break;
	case	3:	seed=_wymix0((_wyr16(p)<<8)|_wyr08(p+2),_wyp4,seed);	break;
	case	4:	seed=_wymix0(_wyr32(p),_wyp4,seed);	break;
	case	5:	seed=_wymix0((_wyr32(p)<<8)|_wyr08(p+4),_wyp4,seed);	break;
	case	6:	seed=_wymix0((_wyr32(p)<<16)|_wyr16(p+4),_wyp4,seed);	break;
	case	7:	seed=_wymix0((_wyr32(p)<<24)|(_wyr16(p+4)<<8)|_wyr08(p+6),_wyp4,seed);	break;
	case	8:	seed=_wymix0(__wyr64(p),_wyp4,seed);	break;
	case	9:	seed=_wymix0(__wyr64(p),_wyr08(p+8),seed);	break;
	case	10:	seed=_wymix0(__wyr64(p),_wyr16(p+8),seed);	break;
	case	11:	seed=_wymix0(__wyr64(p),(_wyr16(p+8)<<8)|_wyr08(p+8+2),seed);	break;
	case	12:	seed=_wymix0(__wyr64(p),_wyr32(p+8),seed);	break;
	case	13:	seed=_wymix0(__wyr64(p),(_wyr32(p+8)<<8)|_wyr08(p+8+4),seed);	break;
	case	14:	seed=_wymix0(__wyr64(p),(_wyr32(p+8)<<16)|_wyr16(p+8+4),seed);	break;
	case	15:	seed=_wymix0(__wyr64(p),(_wyr32(p+8)<<24)|(_wyr16(p+8+4)<<8)|_wyr08(p+8+6),seed);	break;
	case	16:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed);	break;
	case	17:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(_wyr08(p+16),_wyp4,seed);	break;
	case	18:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(_wyr16(p+16),_wyp4,seed);	break;
	case	19:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1((_wyr16(p+16)<<8)|_wyr08(p+16+2),_wyp4,seed);	break;
	case	20:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(_wyr32(p+16),_wyp4,seed);	break;
	case	21:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1((_wyr32(p+16)<<8)|_wyr08(p+16+4),_wyp4,seed);	break;
	case	22:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1((_wyr32(p+16)<<16)|_wyr16(p+16+4),_wyp4,seed);	break;
	case	23:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1((_wyr32(p+16)<<24)|(_wyr16(p+16+4)<<8)|_wyr08(p+16+6),_wyp4,seed);	break;
	case	24:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(__wyr64(p+16),_wyp4,seed);	break;
	case	25:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(__wyr64(p+16),_wyr08(p+24),seed);	break;
	case	26:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(__wyr64(p+16),_wyr16(p+24),seed);	break;
	case	27:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(__wyr64(p+16),(_wyr16(p+24)<<8)|_wyr08(p+24+2),seed);	break;
	case	28:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(__wyr64(p+16),_wyr32(p+24),seed);	break;
	case	29:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(__wyr64(p+16),(_wyr32(p+24)<<8)|_wyr08(p+24+4),seed);	break;
	case	30:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(__wyr64(p+16),(_wyr32(p+24)<<16)|_wyr16(p+24+4),seed);	break;
	case	31:	seed=_wymix0(__wyr64(p),__wyr64(p+8),seed)^_wymix1(__wyr64(p+16),(_wyr32(p+24)<<24)|(_wyr16(p+24+4)<<8)|_wyr08(p+24+6),seed);	break;
	}

	return _wymum(seed^mix, _wyp4);
}
static	inline	uint64_t	wyhash64(uint64_t	A, uint64_t	B){	return	_wymum(_wymum(A^_wyp0,	B^_wyp1),	_wyp2);	}
static	inline	double	wy2u01(uint64_t	r){	const	double	_wynorm=1.0/(1ull<<52);	return	(r&0x000fffffffffffffull)*_wynorm; }
static	inline	float	wy2gau(uint64_t	r){	const	float	_wynorm1=1.0f/(1ull<<20);	return	((r&0x1fffff)+((r>>21)&0x1fffff)+(r>>43))*_wynorm1-3.0f;	}
static	inline	uint64_t	wyrand(uint64_t	*seed){	*seed+=_wyp0;	return	_wymum(*seed^_wyp1,*seed);	}
static	uint64_t	_wyrand_seed=0;
#define	WYRAND_MAX	0xffffffffffffffffull
static	inline	void	wysrand(uint64_t	seed){	_wyrand_seed=seed;	}
static	inline	uint64_t	wyrand(void){
	uint64_t s;
	#if defined(_OPENMP)
	#pragma omp atomic capture
	#endif
	{
		_wyrand_seed += _wyp0;
		s = _wyrand_seed;
	}
	return	_wymum(s^_wyp1,s);
}
#endif

