Mercurial > repos > adam-novak > hexagram
diff hexagram-6ae12361157c/hexagram/jstat-1.0.0.js @ 0:1407e3634bcf draft default tip
Uploaded r11 from test tool shed.
author | adam-novak |
---|---|
date | Tue, 22 Oct 2013 14:17:59 -0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hexagram-6ae12361157c/hexagram/jstat-1.0.0.js Tue Oct 22 14:17:59 2013 -0400 @@ -0,0 +1,2576 @@ +function jstat(){} +j = jstat; +/* Simple JavaScript Inheritance + * By John Resig http://ejohn.org/ + * MIT Licensed. + */ +// Inspired by base2 and Prototype +(function(){ + var initializing = false, fnTest = /xyz/.test(function(){ + xyz; + }) ? /\b_super\b/ : /.*/; + // The base Class implementation (does nothing) + this.Class = function(){}; + + // Create a new Class that inherits from this class + Class.extend = function(prop) { + var _super = this.prototype; + + // Instantiate a base class (but only create the instance, + // don't run the init constructor) + initializing = true; + var prototype = new this(); + initializing = false; + + // Copy the properties over onto the new prototype + for (var name in prop) { + // Check if we're overwriting an existing function + prototype[name] = typeof prop[name] == "function" && + typeof _super[name] == "function" && fnTest.test(prop[name]) ? + (function(name, fn){ + return function() { + var tmp = this._super; + + // Add a new ._super() method that is the same method + // but on the super-class + this._super = _super[name]; + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; + + return ret; + }; + })(name, prop[name]) : + prop[name]; + } + + // The dummy class constructor + function Class() { + // All construction is actually done in the init method + if ( !initializing && this.init ) + this.init.apply(this, arguments); + } + + // Populate our constructed prototype object + Class.prototype = prototype; + + // Enforce the constructor to be what we expect + Class.constructor = Class; + + // And make this class extendable + Class.extend = arguments.callee; + + return Class; + }; +})(); + +/******************************************************************************/ +/* Constants */ +/******************************************************************************/ +jstat.ONE_SQRT_2PI = 0.3989422804014327; +jstat.LN_SQRT_2PI = 0.9189385332046727417803297; +jstat.LN_SQRT_PId2 = 0.225791352644727432363097614947; +jstat.DBL_MIN = 2.22507e-308; +jstat.DBL_EPSILON = 2.220446049250313e-16; +jstat.SQRT_32 = 5.656854249492380195206754896838; +jstat.TWO_PI = 6.283185307179586; +jstat.DBL_MIN_EXP = -999; +jstat.SQRT_2dPI = 0.79788456080287; +jstat.LN_SQRT_PI = 0.5723649429247; +/******************************************************************************/ +/* jstat Functions */ +/******************************************************************************/ +jstat.seq = function(min, max, length) { + var r = new Range(min, max, length); + return r.getPoints(); +} + +jstat.dnorm = function(x, mean, sd, log) { + if(mean == null) mean = 0; + if(sd == null) sd = 1; + if(log == null) log = false; + var n = new NormalDistribution(mean, sd); + if(!isNaN(x)) { + // is a number + return n._pdf(x, log); + } else if(x.length) { + var res = []; + for(var i = 0; i < x.length; i++) { + res.push(n._pdf(x[i], log)); + } + return res; + } else { + throw "Illegal argument: x"; + } +} + +jstat.pnorm = function(q, mean, sd, lower_tail, log) { + if(mean == null) mean = 0; + if(sd == null) sd = 1; + if(lower_tail == null) lower_tail = true; + if(log == null) log = false; + + var n = new NormalDistribution(mean, sd); + if(!isNaN(q)) { + // is a number + return n._cdf(q, lower_tail, log); + } else if(q.length) { + var res = []; + for(var i = 0; i < q.length; i++) { + res.push(n._cdf(q[i], lower_tail, log)); + } + return res; + } else { + throw "Illegal argument: x"; + } +} + +jstat.dlnorm = function(x, meanlog, sdlog, log) { + if(meanlog == null) meanlog = 0; + if(sdlog == null) sdlog = 1; + if(log == null) log = false; + var n = new LogNormalDistribution(meanlog, sdlog); + if(!isNaN(x)) { + // is a number + return n._pdf(x, log); + } else if(x.length) { + var res = []; + for(var i = 0; i < x.length; i++) { + res.push(n._pdf(x[i], log)); + } + return res; + } else { + throw "Illegal argument: x"; + } +} + +jstat.plnorm = function(q, meanlog, sdlog, lower_tail, log) { + if(meanlog == null) meanlog = 0; + if(sdlog == null) sdlog = 1; + if(lower_tail == null) lower_tail = true; + if(log == null) log = false; + + var n = new LogNormalDistribution(meanlog, sdlog); + if(!isNaN(q)) { + // is a number + return n._cdf(q, lower_tail, log); + } + else if(q.length) { + var res = []; + for(var i = 0; i < q.length; i++) { + res.push(n._cdf(q[i], lower_tail, log)); + } + return res; + } else { + throw "Illegal argument: x"; + } +} + +jstat.dbeta = function(x, alpha, beta, ncp, log) { + if(ncp == null) ncp = 0; + if(log == null) log = false; + var b = new BetaDistribution(alpha, beta); + if(!isNaN(x)) { + // is a number + return b._pdf(x, log); + } + else if(x.length) { + var res = []; + for(var i = 0; i < x.length; i++) { + res.push(b._pdf(x[i], log)); + } + return res; + } else { + throw "Illegal argument: x"; + } +} + +jstat.pbeta = function(q, alpha, beta, ncp, lower_tail, log) { + if(ncp == null) ncp = 0; + if(log == null) log = false; + if(lower_tail == null) lower_tail = true; + + var b = new BetaDistribution(alpha, beta); + if(!isNaN(q)) { + // is a number + return b._cdf(q, lower_tail, log); + } else if(q.length) { + var res = []; + for(var i = 0; i < q.length; i++) { + res.push(b._cdf(q[i], lower_tail, log)); + } + return res; + } + else { + throw "Illegal argument: x"; + } +} + +jstat.dgamma = function(x, shape, rate, scale, log) { + if(rate == null) rate = 1; + if(scale == null) scale = 1/rate; + if(log == null) log = false; + + var g = new GammaDistribution(shape, scale); + if(!isNaN(x)) { + // is a number + return g._pdf(x, log); + } else if(x.length) { + var res = []; + for(var i = 0; i < x.length; i++) { + res.push(g._pdf(x[i], log)); + } + return res; + } else { + throw "Illegal argument: x"; + } +} + +jstat.pgamma = function(q, shape, rate, scale, lower_tail, log) { + if(rate == null) rate = 1; + if(scale == null) scale = 1/rate; + if(lower_tail == null) lower_tail = true; + if(log == null) log = false; + + var g = new GammaDistribution(shape, scale); + if(!isNaN(q)) { + // is a number + return g._cdf(q, lower_tail, log); + } else if(q.length) { + var res = []; + for(var i = 0; i < q.length; i++) { + res.push(g._cdf(q[i], lower_tail, log)); + } + return res; + } else { + throw "Illegal argument: x"; + } + +} + +jstat.dt = function(x, df, ncp, log) { + if(log == null) log = false; + + var t = new StudentTDistribution(df, ncp); + if(!isNaN(x)) { + // is a number + return t._pdf(x, log); + } else if(x.length) { + var res = []; + for(var i = 0; i < x.length; i++) { + res.push(t._pdf(x[i], log)); + } + return res; + } else { + throw "Illegal argument: x"; + } + +} + +jstat.pt = function(q, df, ncp, lower_tail, log) { + if(lower_tail == null) lower_tail = true; + if(log == null) log = false; + + var t = new StudentTDistribution(df, ncp); + if(!isNaN(q)) { + // is a number + return t._cdf(q, lower_tail, log); + } else if(q.length) { + var res = []; + for(var i = 0; i < q.length; i++) { + res.push(t._cdf(q[i], lower_tail, log)); + } + return res; + } else { + throw "Illegal argument: x"; + } + +} + +jstat.plot = function(x, y, options) { + if(x == null) { + throw "x is undefined in jstat.plot"; + } + if(y == null) { + throw "y is undefined in jstat.plot"; + } + if(x.length != y.length) { + throw "x and y lengths differ in jstat.plot"; + } + + var flotOpt = { + series: { + lines: { + + }, + points: { + + } + } + }; + + // combine x & y + var series = []; + if(x.length == undefined) { + // single point + series.push([x, y]); + flotOpt.series.points.show = true; + } else { + // array + for(var i = 0; i < x.length; i++) { + series.push([x[i], y[i]]); + } + } + + var title = 'jstat graph'; + + // configure Flot options + if(options != null) { + // options = JSON.parse(String(options)); + if(options.type != null) { + if(options.type == 'l') { + flotOpt.series.lines.show = true; + } else if (options.type == 'p') { + flotOpt.series.lines.show = false; + flotOpt.series.points.show = true; + } + } + if(options.hover != null) { + flotOpt.grid = { + hoverable: options.hover + } + } + + if(options.main != null) { + title = options.main; + } + } + var now = new Date(); + var hash = now.getMilliseconds() * now.getMinutes() + now.getSeconds(); + $('body').append('<div title="' + title + '" style="display: none;" id="'+ hash +'"><div id="graph-' + hash + '" style="width:95%; height: 95%"></div></div>'); + + $('#' + hash).dialog({ + modal: false, + width: 475, + height: 475, + resizable: true, + resize: function() { + $.plot($('#graph-' + hash), [series], flotOpt); + }, + open: function(event, ui) { + var id = '#graph-' + hash; + $.plot($('#graph-' + hash), [series], flotOpt); + } + }) +} + +/******************************************************************************/ +/* Special Functions */ +/******************************************************************************/ + +jstat.log10 = function(arg) { + return Math.log(arg) / Math.LN10; +} + +/* + * + */ +jstat.toSigFig = function(num, n) { + if(num == 0) { + return 0; + } + var d = Math.ceil(jstat.log10(num < 0 ? -num: num)); + var power = n - parseInt(d); + var magnitude = Math.pow(10,power); + var shifted = Math.round(num*magnitude); + return shifted/magnitude; +} + +jstat.trunc = function(x) { + return (x > 0) ? Math.floor(x) : Math.ceil(x); +} + +/** + * Tests whether x is a finite number + */ +jstat.isFinite = function(x) { + return (!isNaN(x) && (x != Number.POSITIVE_INFINITY) && (x != Number.NEGATIVE_INFINITY)); +} + +/** + * dopois_raw() computes the Poisson probability lb^x exp(-lb) / x!. + * This does not check that x is an integer, since dgamma() may + * call this with a fractional x argument. Any necessary argument + * checks should be done in the calling function. + */ +jstat.dopois_raw = function(x, lambda, give_log) { + /* x >= 0 ; integer for dpois(), but not e.g. for pgamma()! + lambda >= 0 + */ + if (lambda == 0) { + if(x == 0) { + return(give_log) ? 0.0 : 1.0; //R_D__1 + } + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0 + } + if (!jstat.isFinite(lambda)) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0; + if (x < 0) return(give_log) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0 + if (x <= lambda * jstat.DBL_MIN) { + return (give_log) ? -lambda : Math.exp(-lambda); // R_D_exp(-lambda) + } + if (lambda < x * jstat.DBL_MIN) { + var param = -lambda + x*Math.log(lambda) -jstat.lgamma(x+1); + return (give_log) ? param : Math.exp(param); // R_D_exp(-lambda + x*log(lambda) -lgammafn(x+1)) + } + var param1 = jstat.TWO_PI * x; // f + var param2 = -jstat.stirlerr(x)-jstat.bd0(x,lambda); // x + return (give_log) ? -0.5*Math.log(param1)+param2 : Math.exp(param2)/Math.sqrt(param1); // R_D_fexp(M_2PI*x, -stirlerr(x)-bd0(x,lambda)) +//return(R_D_fexp( , -stirlerr(x)-bd0(x,lambda) )); +} + +/** Evaluates the "deviance part" + * bd0(x,M) := M * D0(x/M) = M*[ x/M * log(x/M) + 1 - (x/M) ] = + * = x * log(x/M) + M - x + * where M = E[X] = n*p (or = lambda), for x, M > 0 + * + * in a manner that should be stable (with small relative error) + * for all x and M=np. In particular for x/np close to 1, direct + * evaluation fails, and evaluation is based on the Taylor series + * of log((1+v)/(1-v)) with v = (x-np)/(x+np). + */ +jstat.bd0 = function(x, np) { + var ej, s, s1, v, j; + if(!jstat.isFinite(x) || !jstat.isFinite(np) || np == 0.0) throw "illegal parameter in jstat.bd0"; + + if(Math.abs(x-np) > 0.1*(x+np)) { + v = (x-np)/(x+np); + s = (x-np)*v;/* s using v -- change by MM */ + ej = 2*x*v; + v = v*v; + for (j=1; ; j++) { /* Taylor series */ + ej *= v; + s1 = s+ej/((j<<1)+1); + if (s1==s) /* last term was effectively 0 */ + return(s1); + s = s1; + } + } + /* else: | x - np | is not too small */ + return(x*Math.log(x/np)+np-x); +} + +/** Computes the log of the error term in Stirling's formula. + * For n > 15, uses the series 1/12n - 1/360n^3 + ... + * For n <=15, integers or half-integers, uses stored values. + * For other n < 15, uses lgamma directly (don't use this to + * write lgamma!) + */ +jstat.stirlerr= function(n) { + var S0 = 0.083333333333333333333; + var S1 = 0.00277777777777777777778; + var S2 = 0.00079365079365079365079365; + var S3 = 0.000595238095238095238095238; + var S4 = 0.0008417508417508417508417508; + + var sferr_halves = [ + 0.0, /* n=0 - wrong, place holder only */ + 0.1534264097200273452913848, /* 0.5 */ + 0.0810614667953272582196702, /* 1.0 */ + 0.0548141210519176538961390, /* 1.5 */ + 0.0413406959554092940938221, /* 2.0 */ + 0.03316287351993628748511048, /* 2.5 */ + 0.02767792568499833914878929, /* 3.0 */ + 0.02374616365629749597132920, /* 3.5 */ + 0.02079067210376509311152277, /* 4.0 */ + 0.01848845053267318523077934, /* 4.5 */ + 0.01664469118982119216319487, /* 5.0 */ + 0.01513497322191737887351255, /* 5.5 */ + 0.01387612882307074799874573, /* 6.0 */ + 0.01281046524292022692424986, /* 6.5 */ + 0.01189670994589177009505572, /* 7.0 */ + 0.01110455975820691732662991, /* 7.5 */ + 0.010411265261972096497478567, /* 8.0 */ + 0.009799416126158803298389475, /* 8.5 */ + 0.009255462182712732917728637, /* 9.0 */ + 0.008768700134139385462952823, /* 9.5 */ + 0.008330563433362871256469318, /* 10.0 */ + 0.007934114564314020547248100, /* 10.5 */ + 0.007573675487951840794972024, /* 11.0 */ + 0.007244554301320383179543912, /* 11.5 */ + 0.006942840107209529865664152, /* 12.0 */ + 0.006665247032707682442354394, /* 12.5 */ + 0.006408994188004207068439631, /* 13.0 */ + 0.006171712263039457647532867, /* 13.5 */ + 0.005951370112758847735624416, /* 14.0 */ + 0.005746216513010115682023589, /* 14.5 */ + 0.005554733551962801371038690 /* 15.0 */ + ]; + + var nn; + + if (n <= 15.0) { + nn = n + n; + if (nn == parseInt(nn)) return(sferr_halves[parseInt(nn)]); + return(jstat.lgamma(n + 1.0) - (n + 0.5)*Math.log(n) + n - jstat.LN_SQRT_2PI); + } + + nn = n*n; + if (n>500) return((S0-S1/nn)/n); + if (n> 80) return((S0-(S1-S2/nn)/nn)/n); + if (n> 35) return((S0-(S1-(S2-S3/nn)/nn)/nn)/n); + /* 15 < n <= 35 : */ + return((S0-(S1-(S2-(S3-S4/nn)/nn)/nn)/nn)/n); +} + + + +/** The function lgamma computes log|gamma(x)|. The function + * lgammafn_sign in addition assigns the sign of the gamma function + * to the address in the second argument if this is not null. + */ +jstat.lgamma = function(x) { + function lgammafn_sign(x, sgn) { + var ans, y, sinpiy; + var xmax = 2.5327372760800758e+305; + var dxrel = 1.490116119384765696e-8; + + // if (xmax == 0) {/* initialize machine dependent constants _ONCE_ */ + // xmax = jstat.DBL_MAX/Math.log(jstat.DBL_MAX);/* = 2.533 e305 for IEEE double */ + // dxrel = Math.sqrt(jstat.DBL_EPSILON);/* sqrt(Eps) ~ 1.49 e-8 for IEEE double */ + // } + + /* For IEEE double precision DBL_EPSILON = 2^-52 = 2.220446049250313e-16 : + xmax = DBL_MAX / log(DBL_MAX) = 2^1024 / (1024 * log(2)) = 2^1014 / log(2) + dxrel = sqrt(DBL_EPSILON) = 2^-26 = 5^26 * 1e-26 (is *exact* below !) + */ + + if (sgn != null) sgn = 1; + + if(isNaN(x)) return x; + + if (x < 0 && (Math.floor(-x) % 2.0) == 0) + if (sgn != null) sgn = -1; + + if (x <= 0 && x == jstat.trunc(x)) { /* Negative integer argument */ + console.warn("Negative integer argument in lgammafn_sign"); + return Number.POSITIVE_INFINITY;/* +Inf, since lgamma(x) = log|gamma(x)| */ + } + + y = Math.abs(x); + + if(y <= 10) return Math.log(Math.abs(jstat.gamma(x))); // TODO: implement jstat.gamma + + if(y > xmax) { + console.warn("Illegal arguement passed to lgammafn_sign"); + return Number.POSITIVE_INFINITY; + } + + if(x > 0) { + if(x > 1e17) { + return (x*(Math.log(x)-1.0)); + } else if(x > 4934720.0) { + return (jstat.LN_SQRT_2PI + (x-0.5) * Math.log(x) - x); + } else { + return jstat.LN_SQRT_2PI + (x-0.5) * Math.log(x) - x + jstat.lgammacor(x); // TODO: implement lgammacor + } + } + + sinpiy = Math.abs(Math.sin(Math.PI * y)); + + if(sinpiy == 0) { + throw "Should never happen!!"; + } + + ans = jstat.LN_SQRT_PId2 + (x - 0.5) * Math.log(y) - x - Math.log(sinpiy) - jstat.lgammacor(y); + + if(Math.abs((x-jstat.trunc(x-0.5))* ans / x) < dxrel) { + throw "The answer is less than half the precision argument too close to a negative integer"; + } + return ans; + } + + return lgammafn_sign(x, null); +} + +jstat.gamma = function(x) { + var xbig = 171.624; + var p = [ + -1.71618513886549492533811, + 24.7656508055759199108314,-379.804256470945635097577, + 629.331155312818442661052,866.966202790413211295064, + -31451.2729688483675254357,-36144.4134186911729807069, + 66456.1438202405440627855 + ]; + var q = [ + -30.8402300119738975254353, + 315.350626979604161529144,-1015.15636749021914166146, + -3107.77167157231109440444,22538.1184209801510330112, + 4755.84627752788110767815,-134659.959864969306392456, + -115132.259675553483497211 + ]; + var c = [ + -.001910444077728,8.4171387781295e-4, + -5.952379913043012e-4,7.93650793500350248e-4, + -.002777777777777681622553,.08333333333333333331554247, + .0057083835261 + ]; + + var i,n,parity,fact,xden,xnum,y,z,yi,res,sum,ysq; + + parity = (0); + fact = 1.0; + n = 0; + y=x; + if(y <= 0.0) { + /* ------------------------------------------------------------- + Argument is negative + ------------------------------------------------------------- */ + y = -x; + yi = jstat.trunc(y); + res = y - yi; + if (res != 0.0) { + if (yi != jstat.trunc(yi * 0.5) * 2.0) + parity = (1); + fact = -Math.PI / Math.sin(Math.PI * res); + y += 1.0; + } else { + return(Number.POSITIVE_INFINITY); + } + } + /* ----------------------------------------------------------------- + Argument is positive + -----------------------------------------------------------------*/ + if (y < jstat.DBL_EPSILON) { + /* -------------------------------------------------------------- + Argument < EPS + -------------------------------------------------------------- */ + if (y >= jstat.DBL_MIN) { + res = 1.0 / y; + } else { + return(Number.POSITIVE_INFINITY); + } + } else if (y < 12.0) { + yi = y; + if (y < 1.0) { + /* --------------------------------------------------------- + EPS < argument < 1 + --------------------------------------------------------- */ + z = y; + y += 1.0; + } else { + /* ----------------------------------------------------------- + 1 <= argument < 12, reduce argument if necessary + ----------------------------------------------------------- */ + n = parseInt(y) - 1; + y -= parseFloat(n); + z = y - 1.0; + } + /* --------------------------------------------------------- + Evaluate approximation for 1. < argument < 2. + ---------------------------------------------------------*/ + xnum = 0.0; + xden = 1.0; + for (i = 0; i < 8; ++i) { + xnum = (xnum + p[i]) * z; + xden = xden * z + q[i]; + } + res = xnum / xden + 1.0; + if (yi < y) { + /* -------------------------------------------------------- + Adjust result for case 0. < argument < 1. + -------------------------------------------------------- */ + res /= yi; + } else if (yi > y) { + /* ---------------------------------------------------------- + Adjust result for case 2. < argument < 12. + ---------------------------------------------------------- */ + for (i = 0; i < n; ++i) { + res *= y; + y += 1.0; + } + } + } else { + /* ------------------------------------------------------------- + Evaluate for argument >= 12., + ------------------------------------------------------------- */ + if (y <= xbig) { + ysq = y * y; + sum = c[6]; + for (i = 0; i < 6; ++i) { + sum = sum / ysq + c[i]; + } + sum = sum / y - y + jstat.LN_SQRT_2PI; + sum += (y - 0.5) * Math.log(y); + res = Math.exp(sum); + } else { + return(Number.POSITIVE_INFINITY); + } + } + /* ---------------------------------------------------------------------- + Final adjustments and return + ----------------------------------------------------------------------*/ + if (parity) + res = -res; + if (fact != 1.0) + res = fact / res; + return res; +} + +/** Compute the log gamma correction factor for x >= 10 so that + * + * log(gamma(x)) = .5*log(2*pi) + (x-.5)*log(x) -x + lgammacor(x) + * + * [ lgammacor(x) is called Del(x) in other contexts (e.g. dcdflib)] + */ +jstat.lgammacor = function(x) { + var algmcs = [ + +.1666389480451863247205729650822e+0, + -.1384948176067563840732986059135e-4, + +.9810825646924729426157171547487e-8, + -.1809129475572494194263306266719e-10, + +.6221098041892605227126015543416e-13, + -.3399615005417721944303330599666e-15, + +.2683181998482698748957538846666e-17, + -.2868042435334643284144622399999e-19, + +.3962837061046434803679306666666e-21, + -.6831888753985766870111999999999e-23, + +.1429227355942498147573333333333e-24, + -.3547598158101070547199999999999e-26, + +.1025680058010470912000000000000e-27, + -.3401102254316748799999999999999e-29, + +.1276642195630062933333333333333e-30 + ]; + + var tmp; + var nalgm = 5; + var xbig = 94906265.62425156; + var xmax = 3.745194030963158e306; + + if(x < 10) { + return Number.NaN; + } else if (x >= xmax) { + throw "Underflow error in lgammacor"; + } else if (x < xbig) { + tmp = 10 / x; + return jstat.chebyshev(tmp*tmp*2-1,algmcs,nalgm) / x; + } + return 1 / (x*12); +} + +/* + * Incomplete Beta function + */ +jstat.incompleteBeta = function(a, b, x) { + /* + * Used by incompleteBeta: Evaluates continued fraction for incomplete + * beta function by modified Lentz's method. + */ + function betacf(a, b, x) { + var MAXIT = 100; + var EPS = 3.0e-12; + var FPMIN = 1.0e-30; + + var m,m2,aa,c,d,del,h,qab,qam,qap; + + qab=a+b; + qap=a+1.0; + qam=a-1.0; + c=1.0; + d=1.0-qab*x/qap; + + if(Math.abs(d) < FPMIN) { + d=FPMIN; + } + + d = 1.0/d; + h=d; + for(m = 1; m <= MAXIT; m++) { + m2=2*m; + aa=m*(b-m)*x/((qam+m2)*(a+m2)); + d=1.0+aa*d; + if(Math.abs(d) < FPMIN) { + d = FPMIN; + } + c=1.0+aa/c; + if(Math.abs(c) < FPMIN) { + c = FPMIN; + } + d=1.0/d; + h *= d*c; + aa = -(a+m)*(qab+m)*x/((a+m2) * (qap+m2)); + d=1.0+aa*d; + if(Math.abs(d) < FPMIN) { + d = FPMIN; + } + c=1.0+aa/c; + if(Math.abs(c) < FPMIN) { + c=FPMIN; + } + d=1.0/d; + del=d*c; + h *= del; + if(Math.abs(del-1.0) < EPS) { + // are we done? + break; + } + } + if(m > MAXIT) { + console.warn("a or b too big, or MAXIT too small in betacf: " + a + ", " + b + ", " + x + ", " + h); + return h; + } + if(isNaN(h)) { + console.warn(a + ", " + b + ", " + x); + } + return h; + } + + var bt; + + if(x < 0.0 || x > 1.0) { + throw "bad x in routine incompleteBeta"; + } + if(x == 0.0 || x == 1.0) { + bt = 0.0; + } else { + bt = Math.exp(jstat.lgamma(a+b) - jstat.lgamma(a) - jstat.lgamma(b) + a * Math.log(x)+ b * Math.log(1.0-x)); + } + if(x < (a + 1.0)/(a+b+2.0)) { + return bt * betacf(a,b,x)/a; + } else { + return 1.0-bt*betacf(b,a,1.0-x)/b; + } +} + +/** Evaluates the n-term Chebyshev series + * "a" at "x". + */ +jstat.chebyshev = function(x, a, n) { + var b0, b1, b2, twox; + var i; + + if (n < 1 || n > 1000) return Number.NaN; + + if (x < -1.1 || x > 1.1) return Number.NaN; + + twox = x * 2; + b2 = b1 = 0; + b0 = 0; + for (i = 1; i <= n; i++) { + b2 = b1; + b1 = b0; + b0 = twox * b1 - b2 + a[n - i]; + } + return (b0 - b2) * 0.5; +} + +jstat.fmin2 = function(x, y) { + return (x < y) ? x : y; +} + +jstat.log1p = function(x) { + // http://kevin.vanzonneveld.net + // + original by: Brett Zamir (http://brett-zamir.me) + // % note 1: Precision 'n' can be adjusted as desired + // * example 1: log1p(1e-15); + // * returns 1: 9.999999999999995e-16 + + var ret = 0, + n = 50; // degree of precision + if (x <= -1) { + return Number.NEGATIVE_INFINITY; // JavaScript style would be to return Number.NEGATIVE_INFINITY + } + if (x < 0 || x > 1) { + return Math.log(1 + x); + } + for (var i = 1; i < n; i++) { + if ((i % 2) === 0) { + ret -= Math.pow(x, i) / i; + } else { + ret += Math.pow(x, i) / i; + } + } + return ret; +} + +jstat.expm1 = function(x) { + var y, a = Math.abs(x); + if(a < jstat.DBL_EPSILON) return x; + if(a > 0.697) return Math.exp(x) - 1; /* negligable cancellation */ + if(a > 1e-8) { + y = Math.exp(x) - 1; + } else { + y = (x / 2 + 1) * x; + } + + /* Newton step for solving log(1 + y) = x for y : */ + /* WARNING: does not work for y ~ -1: bug in 1.5.0 */ + y -= (1 + y) * (jstat.log1p(y) - x); + return y; +} + +jstat.logBeta = function(a, b) { + var corr, p, q; + p = q = a; + if(b < p) p = b;/* := min(a,b) */ + if(b > q) q = b;/* := max(a,b) */ + + /* both arguments must be >= 0 */ + if (p < 0) { + console.warn('Both arguements must be >= 0'); + return Number.NaN; + } + else if (p == 0) { + return Number.POSITIVE_INFINITY; + } + else if (!jstat.isFinite(q)) { /* q == +Inf */ + return Number.NEGATIVE_INFINITY; + } + + if (p >= 10) { + /* p and q are big. */ + corr = jstat.lgammacor(p) + jstat.lgammacor(q) - jstat.lgammacor(p + q); + return Math.log(q) * -0.5 + jstat.LN_SQRT_2PI + corr + + (p - 0.5) * Math.log(p / (p + q)) + q * jstat.log1p(-p / (p + q)); + } + else if (q >= 10) { + /* p is small, but q is big. */ + corr = jstat.lgammacor(q) - jstat.lgammacor(p + q); + return jstat.lgamma(p) + corr + p - p * Math.log(p + q) + + (q - 0.5) * jstat.log1p(-p / (p + q)); + } + else + /* p and q are small: p <= q < 10. */ + return Math.log(jstat.gamma(p) * (jstat.gamma(q) / jstat.gamma(p + q))); +} + +jstat.dbinom_raw = function(x, n, p, q, give_log) { + if(give_log == null) give_log = false; + var lf, lc; + + if(p == 0) { + if(x == 0) { + // R_D__1 + return (give_log) ? 0.0 : 1.0; + } else { + // R_D__0 + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; + } + } + if(q == 0) { + if(x == n) { + // R_D__1 + return (give_log) ? 0.0 : 1.0; + } else { + // R_D__0 + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; + } + } + + if (x == 0) { + if(n == 0) return (give_log) ? 0.0 : 1.0; //R_D__1; + lc = (p < 0.1) ? -jstat.bd0(n,n*q) - n*p : n*Math.log(q); + return ( give_log ) ? lc : Math.exp(lc); //R_D_exp(lc) + } + + if (x == n) { + lc = (q < 0.1) ? -jstat.bd0(n,n*p) - n*q : n*Math.log(p); + return ( give_log ) ? lc : Math.exp(lc); //R_D_exp(lc) + } + + if (x < 0 || x > n) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0; + + /* n*p or n*q can underflow to zero if n and p or q are small. This + used to occur in dbeta, and gives NaN as from R 2.3.0. */ + lc = jstat.stirlerr(n) - jstat.stirlerr(x) - jstat.stirlerr(n-x) - jstat.bd0(x,n*p) - jstat.bd0(n-x,n*q); + + /* f = (M_2PI*x*(n-x))/n; could overflow or underflow */ + /* Upto R 2.7.1: + * lf = log(M_2PI) + log(x) + log(n-x) - log(n); + * -- following is much better for x << n : */ + lf = Math.log(jstat.TWO_PI) + Math.log(x) + jstat.log1p(- x/n); + + return (give_log) ? lc - 0.5*lf : Math.exp(lc - 0.5*lf); // R_D_exp(lc - 0.5*lf); +} + +jstat.max = function(values) { + var max = Number.NEGATIVE_INFINITY; + for(var i = 0; i < values.length; i++) { + if(values[i] > max) { + max = values[i]; + } + } + return max; +} + +/******************************************************************************/ +/* Probability Distributions */ +/******************************************************************************/ + +/** + * Range class + */ +var Range = Class.extend({ + init: function(min, max, numPoints) { + this._minimum = parseFloat(min); + this._maximum = parseFloat(max); + this._numPoints = parseFloat(numPoints); + }, + getMinimum: function() { + return this._minimum; + }, + getMaximum: function() { + return this._maximum; + }, + getNumPoints: function() { + return this._numPoints; + }, + getPoints: function() { + var results = []; + var x = this._minimum; + var step = (this._maximum-this._minimum)/(this._numPoints-1); + for(var i = 0; i < this._numPoints; i++) { + results[i] = parseFloat(x.toFixed(6)); + x += step; + } + return results; + } +}); + +Range.validate = function(range) { + if( ! range instanceof Range) { + return false; + } + if(isNaN(range.getMinimum()) || isNaN(range.getMaximum()) || isNaN(range.getNumPoints()) || range.getMaximum() < range.getMinimum() || range.getNumPoints() <= 0) { + return false; + } + return true; +} + +var ContinuousDistribution = Class.extend({ + init: function(name) { + this._name = name; + }, + toString: function() { + return this._string; + }, + getName: function() { + return this._name; + }, + getClassName: function() { + return this._name + 'Distribution'; + }, + density: function(valueOrRange) { + if(!isNaN(valueOrRange)) { + // single value + return parseFloat(this._pdf(valueOrRange).toFixed(15)); + } else if (Range.validate(valueOrRange)) { + // multiple values + var points = valueOrRange.getPoints(); + + var result = []; + // For each point in the range + for(var i = 0; i < points.length; i++) { + result[i] = parseFloat(this._pdf(points[i])); + } + return result; + } else { + // neither value or range + throw "Invalid parameter supplied to " + this.getClassName() + ".density()"; + } + }, + cumulativeDensity: function(valueOrRange) { + if(!isNaN(valueOrRange)) { + // single value + return parseFloat(this._cdf(valueOrRange).toFixed(15)); + } else if (Range.validate(valueOrRange)) { + // multiple values + var points = valueOrRange.getPoints(); + var result = []; + // For each point in the range + for(var i = 0; i < points.length; i++) { + result[i] = parseFloat(this._cdf(points[i])); + } + return result; + } else { + // neither value or range + throw "Invalid parameter supplied to " + this.getClassName() + ".cumulativeDensity()"; + } + }, + getRange: function(standardDeviations, numPoints) { + if(standardDeviations == null) { + standardDeviations = 5; + } + if(numPoints == null) { + numPoints = 100; + } + var min = this.getMean() - standardDeviations * Math.sqrt(this.getVariance()); + var max = this.getMean() + standardDeviations * Math.sqrt(this.getVariance()); + + if(this.getClassName() == 'GammaDistribution' || this.getClassName() == 'LogNormalDistribution') { + min = 0.0; + max = this.getMean() + standardDeviations * Math.sqrt(this.getVariance()); + } else if(this.getClassName() == 'BetaDistribution') { + min = 0.0; + max = 1.0; + } + + + var range = new Range(min, max, numPoints); + return range; + }, + getVariance: function(){}, + getMean: function(){}, + getQuantile: function(p) { + var self = this; + /* + * Recursive function to find the closest match + */ + function findClosestMatch(range, p) { + var ERR = 1.0e-5; + var xs = range.getPoints(); + var closestIndex = 0; + var closestDistance = 999; + + for(var i=0; i<xs.length; i++) { + var pp = self.cumulativeDensity(xs[i]); + var distance = Math.abs(pp - p); + if(distance < closestDistance) { + // closer value found + closestIndex = i; + closestDistance = distance; + } + } + if(closestDistance <= ERR) { + // Acceptable - return value; + return xs[closestIndex]; + } else { + // Calculate the new range + var newRange = new Range(xs[closestIndex-1], xs[closestIndex+1],20); + return findClosestMatch(newRange, p); + } + } + var range = this.getRange(5, 20); + return findClosestMatch(range, p); + } +}); + +/** + * A normal distribution object + */ +var NormalDistribution = ContinuousDistribution.extend({ + init: function(mean, sigma) { + this._super('Normal'); + this._mean = parseFloat(mean); + this._sigma = parseFloat(sigma); + this._string = "Normal ("+this._mean.toFixed(2)+", " + this._sigma.toFixed(2) + ")"; + }, + _pdf: function(x, give_log) { + if(give_log == null) { + give_log=false; + } // default is false; + var sigma = this._sigma; + var mu = this._mean; + if(!jstat.isFinite(sigma)) { + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0 + } + if(!jstat.isFinite(x) && mu == x) { + return Number.NaN; + } + if(sigma<=0) { + if(sigma < 0) { + throw "invalid sigma in _pdf"; + } + return (x==mu)?Number.POSITIVE_INFINITY:(give_log)?Number.NEGATIVE_INFINITY:0.0; + } + x=(x-mu)/sigma; + if(!jstat.isFinite(x)){ + return (give_log)?Number.NEGATIVE_INFINITY:0.0; + } + return (give_log ? -(jstat.LN_SQRT_2PI + 0.5 * x * x + Math.log(sigma)) : + jstat.ONE_SQRT_2PI * Math.exp(-0.5 * x * x) / sigma); + }, + _cdf: function(x, lower_tail, log_p) { + + if(lower_tail == null) lower_tail = true; + if(log_p == null) log_p = false; + + function pnorm_both(x, cum, ccum, i_tail, log_p) { + /* i_tail in {0,1,2} means: "lower", "upper", or "both" : + if(lower) return *cum := P[X <= x] + if(upper) return *ccum := P[X > x] = 1 - P[X <= x] + */ + + var a = [ + 2.2352520354606839287, + 161.02823106855587881, + 1067.6894854603709582, + 18154.981253343561249, + 0.065682337918207449113 + ]; + var b = [ + 47.20258190468824187, + 976.09855173777669322, + 10260.932208618978205, + 45507.789335026729956 + ]; + var c = [ + 0.39894151208813466764, + 8.8831497943883759412, + 93.506656132177855979, + 597.27027639480026226, + 2494.5375852903726711, + 6848.1904505362823326, + 11602.651437647350124, + 9842.7148383839780218, + 1.0765576773720192317e-8 + ]; + var d = [ + 22.266688044328115691, + 235.38790178262499861, + 1519.377599407554805, + 6485.558298266760755, + 18615.571640885098091, + 34900.952721145977266, + 38912.003286093271411, + 19685.429676859990727 + ]; + var p = [ + 0.21589853405795699, + 0.1274011611602473639, + 0.022235277870649807, + 0.001421619193227893466, + 2.9112874951168792e-5, + 0.02307344176494017303 + ]; + var q = [ + 1.28426009614491121, + 0.468238212480865118, + 0.0659881378689285515, + 0.00378239633202758244, + 7.29751555083966205e-5 + ]; + + var xden, xnum, temp, del, eps, xsq, y, i, lower, upper; + + /* Consider changing these : */ + eps = jstat.DBL_EPSILON * 0.5; + + /* i_tail in {0,1,2} =^= {lower, upper, both} */ + lower = i_tail != 1; + upper = i_tail != 0; + + y = Math.abs(x); + + if (y <= 0.67448975) { /* qnorm(3/4) = .6744.... -- earlier had 0.66291 */ + if (y > eps) { + xsq = x * x; + xnum = a[4] * xsq; + xden = xsq; + for (i = 0; i < 3; ++i) { + xnum = (xnum + a[i]) * xsq; + xden = (xden + b[i]) * xsq; + } + } else { + xnum = xden = 0.0; + } + temp = x * (xnum + a[3]) / (xden + b[3]); + if(lower) cum = 0.5 + temp; + if(upper) ccum = 0.5 - temp; + if(log_p) { + if(lower) cum = Math.log(cum); + if(upper) ccum = Math.log(ccum); + } + + } else if (y <= jstat.SQRT_32) { + /* Evaluate pnorm for 0.674.. = qnorm(3/4) < |x| <= sqrt(32) ~= 5.657 */ + + xnum = c[8] * y; + xden = y; + for (i = 0; i < 7; ++i) { + xnum = (xnum + c[i]) * y; + xden = (xden + d[i]) * y; + } + temp = (xnum + c[7]) / (xden + d[7]); + + /* do_del */ + xsq = jstat.trunc(x * 16) / 16; + del = (x - xsq) * (x + xsq); + if(log_p) { + cum = (-xsq * xsq * 0.5) + (-del * 0.5) + Math.log(temp); + if((lower && x > 0.) || (upper && x <= 0.)) + ccum = jstat.log1p(-Math.exp(-xsq * xsq * 0.5) * + Math.exp(-del * 0.5) * temp); + } + else { + cum = Math.exp(-xsq * xsq * 0.5) * Math.exp(-del * 0.5) * temp; + ccum = 1.0 - cum; + } + /* end do_del */ + + /* swap_tail */ + if (x > 0.0) {/* swap ccum <--> cum */ + temp = cum; + if(lower) { + cum = ccum; + + } + ccum = temp; + } + /* end swap_tail */ + + } + /* else |x| > sqrt(32) = 5.657 : + * the next two case differentiations were really for lower=T, log=F + * Particularly *not* for log_p ! + + * Cody had (-37.5193 < x && x < 8.2924) ; R originally had y < 50 + * + * Note that we do want symmetry(0), lower/upper -> hence use y + */ + + else if((log_p && y < 1e170)|| (lower && -37.5193 < x && x < 8.2924) + || (upper && -8.2924 < x && x < 37.5193)) { + /* Evaluate pnorm for x in (-37.5, -5.657) union (5.657, 37.5) */ + xsq = 1.0 / (x * x); /* (1./x)*(1./x) might be better */ + xnum = p[5] * xsq; + xden = xsq; + for (i = 0; i < 4; ++i) { + xnum = (xnum + p[i]) * xsq; + xden = (xden + q[i]) * xsq; + } + temp = xsq * (xnum + p[4]) / (xden + q[4]); + temp = (jstat.ONE_SQRT_2PI - temp) / y; + + /* do_del */ + xsq = jstat.trunc(x * 16) / 16; + del = (x - xsq) * (x + xsq); + if(log_p) { + cum = (-xsq * xsq * 0.5) + (-del * 0.5) + Math.log(temp); + if((lower && x > 0.) || (upper && x <= 0.)) + ccum = jstat.log1p(-Math.exp(-xsq * xsq * 0.5) * + Math.exp(-del * 0.5) * temp); + } + else { + cum = Math.exp(-xsq * xsq * 0.5) * Math.exp(-del * 0.5) * temp; + ccum = 1.0 - cum; + } + /* end do_del */ + + /* swap_tail */ + if (x > 0.0) {/* swap ccum <--> cum */ + temp = cum; + if(lower) { + cum = ccum; + + } + ccum = temp; + } + /* end swap_tail */ + + } else { /* large x such that probs are 0 or 1 */ + if(x > 0) { + cum = (log_p) ? 0.0 : 1.0; // R_D__1 + ccum = (log_p) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0; + } else { + cum = (log_p) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0; + ccum = (log_p) ? 0.0 : 1.0; // R_D__1 + } + } + + return [cum, ccum]; + } + + var p, cp; + var mu = this._mean; + var sigma = this._sigma; + var R_DT_0, R_DT_1; + + if(lower_tail) { + if(log_p) { + R_DT_0 = Number.NEGATIVE_INFINITY; + R_DT_1 = 0.0; + } else { + R_DT_0 = 0.0; + R_DT_1 = 1.0; + } + } else { + if(log_p) { + R_DT_0 = 0.0; + R_DT_1 = Number.NEGATIVE_INFINITY; + } else { + R_DT_0 = 1.0; + R_DT_1 = 0.0; + } + } + + if(!jstat.isFinite(x) && mu == x) return Number.NaN; + if(sigma <= 0) { + if(sigma < 0) { + console.warn("Sigma is less than 0"); + return Number.NaN; + } + return (x < mu) ? R_DT_0 : R_DT_1; + } + + p = (x - mu) / sigma; + + if(!jstat.isFinite(p)) { + return (x < mu) ? R_DT_0 : R_DT_1; + } + + x = p; + + // pnorm_both(x, &p, &cp, (lower_tail ? 0 : 1), log_p); + // result[0] == &p + // result[1] == &cp + + var result = pnorm_both(x, p, cp, (lower_tail ? false : true), log_p); + + return (lower_tail ? result[0] : result[1]); + + }, + getMean: function() { + return this._mean; + }, + getSigma: function() { + return this._sigma; + }, + getVariance: function() { + return this._sigma*this._sigma; + } +}); + +/** + * A Log-normal distribution object + */ +var LogNormalDistribution = ContinuousDistribution.extend({ + init: function(location, scale) { + this._super('LogNormal') + this._location = parseFloat(location); + this._scale = parseFloat(scale); + this._string = "LogNormal ("+this._location.toFixed(2)+", " + this._scale.toFixed(2) + ")"; + }, + _pdf: function(x, give_log) { + var y; + var sdlog = this._scale; + var meanlog = this._location; + if(give_log == null) { + give_log = false; + } + + if(sdlog <= 0) throw "Illegal parameter in _pdf"; + + if(x <= 0) { + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; + } + + y = (Math.log(x) - meanlog) / sdlog; + + return (give_log ? -(jstat.LN_SQRT_2PI + 0.5 * y * y + Math.log(x * sdlog)) : + jstat.ONE_SQRT_2PI * Math.exp(-0.5 * y * y) / (x * sdlog)); + + }, + _cdf: function(x, lower_tail, log_p) { + var sdlog = this._scale; + var meanlog = this._location; + if(lower_tail == null) { + lower_tail = true; + } + if(log_p == null) { + log_p = false; + } + + + if(sdlog <= 0) { + throw "illegal std in _cdf"; + } + + if(x > 0) { + var nd = new NormalDistribution(meanlog, sdlog); + return nd._cdf(Math.log(x), lower_tail, log_p); + } + if(lower_tail) { + return (log_p) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0 + } else { + return (log_p) ? 0.0 : 1.0; // R_D__1 + } + }, + getLocation: function() { + return this._location; + }, + getScale: function() { + return this._scale; + }, + getMean: function() { + return Math.exp((this._location + this._scale) / 2); + }, + getVariance: function() { + var ans = (Math.exp(this._scale)-1)*Math.exp(2*this._location+this._scale); + return ans; + } +}); + + +/** + * Gamma distribution object + */ +var GammaDistribution = ContinuousDistribution.extend({ + init: function(shape, scale) { + this._super('Gamma'); + this._shape = parseFloat(shape); + this._scale = parseFloat(scale); + this._string = "Gamma ("+this._shape.toFixed(2)+", " + this._scale.toFixed(2) + ")"; + }, + _pdf: function(x, give_log) { + var pr; + var shape = this._shape; + var scale = this._scale; + if(give_log == null) { + give_log = false; // default value + } + + if(shape < 0 || scale <= 0) { + throw "Illegal argument in _pdf"; + } + + if(x < 0) { + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0 + } + if(shape == 0) { /* point mass at 0 */ + return (x == 0) ? Number.POSITIVE_INFINITY : (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0 + } + if(x == 0) { + if(shape < 1) return Number.POSITIVE_INFINITY; + if(shape > 1) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0 + /* else */ + return (give_log) ? -Math.log(scale) : 1/scale; + } + + if(shape < 1) { + pr = jstat.dopois_raw(shape, x/scale, give_log); + return give_log ? pr + Math.log(shape/x) : pr*shape/x; + } + /* else shape >= 1 */ + pr = jstat.dopois_raw(shape-1, x/scale, give_log); + return give_log ? pr - Math.log(scale) : pr/scale; + + }, + /** + * This function computes the distribution function for the + * gamma distribution with shape parameter alph and scale parameter + * scale. This is also known as the incomplete gamma function. + * See Abramowitz and Stegun (6.5.1) for example. + */ + _cdf: function(x, lower_tail, log_p) { + /* define USE_PNORM */ + function USE_PNORM() { + pn1 = Math.sqrt(alph) * 3.0 * (Math.pow(x/alph,1.0/3.0) + 1.0 / (9.0 * alph) - 1.0); + var norm_dist = new NormalDistribution(0.0, 1.0); + return norm_dist._cdf(pn1, lower_tail, log_p); + } + + /* Defaults */ + if(lower_tail == null) lower_tail = true; + if(log_p == null) log_p = false; + var alph = this._shape; + var scale = this._scale; + var xbig = 1.0e+8; + var xlarge = 1.0e+37; + var alphlimit = 1e5; + var pn1,pn2,pn3,pn4,pn5,pn6,arg,a,b,c,an,osum,sum,n,pearson; + + if(alph <= 0. || scale <= 0.) { + console.warn('Invalid gamma params in _cdf'); + return Number.NaN; + } + + x/=scale; + if(isNaN(x)) return x; + if(x <= 0.0) { + // R_DT_0 + if(lower_tail) { + // R_D__0 + return (log_p) ? Number.NEGATIVE_INFINITY : 0.0; + } else { + // R_D__1 + return (log_p) ? 0.0 : 1.0; + } + } + + if(alph > alphlimit) { + return USE_PNORM(); + } + + if(x > xbig * alph) { + if(x > jstat.DBL_MAX * alph) { + // R_DT_1 + if(lower_tail) { + // R_D__1 + return (log_p) ? 0.0 : 1.0; + } else { + // R_D__0 + return (log_p) ? Number.NEGATIVE_INFINITY : 0.0; + } + } else { + return USE_PNORM(); + } + } + + if(x <= 1.0 || x < alph) { + pearson = 1; /* use pearson's series expansion */ + arg = alph * Math.log(x) - x - jstat.lgamma(alph + 1.0); + + c = 1.0; + sum = 1.0; + a = alph; + do { + a += 1.0; + c *= x / a; + sum += c; + } while(c > jstat.DBL_EPSILON * sum); + } else { /* x >= max( 1, alph) */ + pearson = 0;/* use a continued fraction expansion */ + arg = alph * Math.log(x) - x - jstat.lgamma(alph); + + a = 1. - alph; + b = a + x + 1.; + pn1 = 1.; + pn2 = x; + pn3 = x + 1.; + pn4 = x * b; + sum = pn3 / pn4; + + for (n = 1; ; n++) { + a += 1.;/* = n+1 -alph */ + b += 2.;/* = 2(n+1)-alph+x */ + an = a * n; + pn5 = b * pn3 - an * pn1; + pn6 = b * pn4 - an * pn2; + if (Math.abs(pn6) > 0.) { + osum = sum; + sum = pn5 / pn6; + if (Math.abs(osum - sum) <= jstat.DBL_EPSILON * jstat.fmin2(1.0, sum)) + break; + } + pn1 = pn3; + pn2 = pn4; + pn3 = pn5; + pn4 = pn6; + + if (Math.abs(pn5) >= xlarge) { + pn1 /= xlarge; + pn2 /= xlarge; + pn3 /= xlarge; + pn4 /= xlarge; + } + } + } + arg += Math.log(sum); + lower_tail = (lower_tail == pearson); + + if (log_p && lower_tail) + return(arg); + /* else */ + /* sum = exp(arg); and return if(lower_tail) sum else 1-sum : */ + + if(lower_tail) { + return Math.exp(arg); + } else { + if(log_p) { + // R_Log1_Exp(arg); + return (arg > -Math.LN2) ? Math.log(-jstat.expm1(arg)) : jstat.log1p(-Math.exp(arg)); + } else { + return -jstat.expm1(arg); + } + } + }, + getShape: function() { + return this._shape; + }, + getScale: function() { + return this._scale; + }, + getMean: function() { + return this._shape * this._scale; + }, + getVariance: function() { + return this._shape*Math.pow(this._scale,2); + } +}); + +/** + * A Beta distribution object + */ +var BetaDistribution = ContinuousDistribution.extend({ + init: function(alpha, beta) { + this._super('Beta'); + this._alpha = parseFloat(alpha); + this._beta = parseFloat(beta); + this._string = "Beta ("+this._alpha.toFixed(2)+", " + this._beta.toFixed(2) + ")"; + }, + _pdf: function(x, give_log) { + if(give_log == null) give_log = false; // default; + var a = this._alpha; + var b = this._beta; + var lval; + if(a <= 0 || b <= 0) { + console.warn('Illegal arguments in _pdf'); + return Number.NaN; + } + if(x < 0 || x > 1) { + // R_D__0 + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; + } + if(x == 0) { + if(a > 1) { + // R_D__0 + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; + } + if(a < 1) { + return Number.POSITIVE_INFINITY; + } + /*a == 1 */ return (give_log) ? Math.log(b) : b; // R_D_val(b) + } + if(x == 1) { + if(b > 1) { + // R_D__0 + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; + } + if(b < 1) { + return Number.POSITIVE_INFINITY; + } + /* b == 1 */ return (give_log) ? Math.log(a) : a; // R_D_val(a) + } + if(a<=2||b<=2) { + lval = (a-1)*Math.log(x) + (b-1)*jstat.log1p(-x) - jstat.logBeta(a, b); + } else { + lval = Math.log(a+b-1) + jstat.dbinom_raw(a-1, a+b-2, x, 1-x, true); + } + //R_D_exp(lval) + return (give_log) ? lval : Math.exp(lval); + }, + _cdf: function(x, lower_tail, log_p) { + if(lower_tail == null) lower_tail = true; + if(log_p == null) log_p = false; + + var pin = this._alpha; + var qin = this._beta; + + if(pin <= 0 || qin <= 0) { + console.warn('Invalid argument in _cdf'); + return Number.NaN; + } + + if(x <= 0) { + //R_DT_0; + if(lower_tail) { + // R_D__0 + return (log_p) ? Number.NEGATIVE_INFINITY : 0.0; + } else { + // R_D__1 + return (log_p) ? 0.1 : 1.0; + } + } + + if(x >= 1){ + // R_DT_1 + if(lower_tail) { + // R_D__1 + return (log_p) ? 0.1 : 1.0; + } else { + // R_D__0 + return (log_p) ? Number.NEGATIVE_INFINITY : 0.0; + } + } + + /* else */ + return jstat.incompleteBeta(pin, qin, x); + }, + getAlpha: function() { + return this._alpha; + }, + getBeta: function() { + return this._beta; + }, + getMean: function() { + return this._alpha / (this._alpha+ this._beta); + }, + getVariance: function() { + var ans = (this._alpha * this._beta) / (Math.pow(this._alpha+this._beta,2)*(this._alpha+this._beta+1)); + return ans; + } +}); + +var StudentTDistribution = ContinuousDistribution.extend({ + init: function(degreesOfFreedom, mu) { + this._super('StudentT'); + this._dof = parseFloat(degreesOfFreedom); + + if(mu != null) { + this._mu = parseFloat(mu); + this._string = "StudentT ("+this._dof.toFixed(2)+", " + this._mu.toFixed(2)+ ")"; + } else { + this._mu = 0.0; + this._string = "StudentT ("+this._dof.toFixed(2)+")"; + } + + }, + _pdf: function(x, give_log) { + if(give_log == null) give_log = false; + if(this._mu == null) { + return this._dt(x, give_log); + } else { + var y = this._dnt(x, give_log); + if(y > 1){ + console.warn('x:' + x + ', y: ' + y); + } + return y; + } + }, + _cdf: function(x, lower_tail, give_log) { + if(lower_tail == null) lower_tail = true; + if(give_log == null) give_log = false; + if(this._mu == null) { + return this._pt(x, lower_tail, give_log); + } else { + return this._pnt(x, lower_tail, give_log); + } + }, + _dt: function(x, give_log) { + var t,u; + var n = this._dof; + if (n <= 0){ + console.warn('Invalid parameters in _dt'); + return Number.NaN; + } + if(!jstat.isFinite(x)) { + return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0; + } + + if(!jstat.isFinite(n)) { + var norm = new NormalDistribution(0.0, 1.0); + return norm.density(x, give_log); + } + + + t = -jstat.bd0(n/2.0,(n+1)/2.0) + jstat.stirlerr((n+1)/2.0) - jstat.stirlerr(n/2.0); + if ( x*x > 0.2*n ) + u = Math.log( 1+ x*x/n ) * n/2; + else + u = -jstat.bd0(n/2.0,(n+x*x)/2.0) + x*x/2.0; + + var p1 = jstat.TWO_PI *(1+x*x/n); + var p2 = t-u; + + return (give_log) ? -0.5*Math.log(p1) + p2 : Math.exp(p2)/Math.sqrt(p1); // R_D_fexp(M_2PI*(1+x*x/n), t-u); + }, + _dnt: function(x, give_log) { + if(give_log == null) give_log = false; + var df = this._dof; + var ncp = this._mu; + var u; + + if(df <= 0.0) { + console.warn("Illegal arguments _dnf"); + return Number.NaN; + } + if(ncp == 0.0) { + return this._dt(x, give_log); + } + + if(!jstat.isFinite(x)) { + // R_D__0 + if(give_log) { + return Number.NEGATIVE_INFINITY; + } else { + return 0.0; + } + } + + /* If infinite df then the density is identical to a + normal distribution with mean = ncp. However, the formula + loses a lot of accuracy around df=1e9 + */ + if(!isFinite(df) || df > 1e8) { + var dist = new NormalDistribution(ncp, 1.); + return dist.density(x, give_log); + } + + /* Do calculations on log scale to stabilize */ + + /* Consider two cases: x ~= 0 or not */ + if (Math.abs(x) > Math.sqrt(df * jstat.DBL_EPSILON)) { + var newT = new StudentTDistribution(df+2, ncp); + u = Math.log(df) - Math.log(Math.abs(x)) + + Math.log(Math.abs(newT._pnt(x*Math.sqrt((df+2)/df), true, false) - + this._pnt(x, true, false))); + /* FIXME: the above still suffers from cancellation (but not horribly) */ + } + else { /* x ~= 0 : -> same value as for x = 0 */ + u = jstat.lgamma((df+1)/2) - jstat.lgamma(df/2) + - .5*(Math.log(Math.PI) + Math.log(df) + ncp*ncp); + } + + return (give_log ? u : Math.exp(u)); + }, + _pt: function(x, lower_tail, log_p) { + if(lower_tail == null) lower_tail = true; + if(log_p == null) log_p = false; + var val, nx; + var n = this._dof; + var DT_0, DT_1; + + if(lower_tail) { + if(log_p) { + DT_0 = Number.NEGATIVE_INFINITY; + DT_1 = 1.; + } else { + DT_0 = 0.; + DT_1 = 1.; + } + } else { + if(log_p) { + // not lower_tail but log_p + DT_0 = 0.; + DT_1 = Number.NEGATIVE_INFINITY; + } else { + // not lower_tail and not log_p + DT_0 = 1.; + DT_1 = 0.; + } + } + + if(n <= 0.0) { + console.warn("Invalid T distribution _pt"); + return Number.NaN; + } + var norm = new NormalDistribution(0,1); + if(!jstat.isFinite(x)) { + return (x < 0) ? DT_0 : DT_1; + } + if(!jstat.isFinite(n)) { + return norm._cdf(x, lower_tail, log_p); + } + + if (n > 4e5) { /*-- Fixme(?): test should depend on `n' AND `x' ! */ + /* Approx. from Abramowitz & Stegun 26.7.8 (p.949) */ + val = 1./(4.*n); + return norm._cdf(x*(1. - val)/sqrt(1. + x*x*2.*val), lower_tail, log_p); + } + + nx = 1 + (x/n)*x; + /* FIXME: This test is probably losing rather than gaining precision, + * now that pbeta(*, log_p = TRUE) is much better. + * Note however that a version of this test *is* needed for x*x > D_MAX */ + if(nx > 1e100) { /* <==> x*x > 1e100 * n */ + /* Danger of underflow. So use Abramowitz & Stegun 26.5.4 + pbeta(z, a, b) ~ z^a(1-z)^b / aB(a,b) ~ z^a / aB(a,b), + with z = 1/nx, a = n/2, b= 1/2 : + */ + var lval; + lval = -0.5*n*(2*Math.log(Math.abs(x)) - Math.log(n)) + - jstat.logBeta(0.5*n, 0.5) - Math.log(0.5*n); + val = log_p ? lval : Math.exp(lval); + } else { + /* + val = (n > x * x) + // ? pbeta (x * x / (n + x * x), 0.5, n / 2., 0, log_p) + // : pbeta (1. / nx, n / 2., 0.5, 1, log_p); + */ + if(n > x * x) { + var beta = new BetaDistribution(0.5, n/2.); + return beta._cdf(x*x/ (n + x * x), false, log_p); + } else { + beta = new BetaDistribution(n / 2., 0.5); + return beta._cdf(1. / nx, true, log_p); + } + + + } + + /* Use "1 - v" if lower_tail and x > 0 (but not both):*/ + if(x <= 0.) + lower_tail = !lower_tail; + + if(log_p) { + if(lower_tail) return jstat.log1p(-0.5*Math.exp(val)); + else return val - M_LN2; /* = log(.5* pbeta(....)) */ + } + else { + val /= 2.; + if(lower_tail) { + return (0.5 - val + 0.5); + } else { + return val; + } + } + }, + _pnt: function(t, lower_tail, log_p) { + + var dof = this._dof; + var ncp = this._mu; + var DT_0, DT_1; + + if(lower_tail) { + if(log_p) { + DT_0 = Number.NEGATIVE_INFINITY; + DT_1 = 1.; + } else { + DT_0 = 0.; + DT_1 = 1.; + } + } else { + if(log_p) { + // not lower_tail but log_p + DT_0 = 0.; + DT_1 = Number.NEGATIVE_INFINITY; + } else { + // not lower_tail and not log_p + DT_0 = 1.; + DT_1 = 0.; + } + } + + var albeta, a, b, del, errbd, lambda, rxb, tt, x; + var geven, godd, p, q, s, tnc, xeven, xodd; + var it, negdel; + + /* note - itrmax and errmax may be changed to suit one's needs. */ + var ITRMAX = 1000; + var ERRMAX = 1.e-7; + + if(dof <= 0.0) { + return Number.NaN; + } else if (dof == 0.0) { + return this._pt(t); + } + + if(!jstat.isFinite(t)) { + return (t < 0) ? DT_0 : DT_1; + } + if(t >= 0.) { + negdel = false; + tt = t; + del = ncp; + } else { + /* We deal quickly with left tail if extreme, + since pt(q, df, ncp) <= pt(0, df, ncp) = \Phi(-ncp) */ + if(ncp >= 40 && (!log_p || !lower_tail)) { + return DT_0; + } + negdel = true; + tt = -t; + del = -ncp; + } + + if(dof > 4e5 || del*del > 2* Math.LN2 * (-(jstat.DBL_MIN_EXP))) { + /*-- 2nd part: if del > 37.62, then p=0 below + FIXME: test should depend on `df', `tt' AND `del' ! */ + /* Approx. from Abramowitz & Stegun 26.7.10 (p.949) */ + s=1./(4.*dof); + var norm = new NormalDistribution(del, Math.sqrt(1. + tt*tt*2.*s)); + var result = norm._cdf(tt*(1.-s), lower_tail != negdel, log_p); + return result; + } + + /* initialize twin series */ + /* Guenther, J. (1978). Statist. Computn. Simuln. vol.6, 199. */ + x = t * t; + rxb = dof/(x + dof);/* := (1 - x) {x below} -- but more accurately */ + x = x / (x + dof);/* in [0,1) */ + if (x > 0.) {/* <==> t != 0 */ + lambda = del * del; + p = .5 * Math.exp(-.5 * lambda); + if(p == 0.) { // underflow! + console.warn("underflow in _pnt"); + return DT_0; + } + q = jstat.SQRT_2dPI * p * del; + s = .5 - p; + if(s < 1e-7) { + s = -0.5 * jstat.expm1(-0.5 * lambda); + } + a = .5; + b = .5 * dof; + /* rxb = (1 - x) ^ b [ ~= 1 - b*x for tiny x --> see 'xeven' below] + * where '(1 - x)' =: rxb {accurately!} above */ + rxb = Math.pow(rxb, b); + albeta = jstat.LN_SQRT_PI + jstat.lgamma(b) - jstat.lgamma(.5 + b); + /* TODO: change incompleteBeta function to accept lower_tail and p_log */ + xodd = jstat.incompleteBeta(a, b, x); + godd = 2. * rxb * Math.exp(a * Math.log(x) - albeta); + tnc = b * x; + xeven = (tnc < jstat.DBL_EPSILON) ? tnc : 1. - rxb; + geven = tnc * rxb; + tnc = p * xodd + q * xeven; + + /* repeat until convergence or iteration limit */ + for(it = 1; it <= ITRMAX; it++) { + a += 1.; + xodd -= godd; + xeven -= geven; + godd *= x * (a + b - 1.) / a; + geven *= x * (a + b - .5) / (a + .5); + p *= lambda / (2 * it); + q *= lambda / (2 * it + 1); + tnc += p * xodd + q * xeven; + s -= p; + /* R 2.4.0 added test for rounding error here. */ + if(s < -1.e-10) { /* happens e.g. for (t,df,ncp)=(40,10,38.5), after 799 it.*/ + //console.write("precision error _pnt"); + break; + } + if(s <= 0 && it > 1) break; + + errbd = 2. * s * (xodd - godd); + + if(Math.abs(errbd) < ERRMAX) break;/*convergence*/ + } + + if(it == ITRMAX) { + throw "Non-convergence _pnt"; + } + } else { + tnc = 0.; + } + norm = new NormalDistribution(0,1); + tnc += norm._cdf(-del, /*lower*/true, /*log_p*/ false); + + lower_tail = lower_tail != negdel; /* xor */ + if(tnc > 1 - 1e-10 && lower_tail) { + //console.warn("precision error _pnt"); + } + var res = jstat.fmin2(tnc, 1.); + if(lower_tail) { + if(log_p) { + return Math.log(res); + } else { + return res; + } + } else { + if(log_p) { + return jstat.log1p(-(res)); + } else { + return (0.5 - (res) + 0.5); + } + } + }, + getDegreesOfFreedom: function() { + return this._dof; + }, + getNonCentralityParameter: function() { + return this._mu; + }, + getMean: function() { + if(this._dof > 1) { + var ans = (1/2)*Math.log(this._dof/2) + jstat.lgamma((this._dof-1)/2) - jstat.lgamma(this._dof/2) + return Math.exp(ans) * this._mu; + } else { + return Number.NaN; + } + }, + getVariance: function() { + if(this._dof > 2) { + var ans = this._dof * (1 + this._mu*this._mu)/(this._dof-2) - (((this._mu*this._mu * this._dof) / 2) * Math.pow(Math.exp(jstat.lgamma((this._dof - 1)/2)-jstat.lgamma(this._dof/2)), 2)); + return ans; + } else { + return Number.NaN; + } + } +}); + + +/******************************************************************************/ +/* jQuery Flot graph objects */ +/******************************************************************************/ + +var Plot = Class.extend({ + init: function(id, options) { + this._container = '#' + String(id); + this._plots = []; + this._flotObj = null; + this._locked = false; + + if(options != null) { + this._options = options; + } else { + this._options = { + }; + } + + }, + getContainer: function() { + return this._container; + }, + getGraph: function() { + return this._flotObj; + }, + setData: function(data) { + this._plots = data; + }, + clear: function() { + this._plots = []; + //this.render(); + }, + showLegend: function() { + this._options.legend = { + show: true + } + this.render(); + }, + hideLegend: function() { + this._options.legend = { + show: false + } + this.render(); + }, + render: function() { + this._flotObj = null; + this._flotObj = $.plot($(this._container), this._plots, this._options); + } +}); + +var DistributionPlot = Plot.extend({ + init: function(id, distribution, range, options) { + this._super(id, options); + this._showPDF = true; + this._showCDF = false; + this._pdfValues = []; // raw values for pdf + this._cdfValues = []; // raw values for cdf + this._maxY = 1; + this._plotType = 'line'; // line, point, both + this._fill = false; + this._distribution = distribution; // underlying PDF + // Range object for the plot + if(range != null && Range.validate(range)) { + this._range = range; + } else { + this._range = this._distribution.getRange(); // no range supplied, use distribution default + } + + // render + if(this._distribution != null) { + this._maxY = this._generateValues(); // create the pdf/cdf values in the ctor + } else { + this._options.xaxis = { + min: range.getMinimum(), + max: range.getMaximum() + } + this._options.yaxis = { + max: 1 + } + } + + + + this.render(); + }, + setHover: function(bool) { + if(bool) { + if(this._options.grid == null) { + this._options.grid = { + hoverable: true, + mouseActiveRadius: 25 + } + } else { + this._options.grid.hoverable = true, + this._options.grid.mouseActiveRadius = 25 + } + function showTooltip(x, y, contents, color) { + $('<div id="jstat_tooltip">' + contents + '</div>').css( { + position: 'absolute', + display: 'none', + top: y + 15, + 'font-size': 'small', + left: x + 5, + border: '1px solid ' + color[1], + color: color[2], + padding: '5px', + 'background-color': color[0], + opacity: 0.80 + }).appendTo("body").show(); + } + var previousPoint = null; + $(this._container).bind("plothover", function(event, pos, item) { + $("#x").text(pos.x.toFixed(2)); + $("#y").text(pos.y.toFixed(2)); + if (item) { + if (previousPoint != item.datapoint) { + previousPoint = item.datapoint; + $("#jstat_tooltip").remove(); + var x = jstat.toSigFig(item.datapoint[0],2), y = jstat.toSigFig(item.datapoint[1], 2); + var text = null; + var color = item.series.color; + if(item.series.label == 'PDF') { + text = "P(" + x + ") = " + y; + color = ["#fee", "#fdd", "#C05F5F"]; + } else { + // cdf + text = "F(" + x + ") = " + y; + color = ["#eef", "#ddf", "#4A4AC0"]; + } + showTooltip(item.pageX, item.pageY, text, color); + } + } + else { + $("#jstat_tooltip").remove(); + previousPoint = null; + } + }); + $(this._container).bind("mouseleave", function() { + if($('#jstat_tooltip').is(':visible')) { + $('#jstat_tooltip').remove(); + previousPoint = null; + } + }); + } else { + // unbind + if(this._options.grid == null) { + this._options.grid = { + hoverable: false + } + } else { + this._options.grid.hoverable = false + } + $(this._container).unbind("plothover"); + } + + this.render(); + }, + setType: function(type) { + this._plotType = type; + var lines = {}; + var points = {}; + if(this._plotType == 'line') { + lines.show = true; + points.show = false; + } else if(this._plotType == 'points') { + lines.show = false; + points.show = true; + } else if(this._plotType == 'both') { + lines.show = true; + points.show = true; + } + if(this._options.series == null) { + this._options.series = { + lines: lines, + points: points + } + } else { + if(this._options.series.lines == null) { + this._options.series.lines = lines; + } else { + this._options.series.lines.show = lines.show; + } + if(this._options.series.points == null) { + this._options.series.points = points; + } else { + this._options.series.points.show = points.show; + } + } + + this.render(); + }, + setFill: function(bool) { + this._fill = bool; + if(this._options.series == null) { + this._options.series = { + lines: { + fill: bool + } + } + } else { + if(this._options.series.lines == null) { + this._options.series.lines = { + fill: bool + } + } else { + this._options.series.lines.fill = bool; + } + } + this.render(); + }, + clear: function() { + this._super(); + this._distribution = null; + this._pdfValues = []; + this._cdfValues = []; + this.render(); + }, + _generateValues: function() { + this._cdfValues = []; // reinitialize the arrays. + this._pdfValues = []; + + var xs = this._range.getPoints(); + + this._options.xaxis = { + min: xs[0], + max: xs[xs.length-1] + } + var pdfs = this._distribution.density(this._range); + var cdfs = this._distribution.cumulativeDensity(this._range); + for(var i = 0; i < xs.length; i++) { + if(pdfs[i] == Number.POSITIVE_INFINITY || pdfs[i] == Number.NEGATIVE_INFINITY) { + pdfs[i] = null; + } + if(cdfs[i] == Number.POSITIVE_INFINITY || cdfs[i] == Number.NEGATIVE_INFINITY) { + cdfs[i] = null; + } + this._pdfValues.push([xs[i], pdfs[i]]); + this._cdfValues.push([xs[i], cdfs[i]]); + } + return jstat.max(pdfs); + }, + showPDF: function() { + this._showPDF = true; + this.render(); + }, + hidePDF: function() { + this._showPDF = false; + this.render(); + }, + showCDF: function() { + this._showCDF = true; + this.render(); + }, + hideCDF: function() { + this._showCDF = false; + this.render(); + }, + setDistribution: function(distribution, range) { + this._distribution = distribution; + if(range != null) { + this._range = range; + } else { + this._range = distribution.getRange(); + } + this._maxY = this._generateValues(); + this._options.yaxis = { + max: this._maxY*1.1 + } + + this.render(); + }, + getDistribution: function() { + return this._distribution; + }, + getRange: function() { + return this._range; + }, + setRange: function(range) { + this._range = range; + this._generateValues(); + this.render(); + }, + render: function() { + if(this._distribution != null) { + if(this._showPDF && this._showCDF) { + this.setData([{ + yaxis: 1, + data:this._pdfValues, + color: 'rgb(237,194,64)', + clickable: false, + hoverable: true, + label: "PDF" + }, { + yaxis: 2, + data:this._cdfValues, + clickable: false, + color: 'rgb(175,216,248)', + hoverable: true, + label: "CDF" + }]); + this._options.yaxis = { + max: this._maxY*1.1 + } + } else if(this._showPDF) { + this.setData([{ + data:this._pdfValues, + hoverable: true, + color: 'rgb(237,194,64)', + clickable: false, + label: "PDF" + }]); + this._options.yaxis = { + max: this._maxY*1.1 + } + } else if(this._showCDF) { + this.setData([{ + data:this._cdfValues, + hoverable: true, + color: 'rgb(175,216,248)', + clickable: false, + label: "CDF" + }]); + this._options.yaxis = { + max: 1.1 + } + } + } else { + this.setData([]); + } + this._super(); // Call the parent plot method + } +}); + +var DistributionFactory = {}; +DistributionFactory.build = function(json) { + /* + if(json.name == null) { + try{ + json = JSON.parse(json); + } + catch(err) { + throw "invalid JSON"; + } + + // try to parse it + }*/ + + /* + if(json.name != null) { + var name = json.name; + } else { + throw "Malformed JSON provided to DistributionBuilder " + json; + } + */ + if(json.NormalDistribution) { + if(json.NormalDistribution.mean != null && json.NormalDistribution.standardDeviation != null) { + return new NormalDistribution(json.NormalDistribution.mean[0], json.NormalDistribution.standardDeviation[0]); + } else { + throw "Malformed JSON provided to DistributionBuilder " + json; + } + } else if (json.LogNormalDistribution) { + if(json.LogNormalDistribution.location != null && json.LogNormalDistribution.scale != null) { + return new LogNormalDistribution(json.LogNormalDistribution.location[0], json.LogNormalDistribution.scale[0]); + } else { + throw "Malformed JSON provided to DistributionBuilder " + json; + } + } else if (json.BetaDistribution) { + if(json.BetaDistribution.alpha != null && json.BetaDistribution.beta != null) { + return new BetaDistribution(json.BetaDistribution.alpha[0], json.BetaDistribution.beta[0]); + } else { + throw "Malformed JSON provided to DistributionBuilder " + json; + } + } else if (json.GammaDistribution) { + if(json.GammaDistribution.shape != null && json.GammaDistribution.scale != null) { + return new GammaDistribution(json.GammaDistribution.shape[0], json.GammaDistribution.scale[0]); + } else { + throw "Malformed JSON provided to DistributionBuilder " + json; + } + } else if (json.StudentTDistribution) { + if(json.StudentTDistribution.degreesOfFreedom != null && json.StudentTDistribution.nonCentralityParameter != null) { + return new StudentTDistribution(json.StudentTDistribution.degreesOfFreedom[0], json.StudentTDistribution.nonCentralityParameter[0]); + } else if(json.StudentTDistribution.degreesOfFreedom != null) { + return new StudentTDistribution(json.StudentTDistribution.degreesOfFreedom[0]); + } else { + throw "Malformed JSON provided to DistributionBuilder " + json; + } + } else { + throw "Malformed JSON provided to DistributionBuilder " + json; + } +} + +