--LRNG: Lightweight high-quality psuedorandom numbers (1.1)
--Based on the SFC32 PRNG algorithm
--Released under CC0.
local LRNG = {}

--Returns 32 bits of random data as an integer that can be negative.
local function rng_next(state)
    local b, c, counter = state[2], state[3], state[4]
    local output = state[1] + b + counter
    state[4] = counter + 1
    state[1] = b ^^ (b >> 9)
    state[2] = c * 9
    state[3] = output + ((c << 21) | (c >> 11))
    return output
end

--Reseeds a state with up to 3 seed values in-place.
local function rng_seed(state, ...)
    local fallback_seeds = {1, 69105, 4142001}
    local seeds = {...}
    for i=1, 3 do
        state[4-i] = seeds[i] or fallback_seeds[i]
    end
    state[4] = 1
    for i=1, 20 do
        rng_next(state)
    end
end

LRNG.NextInt = rng_next
LRNG.Seed = rng_seed

-- The rest of these are optional and can be removed if you do not need them.

--Creates a new state with up to 3 seed values.
function LRNG.New(...)
    local state = {}
    rng_seed(state, ...)
    return state
end

--Returns a random integer that will be positive.
function LRNG.NextPositive(state)
    return rng_next(state) >> 1
end

--Returns a random integer between 0 and FRACUNIT.
function LRNG.NextFixed(state)
    return rng_next(state) >> 16
end

--Only guaranteed to work properly with positive keys
--Based on the JDK implementation
function LRNG.NextKey(state, limit)
    if limit < 0 then error("limit can't be negative: "..tostring(n), 2)
    elseif limit < 2 then return 0
	end

	local rand, result
	local mod = limit-1
	repeat
		rand = rng_next(state) >> 1
		result = rand % limit
	until rand - result + mod >= 0

	return result
end

function LRNG.NextRange(state, min, max)
    if max < min then error("max can't be less than min", 2)
    elseif max-min == -1 then return rng_next(state)
    end

    return min + LRNG.NextKey(state, max-min + 1)
end

--Shuffles the array part of a table using Knuth's shuffle algorithm.
--Works in place, so returns nothing.
function LRNG.Shuffle(state, array)
    for i=#array, 2, -1 do
        local rand_val = LRNG.NextKey(state, i) + 1
        array[i], array[rand_val] = array[rand_val], array[i]
    end
end

function LRNG.NextBits(state, bits)
   if bits < 1 or bits > 32 then
        error("invalid bits value ".. bits, 2)
   end

   return rng_next(state) >> (32-bits)
end

rawset(_G,"LRNG",LRNG)