// note by paradox: Okay, this is cool as fuck.

-- Valve's pseudo-random functions ported directly to Lua :)
random = {}

local MAX_RANDOM_RANGE = 0x7FFFFFFF

local NTAB = 32
local IA = 16807
local IM = MAX_RANDOM_RANGE
local IQ = 127773
local IR = 2836
local NDIV = math.floor(1+(IM-1)/NTAB)

-- fran1 -- return a random floating-point number on the interval [0,1)
--
local AM = (1.0/IM)
local EPS = 1.2e-7
local RNMX = (1.0-EPS)

local m_idum = 0
local m_iy = 0
local m_iv = {}

function random.SetSeed( iSeed )
	m_idum = ( ( iSeed < 0 ) and iSeed or -iSeed )
	m_iy = 0
end

function random.GenerateRandomNumber()
	local j = 0
	local k = 0
	
	if (m_idum <= 0 or not m_iy) then
		if ( -(m_idum) < 1 ) then
			m_idum = 1
		else
			m_idum = (-m_idum)
		end
			
		for j = NTAB + 7, 0, -1 do
			k = math.floor((m_idum)/IQ)
			m_idum = IA*(m_idum-k*IQ)-IR*k
			if (m_idum < 0) then
				m_idum = m_idum + IM
			end
			if (j < NTAB) then
				m_iv[j] = m_idum
			end
		end
		
		m_iy = m_iv[0]
	end
	k=math.floor((m_idum)/IQ)
	m_idum=IA*(m_idum-k*IQ)-IR*k
	if (m_idum < 0) then
		m_idum = m_idum + IM
	end
	j=math.floor(m_iy/NDIV)
	
	m_iy=m_iv[j]
	m_iv[j] = m_idum

	return m_iy
end

function random.RandomFloat( flLow, flHigh )
	flLow = flLow or 0
	flHigh = flHigh or 1
	-- float in [0,1)
	local fl = AM * random.GenerateRandomNumber()
	if (fl > RNMX) then 
		fl = RNMX
	end
	return (fl * ( flHigh - flLow ) ) + flLow -- float in [low,high)
end

function random.RandomFloatExp( flMinVal, flMaxVal, flExponent )
	flMinVal = flMinVal or 0
	flMaxVal = flMaxVal or 1
	-- float in [0,1)
	local fl = AM * random.GenerateRandomNumber()
	if (fl > RNMX) then
		fl = RNMX
	end
	if ( flExponent ) then
		fl = math.pow( fl, flExponent )
	end
	return (fl * ( flMaxVal - flMinVal ) ) + flMinVal -- float in [low,high)
end

function random.RandomInt( iLow, iHigh )
	local maxAcceptable
	local x = iHigh-iLow+1
	local n = 0
	if (x <= 1 || MAX_RANDOM_RANGE < x-1) then
		return iLow
	end

	-- The following maps a uniform distribution on the interval [0,MAX_RANDOM_RANGE]
	-- to a smaller, client-specified range of [0,x-1] in a way that doesn't bias
	-- the uniform distribution unfavorably. Even for a worst case x, the loop is
	-- guaranteed to be taken no more than half the time, so for that worst case x,
	-- the average number of times through the loop is 2. For cases where x is
	-- much smaller than MAX_RANDOM_RANGE, the average number of times through the
	-- loop is very close to 1.
	--
	maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE+1) % x )
	repeat
		n = random.GenerateRandomNumber()
	until (n <= maxAcceptable)

	return iLow + (n % x)
end

local m_bHaveValue = false
local m_flRandomValue = 0

function random.GaussianRandomFloat( flMean, flStdDev )
	local fac,rsq,v1,v2 = 0,0,0,0

	if (not m_bHaveValue) then
		-- Pick 2 random #s from -1 to 1
		-- Make sure they lie inside the unit circle. If they don't, try again
		repeat
			v1 = 2.0 * random.RandomFloat() - 1.0
			v2 = 2.0 * random.RandomFloat() - 1.0
			rsq = v1*v1 + v2*v2
		until ((rsq <= 1.0) and (rsq ~= 0.0))

		-- The box-muller transformation to get the two gaussian numbers
		fac = math.sqrt( -2.0 * math.log(rsq) / rsq )

		-- Store off one value for later use
		m_flRandomValue = v1 * fac
		m_bHaveValue = true

		return flStdDev * (v2 * fac) + flMean
	else
		m_bHaveValue = false
		return flStdDev * m_flRandomValue + flMean
	end
end

random.SetSeed( os.time() )

--[[for i=1,10 do
    local heads = 0
    local tails = 0
    for j=1,100 do
    	local r = random.RandomInt(1,2)
    	if r == 1 then
    		heads = heads + 1
    	else
    		tails = tails + 1
    	end
    end
    print( "Trial " .. i )
    print( "\tHeads = " .. heads )
    print( "\tTails = " .. tails )
end]]


I ported over valve's random stuff to Lua just so I could use it shared.

Example of usage.

local _R = debug.getregistry()

_R.CShotManipulator = {}
_R.CShotManipulator.__index = _R.CShotManipulator

function CShotManipulator( vecForward )
	
	local manip = setmetatable( {
		m_vecShotDirection = Vector(),
		m_vecRight = Vector(),
		m_vecUp = Vector(),
		m_vecResult = Vector(),
	}, _R.CShotManipulator )
	
	if vecForward then
		manip:SetShootDir( vecForward )
	end
	
	return manip 
end

function _R.CShotManipulator:SetShootDir( vecForward )
	self.m_vecShotDirection = vecForward
	
	local ang = vecForward:Angle()
	
	self.m_vecRight = ang:Right()
	self.m_vecUp = ang:Up()
end

local ai_shot_bias_min = GetConVar( "ai_shot_bias_min" )
ord">local ai_shot_bias_max = GetConVar( "ai_shot_bias_max" )

function _R.CShotManipulator:ApplySpread( vecSpread, bias )
	local x, y, z = 0,0,0
	
	bias = bias or 1
	
	if ( bias > 1 ) then
		bias = 1
	elseif ( bias < 0 ) then
		bias = 0
	end
	
	local shotBiasMin = ai_shot_bias_min:GetFloat()
	local shotBiasMax = ai_shot_bias_max:GetFloat()

	-- 1.0 gaussian, 0.0 is flat, -1.0 is inverse gaussian
	local shotBias = ( ( shotBiasMax - shotBiasMin ) * bias ) + shotBiasMin
	local flatness = ( math.abs(shotBias) * 0.5 )

	repeat
		x = function">random.RandomFloat(-1,1) * flatness ord operator">+ random.RandomFloat(-1,1) * (1 - flatness)
		y = random.RandomFloat(-1,1) * flatness + random.RandomFloat(-1,1) * (1 - flatness)
		if ( shotBias < 0 ) then
			x = ( x >= 0 ) and 1.0 - x or -1.0 - x
			y = ( y >= 0 ) and 1.0 - y or -1.0 - y
		end
		z = x*x+y*y
	until (z <= 1)

	self.m_vecResult ord operator">= self.m_vecShotDirection + x * vecSpread.x ord operator">* self.m_vecRight + y * vecSpread.y * self.m_vecUp
	
	return self.m_vecResult
end

function _R.CShotManipulator:GetShotDirection()
	return self.m_vecShotDirection
end

function _R.CShotManipulator:GetResult()
	return self.m_vecResult
end

function _R.CShotManipulator:GetRightVector()
	return self.m_vecRight
end

function _R.CShotManipulator:GetUpVector()
	return self.m_vecUp
end