raze-gles/polymer/eduke32/source/lunatic/stat.lua

104 lines
2.5 KiB
Lua
Raw Normal View History

-- Statistics module for Lunatic.
local ffi = require("ffi")
local math = require("math")
local string = require("string")
local tostring = tostring
module(...)
ffi.cdef[[
typedef struct {
double n;
// vvv internal vvv
double m, s;
double min, max;
} runningstat_t;
typedef struct {
const double n;
const double mean, var, std;
const double min, max;
} runningstat_res_t;
]]
local NaN = 0/0
local res_mt = {
__tostring = function(s)
return
string.format("N=%d; mean=%.5g, std=%.5g; min=%.5g, max=%.5g",
s.n, s.mean, s.std, s.min, s.max)
end
}
local rstatres = ffi.metatype("runningstat_res_t", res_mt)
local mt = {
__tostring = function(s)
-- XXX: with this and the other serializing of ffi types, take care of
-- NaN and Infs when reading back (e.g. by "nan=0/0" in that context)
return "stat.new("..s.n..","..s.m..","..s.s..","..s.min..","..s.max..")"
end,
-- See: Accurately computing running variance, by John D. Cook
-- http://www.johndcook.com/standard_deviation.html
__index = {
add = function(s, num)
if (s.n > 0) then
-- N>0, recurrence
s.n = s.n+1
local lastm = s.m
s.m = lastm + (num-lastm)/s.n
s.s = s.s + (num-lastm)*(num-s.m)
if (num < s.min) then s.min = num end
if (num > s.max) then s.max = num end
else
-- N==0, initialization
s.n = 1
s.m = num
s.s = 0
s.min = num
s.max = num
end
end,
getstats = function(s)
local var = s.n > 1 and s.s/(s.n-1) or NaN
return rstatres(s.n, s.m, var, math.sqrt(var), s.min, s.max)
end,
getstatstr = function(s)
return tostring(s:getstats())
end,
reset = function(s)
s.n = 0
s.m, s.s, s.min, s.max = NaN, NaN, NaN, NaN
end,
},
}
local rstat = ffi.metatype("runningstat_t", mt)
function new(n,m,s, min,max)
if (n == nil) then
-- initialization containing no elements
return rstat(0, NaN, NaN, NaN, NaN)
elseif (m == nil) then
-- same as initialization with N==0 above (one element)
return rstat(1, n, 0, n, n)
else
-- generic initialization (internal use only)
return rstat(n,m,s, min,max)
end
end