module std::math::random;

const MUL_MCG128 @local = 0x9e3779b97f4a7c15f39cc0605cedc835;
const MUL_MCG64 @local = 0xf1357aea2e62a9c5;
const MUL_MCG32 @local = 0x93d765dd;
const MUL_MCG16 @local = 0x93d5; // TODO: Find good constant

// Mcg128_64

typedef Mcg128Random (Random) = uint128;

fn void Mcg128Random.set_seed(&self, char[] seed) @dynamic
{
	*self = (Mcg128Random)(random::make_seed(uint128, seed) | 1);
}

<*
 @require bytes.len > 0
*>
fn void Mcg128Random.next_bytes(&self, char[] bytes) @dynamic
{
	@random_value_to_bytes(self.next_long, bytes);
}

fn ulong Mcg128Random.next_long(&self) @dynamic
{
	uint128* s = (uint128*)self;
	ulong result = (ulong)(*s >> 64);
	*s *= MUL_MCG128;
	return result;
}

fn uint128 Mcg128Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long());
fn uint Mcg128Random.next_int(&self) @dynamic => (uint)self.next_long();
fn ushort Mcg128Random.next_short(&self) @dynamic => (ushort)self.next_long();
fn char Mcg128Random.next_byte(&self) @dynamic => (char)self.next_long();




// Mcg64RandomState

typedef Mcg64Random (Random) = ulong;

fn void Mcg64Random.set_seed(&self, char[] seed) @dynamic
{
	*self = (Mcg64Random)random::make_seed(ulong, seed) | 1;
}

<*
 @require bytes.len > 0
*>
fn void Mcg64Random.next_bytes(&self, char[] bytes) @dynamic
{
	@random_value_to_bytes(self.next_int, bytes);
}

fn uint Mcg64Random.next_int(&self) @dynamic
{
	ulong* s = (ulong*)self;
	uint result = (uint)(*s >> 32);
	*s *= MUL_MCG64;
	return result;
}

fn uint128 Mcg64Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long());
fn ulong Mcg64Random.next_long(&self) @dynamic => @int_to_long(self.next_int());
fn ushort Mcg64Random.next_short(&self) @dynamic => (ushort)self.next_int();
fn char Mcg64Random.next_byte(&self) @dynamic => (char)self.next_int();


// Mcg32Random

typedef Mcg32Random (Random) = uint;

fn void Mcg32Random.set_seed(&self, char[] seed) @dynamic
{
	*self = (Mcg32Random)random::make_seed(uint, seed) | 1;
}

fn ushort Mcg32Random.next_short(&self) @dynamic
{
	uint* s = (uint*)self;
	ushort result = (ushort)(*s >> 16);
	*s *= MUL_MCG32;
	return result;
}

<*
 @require bytes.len > 0
*>
fn void Mcg32Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_short, bytes);
fn uint128 Mcg32Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long());
fn ulong Mcg32Random.next_long(&self) @dynamic => @int_to_long(self.next_int());
fn uint Mcg32Random.next_int(&self) @dynamic => @short_to_int(self.next_short());
fn char Mcg32Random.next_byte(&self) @dynamic => (char)self.next_short();

// -------------------------------- Mcg16RandomState --------------------------------

typedef Mcg16Random (Random) = ushort;

fn void Mcg16Random.set_seed(&self, char[] seed) @dynamic
{
	*self = (Mcg16Random)random::make_seed(ushort, seed) | 1;
}

fn char Mcg16Random.next_byte(&self) @dynamic
{
	ushort* s = (ushort*)self;
	char result = (char)(*s >> 8);
	*s *= MUL_MCG16;
	return result;
}

fn void Mcg16Random.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_byte, bytes);
fn uint128 Mcg16Random.next_int128(&self) @dynamic => @long_to_int128(self.next_long());
fn ulong Mcg16Random.next_long(&self) @dynamic => @int_to_long(self.next_int());
fn uint Mcg16Random.next_int(&self) @dynamic => @short_to_int(self.next_short());
fn ushort Mcg16Random.next_short(&self) @dynamic => @char_to_short(self.next_byte());
