comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:1407e3634bcf
1 function jstat(){}
2 j = jstat;
3 /* Simple JavaScript Inheritance
4 * By John Resig http://ejohn.org/
5 * MIT Licensed.
6 */
7 // Inspired by base2 and Prototype
8 (function(){
9 var initializing = false, fnTest = /xyz/.test(function(){
10 xyz;
11 }) ? /\b_super\b/ : /.*/;
12 // The base Class implementation (does nothing)
13 this.Class = function(){};
14
15 // Create a new Class that inherits from this class
16 Class.extend = function(prop) {
17 var _super = this.prototype;
18
19 // Instantiate a base class (but only create the instance,
20 // don't run the init constructor)
21 initializing = true;
22 var prototype = new this();
23 initializing = false;
24
25 // Copy the properties over onto the new prototype
26 for (var name in prop) {
27 // Check if we're overwriting an existing function
28 prototype[name] = typeof prop[name] == "function" &&
29 typeof _super[name] == "function" && fnTest.test(prop[name]) ?
30 (function(name, fn){
31 return function() {
32 var tmp = this._super;
33
34 // Add a new ._super() method that is the same method
35 // but on the super-class
36 this._super = _super[name];
37
38 // The method only need to be bound temporarily, so we
39 // remove it when we're done executing
40 var ret = fn.apply(this, arguments);
41 this._super = tmp;
42
43 return ret;
44 };
45 })(name, prop[name]) :
46 prop[name];
47 }
48
49 // The dummy class constructor
50 function Class() {
51 // All construction is actually done in the init method
52 if ( !initializing && this.init )
53 this.init.apply(this, arguments);
54 }
55
56 // Populate our constructed prototype object
57 Class.prototype = prototype;
58
59 // Enforce the constructor to be what we expect
60 Class.constructor = Class;
61
62 // And make this class extendable
63 Class.extend = arguments.callee;
64
65 return Class;
66 };
67 })();
68
69 /******************************************************************************/
70 /* Constants */
71 /******************************************************************************/
72 jstat.ONE_SQRT_2PI = 0.3989422804014327;
73 jstat.LN_SQRT_2PI = 0.9189385332046727417803297;
74 jstat.LN_SQRT_PId2 = 0.225791352644727432363097614947;
75 jstat.DBL_MIN = 2.22507e-308;
76 jstat.DBL_EPSILON = 2.220446049250313e-16;
77 jstat.SQRT_32 = 5.656854249492380195206754896838;
78 jstat.TWO_PI = 6.283185307179586;
79 jstat.DBL_MIN_EXP = -999;
80 jstat.SQRT_2dPI = 0.79788456080287;
81 jstat.LN_SQRT_PI = 0.5723649429247;
82 /******************************************************************************/
83 /* jstat Functions */
84 /******************************************************************************/
85 jstat.seq = function(min, max, length) {
86 var r = new Range(min, max, length);
87 return r.getPoints();
88 }
89
90 jstat.dnorm = function(x, mean, sd, log) {
91 if(mean == null) mean = 0;
92 if(sd == null) sd = 1;
93 if(log == null) log = false;
94 var n = new NormalDistribution(mean, sd);
95 if(!isNaN(x)) {
96 // is a number
97 return n._pdf(x, log);
98 } else if(x.length) {
99 var res = [];
100 for(var i = 0; i < x.length; i++) {
101 res.push(n._pdf(x[i], log));
102 }
103 return res;
104 } else {
105 throw "Illegal argument: x";
106 }
107 }
108
109 jstat.pnorm = function(q, mean, sd, lower_tail, log) {
110 if(mean == null) mean = 0;
111 if(sd == null) sd = 1;
112 if(lower_tail == null) lower_tail = true;
113 if(log == null) log = false;
114
115 var n = new NormalDistribution(mean, sd);
116 if(!isNaN(q)) {
117 // is a number
118 return n._cdf(q, lower_tail, log);
119 } else if(q.length) {
120 var res = [];
121 for(var i = 0; i < q.length; i++) {
122 res.push(n._cdf(q[i], lower_tail, log));
123 }
124 return res;
125 } else {
126 throw "Illegal argument: x";
127 }
128 }
129
130 jstat.dlnorm = function(x, meanlog, sdlog, log) {
131 if(meanlog == null) meanlog = 0;
132 if(sdlog == null) sdlog = 1;
133 if(log == null) log = false;
134 var n = new LogNormalDistribution(meanlog, sdlog);
135 if(!isNaN(x)) {
136 // is a number
137 return n._pdf(x, log);
138 } else if(x.length) {
139 var res = [];
140 for(var i = 0; i < x.length; i++) {
141 res.push(n._pdf(x[i], log));
142 }
143 return res;
144 } else {
145 throw "Illegal argument: x";
146 }
147 }
148
149 jstat.plnorm = function(q, meanlog, sdlog, lower_tail, log) {
150 if(meanlog == null) meanlog = 0;
151 if(sdlog == null) sdlog = 1;
152 if(lower_tail == null) lower_tail = true;
153 if(log == null) log = false;
154
155 var n = new LogNormalDistribution(meanlog, sdlog);
156 if(!isNaN(q)) {
157 // is a number
158 return n._cdf(q, lower_tail, log);
159 }
160 else if(q.length) {
161 var res = [];
162 for(var i = 0; i < q.length; i++) {
163 res.push(n._cdf(q[i], lower_tail, log));
164 }
165 return res;
166 } else {
167 throw "Illegal argument: x";
168 }
169 }
170
171 jstat.dbeta = function(x, alpha, beta, ncp, log) {
172 if(ncp == null) ncp = 0;
173 if(log == null) log = false;
174 var b = new BetaDistribution(alpha, beta);
175 if(!isNaN(x)) {
176 // is a number
177 return b._pdf(x, log);
178 }
179 else if(x.length) {
180 var res = [];
181 for(var i = 0; i < x.length; i++) {
182 res.push(b._pdf(x[i], log));
183 }
184 return res;
185 } else {
186 throw "Illegal argument: x";
187 }
188 }
189
190 jstat.pbeta = function(q, alpha, beta, ncp, lower_tail, log) {
191 if(ncp == null) ncp = 0;
192 if(log == null) log = false;
193 if(lower_tail == null) lower_tail = true;
194
195 var b = new BetaDistribution(alpha, beta);
196 if(!isNaN(q)) {
197 // is a number
198 return b._cdf(q, lower_tail, log);
199 } else if(q.length) {
200 var res = [];
201 for(var i = 0; i < q.length; i++) {
202 res.push(b._cdf(q[i], lower_tail, log));
203 }
204 return res;
205 }
206 else {
207 throw "Illegal argument: x";
208 }
209 }
210
211 jstat.dgamma = function(x, shape, rate, scale, log) {
212 if(rate == null) rate = 1;
213 if(scale == null) scale = 1/rate;
214 if(log == null) log = false;
215
216 var g = new GammaDistribution(shape, scale);
217 if(!isNaN(x)) {
218 // is a number
219 return g._pdf(x, log);
220 } else if(x.length) {
221 var res = [];
222 for(var i = 0; i < x.length; i++) {
223 res.push(g._pdf(x[i], log));
224 }
225 return res;
226 } else {
227 throw "Illegal argument: x";
228 }
229 }
230
231 jstat.pgamma = function(q, shape, rate, scale, lower_tail, log) {
232 if(rate == null) rate = 1;
233 if(scale == null) scale = 1/rate;
234 if(lower_tail == null) lower_tail = true;
235 if(log == null) log = false;
236
237 var g = new GammaDistribution(shape, scale);
238 if(!isNaN(q)) {
239 // is a number
240 return g._cdf(q, lower_tail, log);
241 } else if(q.length) {
242 var res = [];
243 for(var i = 0; i < q.length; i++) {
244 res.push(g._cdf(q[i], lower_tail, log));
245 }
246 return res;
247 } else {
248 throw "Illegal argument: x";
249 }
250
251 }
252
253 jstat.dt = function(x, df, ncp, log) {
254 if(log == null) log = false;
255
256 var t = new StudentTDistribution(df, ncp);
257 if(!isNaN(x)) {
258 // is a number
259 return t._pdf(x, log);
260 } else if(x.length) {
261 var res = [];
262 for(var i = 0; i < x.length; i++) {
263 res.push(t._pdf(x[i], log));
264 }
265 return res;
266 } else {
267 throw "Illegal argument: x";
268 }
269
270 }
271
272 jstat.pt = function(q, df, ncp, lower_tail, log) {
273 if(lower_tail == null) lower_tail = true;
274 if(log == null) log = false;
275
276 var t = new StudentTDistribution(df, ncp);
277 if(!isNaN(q)) {
278 // is a number
279 return t._cdf(q, lower_tail, log);
280 } else if(q.length) {
281 var res = [];
282 for(var i = 0; i < q.length; i++) {
283 res.push(t._cdf(q[i], lower_tail, log));
284 }
285 return res;
286 } else {
287 throw "Illegal argument: x";
288 }
289
290 }
291
292 jstat.plot = function(x, y, options) {
293 if(x == null) {
294 throw "x is undefined in jstat.plot";
295 }
296 if(y == null) {
297 throw "y is undefined in jstat.plot";
298 }
299 if(x.length != y.length) {
300 throw "x and y lengths differ in jstat.plot";
301 }
302
303 var flotOpt = {
304 series: {
305 lines: {
306
307 },
308 points: {
309
310 }
311 }
312 };
313
314 // combine x & y
315 var series = [];
316 if(x.length == undefined) {
317 // single point
318 series.push([x, y]);
319 flotOpt.series.points.show = true;
320 } else {
321 // array
322 for(var i = 0; i < x.length; i++) {
323 series.push([x[i], y[i]]);
324 }
325 }
326
327 var title = 'jstat graph';
328
329 // configure Flot options
330 if(options != null) {
331 // options = JSON.parse(String(options));
332 if(options.type != null) {
333 if(options.type == 'l') {
334 flotOpt.series.lines.show = true;
335 } else if (options.type == 'p') {
336 flotOpt.series.lines.show = false;
337 flotOpt.series.points.show = true;
338 }
339 }
340 if(options.hover != null) {
341 flotOpt.grid = {
342 hoverable: options.hover
343 }
344 }
345
346 if(options.main != null) {
347 title = options.main;
348 }
349 }
350 var now = new Date();
351 var hash = now.getMilliseconds() * now.getMinutes() + now.getSeconds();
352 $('body').append('<div title="' + title + '" style="display: none;" id="'+ hash +'"><div id="graph-' + hash + '" style="width:95%; height: 95%"></div></div>');
353
354 $('#' + hash).dialog({
355 modal: false,
356 width: 475,
357 height: 475,
358 resizable: true,
359 resize: function() {
360 $.plot($('#graph-' + hash), [series], flotOpt);
361 },
362 open: function(event, ui) {
363 var id = '#graph-' + hash;
364 $.plot($('#graph-' + hash), [series], flotOpt);
365 }
366 })
367 }
368
369 /******************************************************************************/
370 /* Special Functions */
371 /******************************************************************************/
372
373 jstat.log10 = function(arg) {
374 return Math.log(arg) / Math.LN10;
375 }
376
377 /*
378 *
379 */
380 jstat.toSigFig = function(num, n) {
381 if(num == 0) {
382 return 0;
383 }
384 var d = Math.ceil(jstat.log10(num < 0 ? -num: num));
385 var power = n - parseInt(d);
386 var magnitude = Math.pow(10,power);
387 var shifted = Math.round(num*magnitude);
388 return shifted/magnitude;
389 }
390
391 jstat.trunc = function(x) {
392 return (x > 0) ? Math.floor(x) : Math.ceil(x);
393 }
394
395 /**
396 * Tests whether x is a finite number
397 */
398 jstat.isFinite = function(x) {
399 return (!isNaN(x) && (x != Number.POSITIVE_INFINITY) && (x != Number.NEGATIVE_INFINITY));
400 }
401
402 /**
403 * dopois_raw() computes the Poisson probability lb^x exp(-lb) / x!.
404 * This does not check that x is an integer, since dgamma() may
405 * call this with a fractional x argument. Any necessary argument
406 * checks should be done in the calling function.
407 */
408 jstat.dopois_raw = function(x, lambda, give_log) {
409 /* x >= 0 ; integer for dpois(), but not e.g. for pgamma()!
410 lambda >= 0
411 */
412 if (lambda == 0) {
413 if(x == 0) {
414 return(give_log) ? 0.0 : 1.0; //R_D__1
415 }
416 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
417 }
418 if (!jstat.isFinite(lambda)) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0;
419 if (x < 0) return(give_log) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0
420 if (x <= lambda * jstat.DBL_MIN) {
421 return (give_log) ? -lambda : Math.exp(-lambda); // R_D_exp(-lambda)
422 }
423 if (lambda < x * jstat.DBL_MIN) {
424 var param = -lambda + x*Math.log(lambda) -jstat.lgamma(x+1);
425 return (give_log) ? param : Math.exp(param); // R_D_exp(-lambda + x*log(lambda) -lgammafn(x+1))
426 }
427 var param1 = jstat.TWO_PI * x; // f
428 var param2 = -jstat.stirlerr(x)-jstat.bd0(x,lambda); // x
429 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))
430 //return(R_D_fexp( , -stirlerr(x)-bd0(x,lambda) ));
431 }
432
433 /** Evaluates the "deviance part"
434 * bd0(x,M) := M * D0(x/M) = M*[ x/M * log(x/M) + 1 - (x/M) ] =
435 * = x * log(x/M) + M - x
436 * where M = E[X] = n*p (or = lambda), for x, M > 0
437 *
438 * in a manner that should be stable (with small relative error)
439 * for all x and M=np. In particular for x/np close to 1, direct
440 * evaluation fails, and evaluation is based on the Taylor series
441 * of log((1+v)/(1-v)) with v = (x-np)/(x+np).
442 */
443 jstat.bd0 = function(x, np) {
444 var ej, s, s1, v, j;
445 if(!jstat.isFinite(x) || !jstat.isFinite(np) || np == 0.0) throw "illegal parameter in jstat.bd0";
446
447 if(Math.abs(x-np) > 0.1*(x+np)) {
448 v = (x-np)/(x+np);
449 s = (x-np)*v;/* s using v -- change by MM */
450 ej = 2*x*v;
451 v = v*v;
452 for (j=1; ; j++) { /* Taylor series */
453 ej *= v;
454 s1 = s+ej/((j<<1)+1);
455 if (s1==s) /* last term was effectively 0 */
456 return(s1);
457 s = s1;
458 }
459 }
460 /* else: | x - np | is not too small */
461 return(x*Math.log(x/np)+np-x);
462 }
463
464 /** Computes the log of the error term in Stirling's formula.
465 * For n > 15, uses the series 1/12n - 1/360n^3 + ...
466 * For n <=15, integers or half-integers, uses stored values.
467 * For other n < 15, uses lgamma directly (don't use this to
468 * write lgamma!)
469 */
470 jstat.stirlerr= function(n) {
471 var S0 = 0.083333333333333333333;
472 var S1 = 0.00277777777777777777778;
473 var S2 = 0.00079365079365079365079365;
474 var S3 = 0.000595238095238095238095238;
475 var S4 = 0.0008417508417508417508417508;
476
477 var sferr_halves = [
478 0.0, /* n=0 - wrong, place holder only */
479 0.1534264097200273452913848, /* 0.5 */
480 0.0810614667953272582196702, /* 1.0 */
481 0.0548141210519176538961390, /* 1.5 */
482 0.0413406959554092940938221, /* 2.0 */
483 0.03316287351993628748511048, /* 2.5 */
484 0.02767792568499833914878929, /* 3.0 */
485 0.02374616365629749597132920, /* 3.5 */
486 0.02079067210376509311152277, /* 4.0 */
487 0.01848845053267318523077934, /* 4.5 */
488 0.01664469118982119216319487, /* 5.0 */
489 0.01513497322191737887351255, /* 5.5 */
490 0.01387612882307074799874573, /* 6.0 */
491 0.01281046524292022692424986, /* 6.5 */
492 0.01189670994589177009505572, /* 7.0 */
493 0.01110455975820691732662991, /* 7.5 */
494 0.010411265261972096497478567, /* 8.0 */
495 0.009799416126158803298389475, /* 8.5 */
496 0.009255462182712732917728637, /* 9.0 */
497 0.008768700134139385462952823, /* 9.5 */
498 0.008330563433362871256469318, /* 10.0 */
499 0.007934114564314020547248100, /* 10.5 */
500 0.007573675487951840794972024, /* 11.0 */
501 0.007244554301320383179543912, /* 11.5 */
502 0.006942840107209529865664152, /* 12.0 */
503 0.006665247032707682442354394, /* 12.5 */
504 0.006408994188004207068439631, /* 13.0 */
505 0.006171712263039457647532867, /* 13.5 */
506 0.005951370112758847735624416, /* 14.0 */
507 0.005746216513010115682023589, /* 14.5 */
508 0.005554733551962801371038690 /* 15.0 */
509 ];
510
511 var nn;
512
513 if (n <= 15.0) {
514 nn = n + n;
515 if (nn == parseInt(nn)) return(sferr_halves[parseInt(nn)]);
516 return(jstat.lgamma(n + 1.0) - (n + 0.5)*Math.log(n) + n - jstat.LN_SQRT_2PI);
517 }
518
519 nn = n*n;
520 if (n>500) return((S0-S1/nn)/n);
521 if (n> 80) return((S0-(S1-S2/nn)/nn)/n);
522 if (n> 35) return((S0-(S1-(S2-S3/nn)/nn)/nn)/n);
523 /* 15 < n <= 35 : */
524 return((S0-(S1-(S2-(S3-S4/nn)/nn)/nn)/nn)/n);
525 }
526
527
528
529 /** The function lgamma computes log|gamma(x)|. The function
530 * lgammafn_sign in addition assigns the sign of the gamma function
531 * to the address in the second argument if this is not null.
532 */
533 jstat.lgamma = function(x) {
534 function lgammafn_sign(x, sgn) {
535 var ans, y, sinpiy;
536 var xmax = 2.5327372760800758e+305;
537 var dxrel = 1.490116119384765696e-8;
538
539 // if (xmax == 0) {/* initialize machine dependent constants _ONCE_ */
540 // xmax = jstat.DBL_MAX/Math.log(jstat.DBL_MAX);/* = 2.533 e305 for IEEE double */
541 // dxrel = Math.sqrt(jstat.DBL_EPSILON);/* sqrt(Eps) ~ 1.49 e-8 for IEEE double */
542 // }
543
544 /* For IEEE double precision DBL_EPSILON = 2^-52 = 2.220446049250313e-16 :
545 xmax = DBL_MAX / log(DBL_MAX) = 2^1024 / (1024 * log(2)) = 2^1014 / log(2)
546 dxrel = sqrt(DBL_EPSILON) = 2^-26 = 5^26 * 1e-26 (is *exact* below !)
547 */
548
549 if (sgn != null) sgn = 1;
550
551 if(isNaN(x)) return x;
552
553 if (x < 0 && (Math.floor(-x) % 2.0) == 0)
554 if (sgn != null) sgn = -1;
555
556 if (x <= 0 && x == jstat.trunc(x)) { /* Negative integer argument */
557 console.warn("Negative integer argument in lgammafn_sign");
558 return Number.POSITIVE_INFINITY;/* +Inf, since lgamma(x) = log|gamma(x)| */
559 }
560
561 y = Math.abs(x);
562
563 if(y <= 10) return Math.log(Math.abs(jstat.gamma(x))); // TODO: implement jstat.gamma
564
565 if(y > xmax) {
566 console.warn("Illegal arguement passed to lgammafn_sign");
567 return Number.POSITIVE_INFINITY;
568 }
569
570 if(x > 0) {
571 if(x > 1e17) {
572 return (x*(Math.log(x)-1.0));
573 } else if(x > 4934720.0) {
574 return (jstat.LN_SQRT_2PI + (x-0.5) * Math.log(x) - x);
575 } else {
576 return jstat.LN_SQRT_2PI + (x-0.5) * Math.log(x) - x + jstat.lgammacor(x); // TODO: implement lgammacor
577 }
578 }
579
580 sinpiy = Math.abs(Math.sin(Math.PI * y));
581
582 if(sinpiy == 0) {
583 throw "Should never happen!!";
584 }
585
586 ans = jstat.LN_SQRT_PId2 + (x - 0.5) * Math.log(y) - x - Math.log(sinpiy) - jstat.lgammacor(y);
587
588 if(Math.abs((x-jstat.trunc(x-0.5))* ans / x) < dxrel) {
589 throw "The answer is less than half the precision argument too close to a negative integer";
590 }
591 return ans;
592 }
593
594 return lgammafn_sign(x, null);
595 }
596
597 jstat.gamma = function(x) {
598 var xbig = 171.624;
599 var p = [
600 -1.71618513886549492533811,
601 24.7656508055759199108314,-379.804256470945635097577,
602 629.331155312818442661052,866.966202790413211295064,
603 -31451.2729688483675254357,-36144.4134186911729807069,
604 66456.1438202405440627855
605 ];
606 var q = [
607 -30.8402300119738975254353,
608 315.350626979604161529144,-1015.15636749021914166146,
609 -3107.77167157231109440444,22538.1184209801510330112,
610 4755.84627752788110767815,-134659.959864969306392456,
611 -115132.259675553483497211
612 ];
613 var c = [
614 -.001910444077728,8.4171387781295e-4,
615 -5.952379913043012e-4,7.93650793500350248e-4,
616 -.002777777777777681622553,.08333333333333333331554247,
617 .0057083835261
618 ];
619
620 var i,n,parity,fact,xden,xnum,y,z,yi,res,sum,ysq;
621
622 parity = (0);
623 fact = 1.0;
624 n = 0;
625 y=x;
626 if(y <= 0.0) {
627 /* -------------------------------------------------------------
628 Argument is negative
629 ------------------------------------------------------------- */
630 y = -x;
631 yi = jstat.trunc(y);
632 res = y - yi;
633 if (res != 0.0) {
634 if (yi != jstat.trunc(yi * 0.5) * 2.0)
635 parity = (1);
636 fact = -Math.PI / Math.sin(Math.PI * res);
637 y += 1.0;
638 } else {
639 return(Number.POSITIVE_INFINITY);
640 }
641 }
642 /* -----------------------------------------------------------------
643 Argument is positive
644 -----------------------------------------------------------------*/
645 if (y < jstat.DBL_EPSILON) {
646 /* --------------------------------------------------------------
647 Argument < EPS
648 -------------------------------------------------------------- */
649 if (y >= jstat.DBL_MIN) {
650 res = 1.0 / y;
651 } else {
652 return(Number.POSITIVE_INFINITY);
653 }
654 } else if (y < 12.0) {
655 yi = y;
656 if (y < 1.0) {
657 /* ---------------------------------------------------------
658 EPS < argument < 1
659 --------------------------------------------------------- */
660 z = y;
661 y += 1.0;
662 } else {
663 /* -----------------------------------------------------------
664 1 <= argument < 12, reduce argument if necessary
665 ----------------------------------------------------------- */
666 n = parseInt(y) - 1;
667 y -= parseFloat(n);
668 z = y - 1.0;
669 }
670 /* ---------------------------------------------------------
671 Evaluate approximation for 1. < argument < 2.
672 ---------------------------------------------------------*/
673 xnum = 0.0;
674 xden = 1.0;
675 for (i = 0; i < 8; ++i) {
676 xnum = (xnum + p[i]) * z;
677 xden = xden * z + q[i];
678 }
679 res = xnum / xden + 1.0;
680 if (yi < y) {
681 /* --------------------------------------------------------
682 Adjust result for case 0. < argument < 1.
683 -------------------------------------------------------- */
684 res /= yi;
685 } else if (yi > y) {
686 /* ----------------------------------------------------------
687 Adjust result for case 2. < argument < 12.
688 ---------------------------------------------------------- */
689 for (i = 0; i < n; ++i) {
690 res *= y;
691 y += 1.0;
692 }
693 }
694 } else {
695 /* -------------------------------------------------------------
696 Evaluate for argument >= 12.,
697 ------------------------------------------------------------- */
698 if (y <= xbig) {
699 ysq = y * y;
700 sum = c[6];
701 for (i = 0; i < 6; ++i) {
702 sum = sum / ysq + c[i];
703 }
704 sum = sum / y - y + jstat.LN_SQRT_2PI;
705 sum += (y - 0.5) * Math.log(y);
706 res = Math.exp(sum);
707 } else {
708 return(Number.POSITIVE_INFINITY);
709 }
710 }
711 /* ----------------------------------------------------------------------
712 Final adjustments and return
713 ----------------------------------------------------------------------*/
714 if (parity)
715 res = -res;
716 if (fact != 1.0)
717 res = fact / res;
718 return res;
719 }
720
721 /** Compute the log gamma correction factor for x >= 10 so that
722 *
723 * log(gamma(x)) = .5*log(2*pi) + (x-.5)*log(x) -x + lgammacor(x)
724 *
725 * [ lgammacor(x) is called Del(x) in other contexts (e.g. dcdflib)]
726 */
727 jstat.lgammacor = function(x) {
728 var algmcs = [
729 +.1666389480451863247205729650822e+0,
730 -.1384948176067563840732986059135e-4,
731 +.9810825646924729426157171547487e-8,
732 -.1809129475572494194263306266719e-10,
733 +.6221098041892605227126015543416e-13,
734 -.3399615005417721944303330599666e-15,
735 +.2683181998482698748957538846666e-17,
736 -.2868042435334643284144622399999e-19,
737 +.3962837061046434803679306666666e-21,
738 -.6831888753985766870111999999999e-23,
739 +.1429227355942498147573333333333e-24,
740 -.3547598158101070547199999999999e-26,
741 +.1025680058010470912000000000000e-27,
742 -.3401102254316748799999999999999e-29,
743 +.1276642195630062933333333333333e-30
744 ];
745
746 var tmp;
747 var nalgm = 5;
748 var xbig = 94906265.62425156;
749 var xmax = 3.745194030963158e306;
750
751 if(x < 10) {
752 return Number.NaN;
753 } else if (x >= xmax) {
754 throw "Underflow error in lgammacor";
755 } else if (x < xbig) {
756 tmp = 10 / x;
757 return jstat.chebyshev(tmp*tmp*2-1,algmcs,nalgm) / x;
758 }
759 return 1 / (x*12);
760 }
761
762 /*
763 * Incomplete Beta function
764 */
765 jstat.incompleteBeta = function(a, b, x) {
766 /*
767 * Used by incompleteBeta: Evaluates continued fraction for incomplete
768 * beta function by modified Lentz's method.
769 */
770 function betacf(a, b, x) {
771 var MAXIT = 100;
772 var EPS = 3.0e-12;
773 var FPMIN = 1.0e-30;
774
775 var m,m2,aa,c,d,del,h,qab,qam,qap;
776
777 qab=a+b;
778 qap=a+1.0;
779 qam=a-1.0;
780 c=1.0;
781 d=1.0-qab*x/qap;
782
783 if(Math.abs(d) < FPMIN) {
784 d=FPMIN;
785 }
786
787 d = 1.0/d;
788 h=d;
789 for(m = 1; m <= MAXIT; m++) {
790 m2=2*m;
791 aa=m*(b-m)*x/((qam+m2)*(a+m2));
792 d=1.0+aa*d;
793 if(Math.abs(d) < FPMIN) {
794 d = FPMIN;
795 }
796 c=1.0+aa/c;
797 if(Math.abs(c) < FPMIN) {
798 c = FPMIN;
799 }
800 d=1.0/d;
801 h *= d*c;
802 aa = -(a+m)*(qab+m)*x/((a+m2) * (qap+m2));
803 d=1.0+aa*d;
804 if(Math.abs(d) < FPMIN) {
805 d = FPMIN;
806 }
807 c=1.0+aa/c;
808 if(Math.abs(c) < FPMIN) {
809 c=FPMIN;
810 }
811 d=1.0/d;
812 del=d*c;
813 h *= del;
814 if(Math.abs(del-1.0) < EPS) {
815 // are we done?
816 break;
817 }
818 }
819 if(m > MAXIT) {
820 console.warn("a or b too big, or MAXIT too small in betacf: " + a + ", " + b + ", " + x + ", " + h);
821 return h;
822 }
823 if(isNaN(h)) {
824 console.warn(a + ", " + b + ", " + x);
825 }
826 return h;
827 }
828
829 var bt;
830
831 if(x < 0.0 || x > 1.0) {
832 throw "bad x in routine incompleteBeta";
833 }
834 if(x == 0.0 || x == 1.0) {
835 bt = 0.0;
836 } else {
837 bt = Math.exp(jstat.lgamma(a+b) - jstat.lgamma(a) - jstat.lgamma(b) + a * Math.log(x)+ b * Math.log(1.0-x));
838 }
839 if(x < (a + 1.0)/(a+b+2.0)) {
840 return bt * betacf(a,b,x)/a;
841 } else {
842 return 1.0-bt*betacf(b,a,1.0-x)/b;
843 }
844 }
845
846 /** Evaluates the n-term Chebyshev series
847 * "a" at "x".
848 */
849 jstat.chebyshev = function(x, a, n) {
850 var b0, b1, b2, twox;
851 var i;
852
853 if (n < 1 || n > 1000) return Number.NaN;
854
855 if (x < -1.1 || x > 1.1) return Number.NaN;
856
857 twox = x * 2;
858 b2 = b1 = 0;
859 b0 = 0;
860 for (i = 1; i <= n; i++) {
861 b2 = b1;
862 b1 = b0;
863 b0 = twox * b1 - b2 + a[n - i];
864 }
865 return (b0 - b2) * 0.5;
866 }
867
868 jstat.fmin2 = function(x, y) {
869 return (x < y) ? x : y;
870 }
871
872 jstat.log1p = function(x) {
873 // http://kevin.vanzonneveld.net
874 // + original by: Brett Zamir (http://brett-zamir.me)
875 // % note 1: Precision 'n' can be adjusted as desired
876 // * example 1: log1p(1e-15);
877 // * returns 1: 9.999999999999995e-16
878
879 var ret = 0,
880 n = 50; // degree of precision
881 if (x <= -1) {
882 return Number.NEGATIVE_INFINITY; // JavaScript style would be to return Number.NEGATIVE_INFINITY
883 }
884 if (x < 0 || x > 1) {
885 return Math.log(1 + x);
886 }
887 for (var i = 1; i < n; i++) {
888 if ((i % 2) === 0) {
889 ret -= Math.pow(x, i) / i;
890 } else {
891 ret += Math.pow(x, i) / i;
892 }
893 }
894 return ret;
895 }
896
897 jstat.expm1 = function(x) {
898 var y, a = Math.abs(x);
899 if(a < jstat.DBL_EPSILON) return x;
900 if(a > 0.697) return Math.exp(x) - 1; /* negligable cancellation */
901 if(a > 1e-8) {
902 y = Math.exp(x) - 1;
903 } else {
904 y = (x / 2 + 1) * x;
905 }
906
907 /* Newton step for solving log(1 + y) = x for y : */
908 /* WARNING: does not work for y ~ -1: bug in 1.5.0 */
909 y -= (1 + y) * (jstat.log1p(y) - x);
910 return y;
911 }
912
913 jstat.logBeta = function(a, b) {
914 var corr, p, q;
915 p = q = a;
916 if(b < p) p = b;/* := min(a,b) */
917 if(b > q) q = b;/* := max(a,b) */
918
919 /* both arguments must be >= 0 */
920 if (p < 0) {
921 console.warn('Both arguements must be >= 0');
922 return Number.NaN;
923 }
924 else if (p == 0) {
925 return Number.POSITIVE_INFINITY;
926 }
927 else if (!jstat.isFinite(q)) { /* q == +Inf */
928 return Number.NEGATIVE_INFINITY;
929 }
930
931 if (p >= 10) {
932 /* p and q are big. */
933 corr = jstat.lgammacor(p) + jstat.lgammacor(q) - jstat.lgammacor(p + q);
934 return Math.log(q) * -0.5 + jstat.LN_SQRT_2PI + corr
935 + (p - 0.5) * Math.log(p / (p + q)) + q * jstat.log1p(-p / (p + q));
936 }
937 else if (q >= 10) {
938 /* p is small, but q is big. */
939 corr = jstat.lgammacor(q) - jstat.lgammacor(p + q);
940 return jstat.lgamma(p) + corr + p - p * Math.log(p + q)
941 + (q - 0.5) * jstat.log1p(-p / (p + q));
942 }
943 else
944 /* p and q are small: p <= q < 10. */
945 return Math.log(jstat.gamma(p) * (jstat.gamma(q) / jstat.gamma(p + q)));
946 }
947
948 jstat.dbinom_raw = function(x, n, p, q, give_log) {
949 if(give_log == null) give_log = false;
950 var lf, lc;
951
952 if(p == 0) {
953 if(x == 0) {
954 // R_D__1
955 return (give_log) ? 0.0 : 1.0;
956 } else {
957 // R_D__0
958 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
959 }
960 }
961 if(q == 0) {
962 if(x == n) {
963 // R_D__1
964 return (give_log) ? 0.0 : 1.0;
965 } else {
966 // R_D__0
967 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
968 }
969 }
970
971 if (x == 0) {
972 if(n == 0) return (give_log) ? 0.0 : 1.0; //R_D__1;
973 lc = (p < 0.1) ? -jstat.bd0(n,n*q) - n*p : n*Math.log(q);
974 return ( give_log ) ? lc : Math.exp(lc); //R_D_exp(lc)
975 }
976
977 if (x == n) {
978 lc = (q < 0.1) ? -jstat.bd0(n,n*p) - n*q : n*Math.log(p);
979 return ( give_log ) ? lc : Math.exp(lc); //R_D_exp(lc)
980 }
981
982 if (x < 0 || x > n) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0;
983
984 /* n*p or n*q can underflow to zero if n and p or q are small. This
985 used to occur in dbeta, and gives NaN as from R 2.3.0. */
986 lc = jstat.stirlerr(n) - jstat.stirlerr(x) - jstat.stirlerr(n-x) - jstat.bd0(x,n*p) - jstat.bd0(n-x,n*q);
987
988 /* f = (M_2PI*x*(n-x))/n; could overflow or underflow */
989 /* Upto R 2.7.1:
990 * lf = log(M_2PI) + log(x) + log(n-x) - log(n);
991 * -- following is much better for x << n : */
992 lf = Math.log(jstat.TWO_PI) + Math.log(x) + jstat.log1p(- x/n);
993
994 return (give_log) ? lc - 0.5*lf : Math.exp(lc - 0.5*lf); // R_D_exp(lc - 0.5*lf);
995 }
996
997 jstat.max = function(values) {
998 var max = Number.NEGATIVE_INFINITY;
999 for(var i = 0; i < values.length; i++) {
1000 if(values[i] > max) {
1001 max = values[i];
1002 }
1003 }
1004 return max;
1005 }
1006
1007 /******************************************************************************/
1008 /* Probability Distributions */
1009 /******************************************************************************/
1010
1011 /**
1012 * Range class
1013 */
1014 var Range = Class.extend({
1015 init: function(min, max, numPoints) {
1016 this._minimum = parseFloat(min);
1017 this._maximum = parseFloat(max);
1018 this._numPoints = parseFloat(numPoints);
1019 },
1020 getMinimum: function() {
1021 return this._minimum;
1022 },
1023 getMaximum: function() {
1024 return this._maximum;
1025 },
1026 getNumPoints: function() {
1027 return this._numPoints;
1028 },
1029 getPoints: function() {
1030 var results = [];
1031 var x = this._minimum;
1032 var step = (this._maximum-this._minimum)/(this._numPoints-1);
1033 for(var i = 0; i < this._numPoints; i++) {
1034 results[i] = parseFloat(x.toFixed(6));
1035 x += step;
1036 }
1037 return results;
1038 }
1039 });
1040
1041 Range.validate = function(range) {
1042 if( ! range instanceof Range) {
1043 return false;
1044 }
1045 if(isNaN(range.getMinimum()) || isNaN(range.getMaximum()) || isNaN(range.getNumPoints()) || range.getMaximum() < range.getMinimum() || range.getNumPoints() <= 0) {
1046 return false;
1047 }
1048 return true;
1049 }
1050
1051 var ContinuousDistribution = Class.extend({
1052 init: function(name) {
1053 this._name = name;
1054 },
1055 toString: function() {
1056 return this._string;
1057 },
1058 getName: function() {
1059 return this._name;
1060 },
1061 getClassName: function() {
1062 return this._name + 'Distribution';
1063 },
1064 density: function(valueOrRange) {
1065 if(!isNaN(valueOrRange)) {
1066 // single value
1067 return parseFloat(this._pdf(valueOrRange).toFixed(15));
1068 } else if (Range.validate(valueOrRange)) {
1069 // multiple values
1070 var points = valueOrRange.getPoints();
1071
1072 var result = [];
1073 // For each point in the range
1074 for(var i = 0; i < points.length; i++) {
1075 result[i] = parseFloat(this._pdf(points[i]));
1076 }
1077 return result;
1078 } else {
1079 // neither value or range
1080 throw "Invalid parameter supplied to " + this.getClassName() + ".density()";
1081 }
1082 },
1083 cumulativeDensity: function(valueOrRange) {
1084 if(!isNaN(valueOrRange)) {
1085 // single value
1086 return parseFloat(this._cdf(valueOrRange).toFixed(15));
1087 } else if (Range.validate(valueOrRange)) {
1088 // multiple values
1089 var points = valueOrRange.getPoints();
1090 var result = [];
1091 // For each point in the range
1092 for(var i = 0; i < points.length; i++) {
1093 result[i] = parseFloat(this._cdf(points[i]));
1094 }
1095 return result;
1096 } else {
1097 // neither value or range
1098 throw "Invalid parameter supplied to " + this.getClassName() + ".cumulativeDensity()";
1099 }
1100 },
1101 getRange: function(standardDeviations, numPoints) {
1102 if(standardDeviations == null) {
1103 standardDeviations = 5;
1104 }
1105 if(numPoints == null) {
1106 numPoints = 100;
1107 }
1108 var min = this.getMean() - standardDeviations * Math.sqrt(this.getVariance());
1109 var max = this.getMean() + standardDeviations * Math.sqrt(this.getVariance());
1110
1111 if(this.getClassName() == 'GammaDistribution' || this.getClassName() == 'LogNormalDistribution') {
1112 min = 0.0;
1113 max = this.getMean() + standardDeviations * Math.sqrt(this.getVariance());
1114 } else if(this.getClassName() == 'BetaDistribution') {
1115 min = 0.0;
1116 max = 1.0;
1117 }
1118
1119
1120 var range = new Range(min, max, numPoints);
1121 return range;
1122 },
1123 getVariance: function(){},
1124 getMean: function(){},
1125 getQuantile: function(p) {
1126 var self = this;
1127 /*
1128 * Recursive function to find the closest match
1129 */
1130 function findClosestMatch(range, p) {
1131 var ERR = 1.0e-5;
1132 var xs = range.getPoints();
1133 var closestIndex = 0;
1134 var closestDistance = 999;
1135
1136 for(var i=0; i<xs.length; i++) {
1137 var pp = self.cumulativeDensity(xs[i]);
1138 var distance = Math.abs(pp - p);
1139 if(distance < closestDistance) {
1140 // closer value found
1141 closestIndex = i;
1142 closestDistance = distance;
1143 }
1144 }
1145 if(closestDistance <= ERR) {
1146 // Acceptable - return value;
1147 return xs[closestIndex];
1148 } else {
1149 // Calculate the new range
1150 var newRange = new Range(xs[closestIndex-1], xs[closestIndex+1],20);
1151 return findClosestMatch(newRange, p);
1152 }
1153 }
1154 var range = this.getRange(5, 20);
1155 return findClosestMatch(range, p);
1156 }
1157 });
1158
1159 /**
1160 * A normal distribution object
1161 */
1162 var NormalDistribution = ContinuousDistribution.extend({
1163 init: function(mean, sigma) {
1164 this._super('Normal');
1165 this._mean = parseFloat(mean);
1166 this._sigma = parseFloat(sigma);
1167 this._string = "Normal ("+this._mean.toFixed(2)+", " + this._sigma.toFixed(2) + ")";
1168 },
1169 _pdf: function(x, give_log) {
1170 if(give_log == null) {
1171 give_log=false;
1172 } // default is false;
1173 var sigma = this._sigma;
1174 var mu = this._mean;
1175 if(!jstat.isFinite(sigma)) {
1176 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0
1177 }
1178 if(!jstat.isFinite(x) && mu == x) {
1179 return Number.NaN;
1180 }
1181 if(sigma<=0) {
1182 if(sigma < 0) {
1183 throw "invalid sigma in _pdf";
1184 }
1185 return (x==mu)?Number.POSITIVE_INFINITY:(give_log)?Number.NEGATIVE_INFINITY:0.0;
1186 }
1187 x=(x-mu)/sigma;
1188 if(!jstat.isFinite(x)){
1189 return (give_log)?Number.NEGATIVE_INFINITY:0.0;
1190 }
1191 return (give_log ? -(jstat.LN_SQRT_2PI + 0.5 * x * x + Math.log(sigma)) :
1192 jstat.ONE_SQRT_2PI * Math.exp(-0.5 * x * x) / sigma);
1193 },
1194 _cdf: function(x, lower_tail, log_p) {
1195
1196 if(lower_tail == null) lower_tail = true;
1197 if(log_p == null) log_p = false;
1198
1199 function pnorm_both(x, cum, ccum, i_tail, log_p) {
1200 /* i_tail in {0,1,2} means: "lower", "upper", or "both" :
1201 if(lower) return *cum := P[X <= x]
1202 if(upper) return *ccum := P[X > x] = 1 - P[X <= x]
1203 */
1204
1205 var a = [
1206 2.2352520354606839287,
1207 161.02823106855587881,
1208 1067.6894854603709582,
1209 18154.981253343561249,
1210 0.065682337918207449113
1211 ];
1212 var b = [
1213 47.20258190468824187,
1214 976.09855173777669322,
1215 10260.932208618978205,
1216 45507.789335026729956
1217 ];
1218 var c = [
1219 0.39894151208813466764,
1220 8.8831497943883759412,
1221 93.506656132177855979,
1222 597.27027639480026226,
1223 2494.5375852903726711,
1224 6848.1904505362823326,
1225 11602.651437647350124,
1226 9842.7148383839780218,
1227 1.0765576773720192317e-8
1228 ];
1229 var d = [
1230 22.266688044328115691,
1231 235.38790178262499861,
1232 1519.377599407554805,
1233 6485.558298266760755,
1234 18615.571640885098091,
1235 34900.952721145977266,
1236 38912.003286093271411,
1237 19685.429676859990727
1238 ];
1239 var p = [
1240 0.21589853405795699,
1241 0.1274011611602473639,
1242 0.022235277870649807,
1243 0.001421619193227893466,
1244 2.9112874951168792e-5,
1245 0.02307344176494017303
1246 ];
1247 var q = [
1248 1.28426009614491121,
1249 0.468238212480865118,
1250 0.0659881378689285515,
1251 0.00378239633202758244,
1252 7.29751555083966205e-5
1253 ];
1254
1255 var xden, xnum, temp, del, eps, xsq, y, i, lower, upper;
1256
1257 /* Consider changing these : */
1258 eps = jstat.DBL_EPSILON * 0.5;
1259
1260 /* i_tail in {0,1,2} =^= {lower, upper, both} */
1261 lower = i_tail != 1;
1262 upper = i_tail != 0;
1263
1264 y = Math.abs(x);
1265
1266 if (y <= 0.67448975) { /* qnorm(3/4) = .6744.... -- earlier had 0.66291 */
1267 if (y > eps) {
1268 xsq = x * x;
1269 xnum = a[4] * xsq;
1270 xden = xsq;
1271 for (i = 0; i < 3; ++i) {
1272 xnum = (xnum + a[i]) * xsq;
1273 xden = (xden + b[i]) * xsq;
1274 }
1275 } else {
1276 xnum = xden = 0.0;
1277 }
1278 temp = x * (xnum + a[3]) / (xden + b[3]);
1279 if(lower) cum = 0.5 + temp;
1280 if(upper) ccum = 0.5 - temp;
1281 if(log_p) {
1282 if(lower) cum = Math.log(cum);
1283 if(upper) ccum = Math.log(ccum);
1284 }
1285
1286 } else if (y <= jstat.SQRT_32) {
1287 /* Evaluate pnorm for 0.674.. = qnorm(3/4) < |x| <= sqrt(32) ~= 5.657 */
1288
1289 xnum = c[8] * y;
1290 xden = y;
1291 for (i = 0; i < 7; ++i) {
1292 xnum = (xnum + c[i]) * y;
1293 xden = (xden + d[i]) * y;
1294 }
1295 temp = (xnum + c[7]) / (xden + d[7]);
1296
1297 /* do_del */
1298 xsq = jstat.trunc(x * 16) / 16;
1299 del = (x - xsq) * (x + xsq);
1300 if(log_p) {
1301 cum = (-xsq * xsq * 0.5) + (-del * 0.5) + Math.log(temp);
1302 if((lower && x > 0.) || (upper && x <= 0.))
1303 ccum = jstat.log1p(-Math.exp(-xsq * xsq * 0.5) *
1304 Math.exp(-del * 0.5) * temp);
1305 }
1306 else {
1307 cum = Math.exp(-xsq * xsq * 0.5) * Math.exp(-del * 0.5) * temp;
1308 ccum = 1.0 - cum;
1309 }
1310 /* end do_del */
1311
1312 /* swap_tail */
1313 if (x > 0.0) {/* swap ccum <--> cum */
1314 temp = cum;
1315 if(lower) {
1316 cum = ccum;
1317
1318 }
1319 ccum = temp;
1320 }
1321 /* end swap_tail */
1322
1323 }
1324 /* else |x| > sqrt(32) = 5.657 :
1325 * the next two case differentiations were really for lower=T, log=F
1326 * Particularly *not* for log_p !
1327
1328 * Cody had (-37.5193 < x && x < 8.2924) ; R originally had y < 50
1329 *
1330 * Note that we do want symmetry(0), lower/upper -> hence use y
1331 */
1332
1333 else if((log_p && y < 1e170)|| (lower && -37.5193 < x && x < 8.2924)
1334 || (upper && -8.2924 < x && x < 37.5193)) {
1335 /* Evaluate pnorm for x in (-37.5, -5.657) union (5.657, 37.5) */
1336 xsq = 1.0 / (x * x); /* (1./x)*(1./x) might be better */
1337 xnum = p[5] * xsq;
1338 xden = xsq;
1339 for (i = 0; i < 4; ++i) {
1340 xnum = (xnum + p[i]) * xsq;
1341 xden = (xden + q[i]) * xsq;
1342 }
1343 temp = xsq * (xnum + p[4]) / (xden + q[4]);
1344 temp = (jstat.ONE_SQRT_2PI - temp) / y;
1345
1346 /* do_del */
1347 xsq = jstat.trunc(x * 16) / 16;
1348 del = (x - xsq) * (x + xsq);
1349 if(log_p) {
1350 cum = (-xsq * xsq * 0.5) + (-del * 0.5) + Math.log(temp);
1351 if((lower && x > 0.) || (upper && x <= 0.))
1352 ccum = jstat.log1p(-Math.exp(-xsq * xsq * 0.5) *
1353 Math.exp(-del * 0.5) * temp);
1354 }
1355 else {
1356 cum = Math.exp(-xsq * xsq * 0.5) * Math.exp(-del * 0.5) * temp;
1357 ccum = 1.0 - cum;
1358 }
1359 /* end do_del */
1360
1361 /* swap_tail */
1362 if (x > 0.0) {/* swap ccum <--> cum */
1363 temp = cum;
1364 if(lower) {
1365 cum = ccum;
1366
1367 }
1368 ccum = temp;
1369 }
1370 /* end swap_tail */
1371
1372 } else { /* large x such that probs are 0 or 1 */
1373 if(x > 0) {
1374 cum = (log_p) ? 0.0 : 1.0; // R_D__1
1375 ccum = (log_p) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0;
1376 } else {
1377 cum = (log_p) ? Number.NEGATIVE_INFINITY : 0.0; //R_D__0;
1378 ccum = (log_p) ? 0.0 : 1.0; // R_D__1
1379 }
1380 }
1381
1382 return [cum, ccum];
1383 }
1384
1385 var p, cp;
1386 var mu = this._mean;
1387 var sigma = this._sigma;
1388 var R_DT_0, R_DT_1;
1389
1390 if(lower_tail) {
1391 if(log_p) {
1392 R_DT_0 = Number.NEGATIVE_INFINITY;
1393 R_DT_1 = 0.0;
1394 } else {
1395 R_DT_0 = 0.0;
1396 R_DT_1 = 1.0;
1397 }
1398 } else {
1399 if(log_p) {
1400 R_DT_0 = 0.0;
1401 R_DT_1 = Number.NEGATIVE_INFINITY;
1402 } else {
1403 R_DT_0 = 1.0;
1404 R_DT_1 = 0.0;
1405 }
1406 }
1407
1408 if(!jstat.isFinite(x) && mu == x) return Number.NaN;
1409 if(sigma <= 0) {
1410 if(sigma < 0) {
1411 console.warn("Sigma is less than 0");
1412 return Number.NaN;
1413 }
1414 return (x < mu) ? R_DT_0 : R_DT_1;
1415 }
1416
1417 p = (x - mu) / sigma;
1418
1419 if(!jstat.isFinite(p)) {
1420 return (x < mu) ? R_DT_0 : R_DT_1;
1421 }
1422
1423 x = p;
1424
1425 // pnorm_both(x, &p, &cp, (lower_tail ? 0 : 1), log_p);
1426 // result[0] == &p
1427 // result[1] == &cp
1428
1429 var result = pnorm_both(x, p, cp, (lower_tail ? false : true), log_p);
1430
1431 return (lower_tail ? result[0] : result[1]);
1432
1433 },
1434 getMean: function() {
1435 return this._mean;
1436 },
1437 getSigma: function() {
1438 return this._sigma;
1439 },
1440 getVariance: function() {
1441 return this._sigma*this._sigma;
1442 }
1443 });
1444
1445 /**
1446 * A Log-normal distribution object
1447 */
1448 var LogNormalDistribution = ContinuousDistribution.extend({
1449 init: function(location, scale) {
1450 this._super('LogNormal')
1451 this._location = parseFloat(location);
1452 this._scale = parseFloat(scale);
1453 this._string = "LogNormal ("+this._location.toFixed(2)+", " + this._scale.toFixed(2) + ")";
1454 },
1455 _pdf: function(x, give_log) {
1456 var y;
1457 var sdlog = this._scale;
1458 var meanlog = this._location;
1459 if(give_log == null) {
1460 give_log = false;
1461 }
1462
1463 if(sdlog <= 0) throw "Illegal parameter in _pdf";
1464
1465 if(x <= 0) {
1466 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
1467 }
1468
1469 y = (Math.log(x) - meanlog) / sdlog;
1470
1471 return (give_log ? -(jstat.LN_SQRT_2PI + 0.5 * y * y + Math.log(x * sdlog)) :
1472 jstat.ONE_SQRT_2PI * Math.exp(-0.5 * y * y) / (x * sdlog));
1473
1474 },
1475 _cdf: function(x, lower_tail, log_p) {
1476 var sdlog = this._scale;
1477 var meanlog = this._location;
1478 if(lower_tail == null) {
1479 lower_tail = true;
1480 }
1481 if(log_p == null) {
1482 log_p = false;
1483 }
1484
1485
1486 if(sdlog <= 0) {
1487 throw "illegal std in _cdf";
1488 }
1489
1490 if(x > 0) {
1491 var nd = new NormalDistribution(meanlog, sdlog);
1492 return nd._cdf(Math.log(x), lower_tail, log_p);
1493 }
1494 if(lower_tail) {
1495 return (log_p) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
1496 } else {
1497 return (log_p) ? 0.0 : 1.0; // R_D__1
1498 }
1499 },
1500 getLocation: function() {
1501 return this._location;
1502 },
1503 getScale: function() {
1504 return this._scale;
1505 },
1506 getMean: function() {
1507 return Math.exp((this._location + this._scale) / 2);
1508 },
1509 getVariance: function() {
1510 var ans = (Math.exp(this._scale)-1)*Math.exp(2*this._location+this._scale);
1511 return ans;
1512 }
1513 });
1514
1515
1516 /**
1517 * Gamma distribution object
1518 */
1519 var GammaDistribution = ContinuousDistribution.extend({
1520 init: function(shape, scale) {
1521 this._super('Gamma');
1522 this._shape = parseFloat(shape);
1523 this._scale = parseFloat(scale);
1524 this._string = "Gamma ("+this._shape.toFixed(2)+", " + this._scale.toFixed(2) + ")";
1525 },
1526 _pdf: function(x, give_log) {
1527 var pr;
1528 var shape = this._shape;
1529 var scale = this._scale;
1530 if(give_log == null) {
1531 give_log = false; // default value
1532 }
1533
1534 if(shape < 0 || scale <= 0) {
1535 throw "Illegal argument in _pdf";
1536 }
1537
1538 if(x < 0) {
1539 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
1540 }
1541 if(shape == 0) { /* point mass at 0 */
1542 return (x == 0) ? Number.POSITIVE_INFINITY : (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
1543 }
1544 if(x == 0) {
1545 if(shape < 1) return Number.POSITIVE_INFINITY;
1546 if(shape > 1) return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0
1547 /* else */
1548 return (give_log) ? -Math.log(scale) : 1/scale;
1549 }
1550
1551 if(shape < 1) {
1552 pr = jstat.dopois_raw(shape, x/scale, give_log);
1553 return give_log ? pr + Math.log(shape/x) : pr*shape/x;
1554 }
1555 /* else shape >= 1 */
1556 pr = jstat.dopois_raw(shape-1, x/scale, give_log);
1557 return give_log ? pr - Math.log(scale) : pr/scale;
1558
1559 },
1560 /**
1561 * This function computes the distribution function for the
1562 * gamma distribution with shape parameter alph and scale parameter
1563 * scale. This is also known as the incomplete gamma function.
1564 * See Abramowitz and Stegun (6.5.1) for example.
1565 */
1566 _cdf: function(x, lower_tail, log_p) {
1567 /* define USE_PNORM */
1568 function USE_PNORM() {
1569 pn1 = Math.sqrt(alph) * 3.0 * (Math.pow(x/alph,1.0/3.0) + 1.0 / (9.0 * alph) - 1.0);
1570 var norm_dist = new NormalDistribution(0.0, 1.0);
1571 return norm_dist._cdf(pn1, lower_tail, log_p);
1572 }
1573
1574 /* Defaults */
1575 if(lower_tail == null) lower_tail = true;
1576 if(log_p == null) log_p = false;
1577 var alph = this._shape;
1578 var scale = this._scale;
1579 var xbig = 1.0e+8;
1580 var xlarge = 1.0e+37;
1581 var alphlimit = 1e5;
1582 var pn1,pn2,pn3,pn4,pn5,pn6,arg,a,b,c,an,osum,sum,n,pearson;
1583
1584 if(alph <= 0. || scale <= 0.) {
1585 console.warn('Invalid gamma params in _cdf');
1586 return Number.NaN;
1587 }
1588
1589 x/=scale;
1590 if(isNaN(x)) return x;
1591 if(x <= 0.0) {
1592 // R_DT_0
1593 if(lower_tail) {
1594 // R_D__0
1595 return (log_p) ? Number.NEGATIVE_INFINITY : 0.0;
1596 } else {
1597 // R_D__1
1598 return (log_p) ? 0.0 : 1.0;
1599 }
1600 }
1601
1602 if(alph > alphlimit) {
1603 return USE_PNORM();
1604 }
1605
1606 if(x > xbig * alph) {
1607 if(x > jstat.DBL_MAX * alph) {
1608 // R_DT_1
1609 if(lower_tail) {
1610 // R_D__1
1611 return (log_p) ? 0.0 : 1.0;
1612 } else {
1613 // R_D__0
1614 return (log_p) ? Number.NEGATIVE_INFINITY : 0.0;
1615 }
1616 } else {
1617 return USE_PNORM();
1618 }
1619 }
1620
1621 if(x <= 1.0 || x < alph) {
1622 pearson = 1; /* use pearson's series expansion */
1623 arg = alph * Math.log(x) - x - jstat.lgamma(alph + 1.0);
1624
1625 c = 1.0;
1626 sum = 1.0;
1627 a = alph;
1628 do {
1629 a += 1.0;
1630 c *= x / a;
1631 sum += c;
1632 } while(c > jstat.DBL_EPSILON * sum);
1633 } else { /* x >= max( 1, alph) */
1634 pearson = 0;/* use a continued fraction expansion */
1635 arg = alph * Math.log(x) - x - jstat.lgamma(alph);
1636
1637 a = 1. - alph;
1638 b = a + x + 1.;
1639 pn1 = 1.;
1640 pn2 = x;
1641 pn3 = x + 1.;
1642 pn4 = x * b;
1643 sum = pn3 / pn4;
1644
1645 for (n = 1; ; n++) {
1646 a += 1.;/* = n+1 -alph */
1647 b += 2.;/* = 2(n+1)-alph+x */
1648 an = a * n;
1649 pn5 = b * pn3 - an * pn1;
1650 pn6 = b * pn4 - an * pn2;
1651 if (Math.abs(pn6) > 0.) {
1652 osum = sum;
1653 sum = pn5 / pn6;
1654 if (Math.abs(osum - sum) <= jstat.DBL_EPSILON * jstat.fmin2(1.0, sum))
1655 break;
1656 }
1657 pn1 = pn3;
1658 pn2 = pn4;
1659 pn3 = pn5;
1660 pn4 = pn6;
1661
1662 if (Math.abs(pn5) >= xlarge) {
1663 pn1 /= xlarge;
1664 pn2 /= xlarge;
1665 pn3 /= xlarge;
1666 pn4 /= xlarge;
1667 }
1668 }
1669 }
1670 arg += Math.log(sum);
1671 lower_tail = (lower_tail == pearson);
1672
1673 if (log_p && lower_tail)
1674 return(arg);
1675 /* else */
1676 /* sum = exp(arg); and return if(lower_tail) sum else 1-sum : */
1677
1678 if(lower_tail) {
1679 return Math.exp(arg);
1680 } else {
1681 if(log_p) {
1682 // R_Log1_Exp(arg);
1683 return (arg > -Math.LN2) ? Math.log(-jstat.expm1(arg)) : jstat.log1p(-Math.exp(arg));
1684 } else {
1685 return -jstat.expm1(arg);
1686 }
1687 }
1688 },
1689 getShape: function() {
1690 return this._shape;
1691 },
1692 getScale: function() {
1693 return this._scale;
1694 },
1695 getMean: function() {
1696 return this._shape * this._scale;
1697 },
1698 getVariance: function() {
1699 return this._shape*Math.pow(this._scale,2);
1700 }
1701 });
1702
1703 /**
1704 * A Beta distribution object
1705 */
1706 var BetaDistribution = ContinuousDistribution.extend({
1707 init: function(alpha, beta) {
1708 this._super('Beta');
1709 this._alpha = parseFloat(alpha);
1710 this._beta = parseFloat(beta);
1711 this._string = "Beta ("+this._alpha.toFixed(2)+", " + this._beta.toFixed(2) + ")";
1712 },
1713 _pdf: function(x, give_log) {
1714 if(give_log == null) give_log = false; // default;
1715 var a = this._alpha;
1716 var b = this._beta;
1717 var lval;
1718 if(a <= 0 || b <= 0) {
1719 console.warn('Illegal arguments in _pdf');
1720 return Number.NaN;
1721 }
1722 if(x < 0 || x > 1) {
1723 // R_D__0
1724 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
1725 }
1726 if(x == 0) {
1727 if(a > 1) {
1728 // R_D__0
1729 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
1730 }
1731 if(a < 1) {
1732 return Number.POSITIVE_INFINITY;
1733 }
1734 /*a == 1 */ return (give_log) ? Math.log(b) : b; // R_D_val(b)
1735 }
1736 if(x == 1) {
1737 if(b > 1) {
1738 // R_D__0
1739 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0;
1740 }
1741 if(b < 1) {
1742 return Number.POSITIVE_INFINITY;
1743 }
1744 /* b == 1 */ return (give_log) ? Math.log(a) : a; // R_D_val(a)
1745 }
1746 if(a<=2||b<=2) {
1747 lval = (a-1)*Math.log(x) + (b-1)*jstat.log1p(-x) - jstat.logBeta(a, b);
1748 } else {
1749 lval = Math.log(a+b-1) + jstat.dbinom_raw(a-1, a+b-2, x, 1-x, true);
1750 }
1751 //R_D_exp(lval)
1752 return (give_log) ? lval : Math.exp(lval);
1753 },
1754 _cdf: function(x, lower_tail, log_p) {
1755 if(lower_tail == null) lower_tail = true;
1756 if(log_p == null) log_p = false;
1757
1758 var pin = this._alpha;
1759 var qin = this._beta;
1760
1761 if(pin <= 0 || qin <= 0) {
1762 console.warn('Invalid argument in _cdf');
1763 return Number.NaN;
1764 }
1765
1766 if(x <= 0) {
1767 //R_DT_0;
1768 if(lower_tail) {
1769 // R_D__0
1770 return (log_p) ? Number.NEGATIVE_INFINITY : 0.0;
1771 } else {
1772 // R_D__1
1773 return (log_p) ? 0.1 : 1.0;
1774 }
1775 }
1776
1777 if(x >= 1){
1778 // R_DT_1
1779 if(lower_tail) {
1780 // R_D__1
1781 return (log_p) ? 0.1 : 1.0;
1782 } else {
1783 // R_D__0
1784 return (log_p) ? Number.NEGATIVE_INFINITY : 0.0;
1785 }
1786 }
1787
1788 /* else */
1789 return jstat.incompleteBeta(pin, qin, x);
1790 },
1791 getAlpha: function() {
1792 return this._alpha;
1793 },
1794 getBeta: function() {
1795 return this._beta;
1796 },
1797 getMean: function() {
1798 return this._alpha / (this._alpha+ this._beta);
1799 },
1800 getVariance: function() {
1801 var ans = (this._alpha * this._beta) / (Math.pow(this._alpha+this._beta,2)*(this._alpha+this._beta+1));
1802 return ans;
1803 }
1804 });
1805
1806 var StudentTDistribution = ContinuousDistribution.extend({
1807 init: function(degreesOfFreedom, mu) {
1808 this._super('StudentT');
1809 this._dof = parseFloat(degreesOfFreedom);
1810
1811 if(mu != null) {
1812 this._mu = parseFloat(mu);
1813 this._string = "StudentT ("+this._dof.toFixed(2)+", " + this._mu.toFixed(2)+ ")";
1814 } else {
1815 this._mu = 0.0;
1816 this._string = "StudentT ("+this._dof.toFixed(2)+")";
1817 }
1818
1819 },
1820 _pdf: function(x, give_log) {
1821 if(give_log == null) give_log = false;
1822 if(this._mu == null) {
1823 return this._dt(x, give_log);
1824 } else {
1825 var y = this._dnt(x, give_log);
1826 if(y > 1){
1827 console.warn('x:' + x + ', y: ' + y);
1828 }
1829 return y;
1830 }
1831 },
1832 _cdf: function(x, lower_tail, give_log) {
1833 if(lower_tail == null) lower_tail = true;
1834 if(give_log == null) give_log = false;
1835 if(this._mu == null) {
1836 return this._pt(x, lower_tail, give_log);
1837 } else {
1838 return this._pnt(x, lower_tail, give_log);
1839 }
1840 },
1841 _dt: function(x, give_log) {
1842 var t,u;
1843 var n = this._dof;
1844 if (n <= 0){
1845 console.warn('Invalid parameters in _dt');
1846 return Number.NaN;
1847 }
1848 if(!jstat.isFinite(x)) {
1849 return (give_log) ? Number.NEGATIVE_INFINITY : 0.0; // R_D__0;
1850 }
1851
1852 if(!jstat.isFinite(n)) {
1853 var norm = new NormalDistribution(0.0, 1.0);
1854 return norm.density(x, give_log);
1855 }
1856
1857
1858 t = -jstat.bd0(n/2.0,(n+1)/2.0) + jstat.stirlerr((n+1)/2.0) - jstat.stirlerr(n/2.0);
1859 if ( x*x > 0.2*n )
1860 u = Math.log( 1+ x*x/n ) * n/2;
1861 else
1862 u = -jstat.bd0(n/2.0,(n+x*x)/2.0) + x*x/2.0;
1863
1864 var p1 = jstat.TWO_PI *(1+x*x/n);
1865 var p2 = t-u;
1866
1867 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);
1868 },
1869 _dnt: function(x, give_log) {
1870 if(give_log == null) give_log = false;
1871 var df = this._dof;
1872 var ncp = this._mu;
1873 var u;
1874
1875 if(df <= 0.0) {
1876 console.warn("Illegal arguments _dnf");
1877 return Number.NaN;
1878 }
1879 if(ncp == 0.0) {
1880 return this._dt(x, give_log);
1881 }
1882
1883 if(!jstat.isFinite(x)) {
1884 // R_D__0
1885 if(give_log) {
1886 return Number.NEGATIVE_INFINITY;
1887 } else {
1888 return 0.0;
1889 }
1890 }
1891
1892 /* If infinite df then the density is identical to a
1893 normal distribution with mean = ncp. However, the formula
1894 loses a lot of accuracy around df=1e9
1895 */
1896 if(!isFinite(df) || df > 1e8) {
1897 var dist = new NormalDistribution(ncp, 1.);
1898 return dist.density(x, give_log);
1899 }
1900
1901 /* Do calculations on log scale to stabilize */
1902
1903 /* Consider two cases: x ~= 0 or not */
1904 if (Math.abs(x) > Math.sqrt(df * jstat.DBL_EPSILON)) {
1905 var newT = new StudentTDistribution(df+2, ncp);
1906 u = Math.log(df) - Math.log(Math.abs(x)) +
1907 Math.log(Math.abs(newT._pnt(x*Math.sqrt((df+2)/df), true, false) -
1908 this._pnt(x, true, false)));
1909 /* FIXME: the above still suffers from cancellation (but not horribly) */
1910 }
1911 else { /* x ~= 0 : -> same value as for x = 0 */
1912 u = jstat.lgamma((df+1)/2) - jstat.lgamma(df/2)
1913 - .5*(Math.log(Math.PI) + Math.log(df) + ncp*ncp);
1914 }
1915
1916 return (give_log ? u : Math.exp(u));
1917 },
1918 _pt: function(x, lower_tail, log_p) {
1919 if(lower_tail == null) lower_tail = true;
1920 if(log_p == null) log_p = false;
1921 var val, nx;
1922 var n = this._dof;
1923 var DT_0, DT_1;
1924
1925 if(lower_tail) {
1926 if(log_p) {
1927 DT_0 = Number.NEGATIVE_INFINITY;
1928 DT_1 = 1.;
1929 } else {
1930 DT_0 = 0.;
1931 DT_1 = 1.;
1932 }
1933 } else {
1934 if(log_p) {
1935 // not lower_tail but log_p
1936 DT_0 = 0.;
1937 DT_1 = Number.NEGATIVE_INFINITY;
1938 } else {
1939 // not lower_tail and not log_p
1940 DT_0 = 1.;
1941 DT_1 = 0.;
1942 }
1943 }
1944
1945 if(n <= 0.0) {
1946 console.warn("Invalid T distribution _pt");
1947 return Number.NaN;
1948 }
1949 var norm = new NormalDistribution(0,1);
1950 if(!jstat.isFinite(x)) {
1951 return (x < 0) ? DT_0 : DT_1;
1952 }
1953 if(!jstat.isFinite(n)) {
1954 return norm._cdf(x, lower_tail, log_p);
1955 }
1956
1957 if (n > 4e5) { /*-- Fixme(?): test should depend on `n' AND `x' ! */
1958 /* Approx. from Abramowitz & Stegun 26.7.8 (p.949) */
1959 val = 1./(4.*n);
1960 return norm._cdf(x*(1. - val)/sqrt(1. + x*x*2.*val), lower_tail, log_p);
1961 }
1962
1963 nx = 1 + (x/n)*x;
1964 /* FIXME: This test is probably losing rather than gaining precision,
1965 * now that pbeta(*, log_p = TRUE) is much better.
1966 * Note however that a version of this test *is* needed for x*x > D_MAX */
1967 if(nx > 1e100) { /* <==> x*x > 1e100 * n */
1968 /* Danger of underflow. So use Abramowitz & Stegun 26.5.4
1969 pbeta(z, a, b) ~ z^a(1-z)^b / aB(a,b) ~ z^a / aB(a,b),
1970 with z = 1/nx, a = n/2, b= 1/2 :
1971 */
1972 var lval;
1973 lval = -0.5*n*(2*Math.log(Math.abs(x)) - Math.log(n))
1974 - jstat.logBeta(0.5*n, 0.5) - Math.log(0.5*n);
1975 val = log_p ? lval : Math.exp(lval);
1976 } else {
1977 /*
1978 val = (n > x * x)
1979 // ? pbeta (x * x / (n + x * x), 0.5, n / 2., 0, log_p)
1980 // : pbeta (1. / nx, n / 2., 0.5, 1, log_p);
1981 */
1982 if(n > x * x) {
1983 var beta = new BetaDistribution(0.5, n/2.);
1984 return beta._cdf(x*x/ (n + x * x), false, log_p);
1985 } else {
1986 beta = new BetaDistribution(n / 2., 0.5);
1987 return beta._cdf(1. / nx, true, log_p);
1988 }
1989
1990
1991 }
1992
1993 /* Use "1 - v" if lower_tail and x > 0 (but not both):*/
1994 if(x <= 0.)
1995 lower_tail = !lower_tail;
1996
1997 if(log_p) {
1998 if(lower_tail) return jstat.log1p(-0.5*Math.exp(val));
1999 else return val - M_LN2; /* = log(.5* pbeta(....)) */
2000 }
2001 else {
2002 val /= 2.;
2003 if(lower_tail) {
2004 return (0.5 - val + 0.5);
2005 } else {
2006 return val;
2007 }
2008 }
2009 },
2010 _pnt: function(t, lower_tail, log_p) {
2011
2012 var dof = this._dof;
2013 var ncp = this._mu;
2014 var DT_0, DT_1;
2015
2016 if(lower_tail) {
2017 if(log_p) {
2018 DT_0 = Number.NEGATIVE_INFINITY;
2019 DT_1 = 1.;
2020 } else {
2021 DT_0 = 0.;
2022 DT_1 = 1.;
2023 }
2024 } else {
2025 if(log_p) {
2026 // not lower_tail but log_p
2027 DT_0 = 0.;
2028 DT_1 = Number.NEGATIVE_INFINITY;
2029 } else {
2030 // not lower_tail and not log_p
2031 DT_0 = 1.;
2032 DT_1 = 0.;
2033 }
2034 }
2035
2036 var albeta, a, b, del, errbd, lambda, rxb, tt, x;
2037 var geven, godd, p, q, s, tnc, xeven, xodd;
2038 var it, negdel;
2039
2040 /* note - itrmax and errmax may be changed to suit one's needs. */
2041 var ITRMAX = 1000;
2042 var ERRMAX = 1.e-7;
2043
2044 if(dof <= 0.0) {
2045 return Number.NaN;
2046 } else if (dof == 0.0) {
2047 return this._pt(t);
2048 }
2049
2050 if(!jstat.isFinite(t)) {
2051 return (t < 0) ? DT_0 : DT_1;
2052 }
2053 if(t >= 0.) {
2054 negdel = false;
2055 tt = t;
2056 del = ncp;
2057 } else {
2058 /* We deal quickly with left tail if extreme,
2059 since pt(q, df, ncp) <= pt(0, df, ncp) = \Phi(-ncp) */
2060 if(ncp >= 40 && (!log_p || !lower_tail)) {
2061 return DT_0;
2062 }
2063 negdel = true;
2064 tt = -t;
2065 del = -ncp;
2066 }
2067
2068 if(dof > 4e5 || del*del > 2* Math.LN2 * (-(jstat.DBL_MIN_EXP))) {
2069 /*-- 2nd part: if del > 37.62, then p=0 below
2070 FIXME: test should depend on `df', `tt' AND `del' ! */
2071 /* Approx. from Abramowitz & Stegun 26.7.10 (p.949) */
2072 s=1./(4.*dof);
2073 var norm = new NormalDistribution(del, Math.sqrt(1. + tt*tt*2.*s));
2074 var result = norm._cdf(tt*(1.-s), lower_tail != negdel, log_p);
2075 return result;
2076 }
2077
2078 /* initialize twin series */
2079 /* Guenther, J. (1978). Statist. Computn. Simuln. vol.6, 199. */
2080 x = t * t;
2081 rxb = dof/(x + dof);/* := (1 - x) {x below} -- but more accurately */
2082 x = x / (x + dof);/* in [0,1) */
2083 if (x > 0.) {/* <==> t != 0 */
2084 lambda = del * del;
2085 p = .5 * Math.exp(-.5 * lambda);
2086 if(p == 0.) { // underflow!
2087 console.warn("underflow in _pnt");
2088 return DT_0;
2089 }
2090 q = jstat.SQRT_2dPI * p * del;
2091 s = .5 - p;
2092 if(s < 1e-7) {
2093 s = -0.5 * jstat.expm1(-0.5 * lambda);
2094 }
2095 a = .5;
2096 b = .5 * dof;
2097 /* rxb = (1 - x) ^ b [ ~= 1 - b*x for tiny x --> see 'xeven' below]
2098 * where '(1 - x)' =: rxb {accurately!} above */
2099 rxb = Math.pow(rxb, b);
2100 albeta = jstat.LN_SQRT_PI + jstat.lgamma(b) - jstat.lgamma(.5 + b);
2101 /* TODO: change incompleteBeta function to accept lower_tail and p_log */
2102 xodd = jstat.incompleteBeta(a, b, x);
2103 godd = 2. * rxb * Math.exp(a * Math.log(x) - albeta);
2104 tnc = b * x;
2105 xeven = (tnc < jstat.DBL_EPSILON) ? tnc : 1. - rxb;
2106 geven = tnc * rxb;
2107 tnc = p * xodd + q * xeven;
2108
2109 /* repeat until convergence or iteration limit */
2110 for(it = 1; it <= ITRMAX; it++) {
2111 a += 1.;
2112 xodd -= godd;
2113 xeven -= geven;
2114 godd *= x * (a + b - 1.) / a;
2115 geven *= x * (a + b - .5) / (a + .5);
2116 p *= lambda / (2 * it);
2117 q *= lambda / (2 * it + 1);
2118 tnc += p * xodd + q * xeven;
2119 s -= p;
2120 /* R 2.4.0 added test for rounding error here. */
2121 if(s < -1.e-10) { /* happens e.g. for (t,df,ncp)=(40,10,38.5), after 799 it.*/
2122 //console.write("precision error _pnt");
2123 break;
2124 }
2125 if(s <= 0 && it > 1) break;
2126
2127 errbd = 2. * s * (xodd - godd);
2128
2129 if(Math.abs(errbd) < ERRMAX) break;/*convergence*/
2130 }
2131
2132 if(it == ITRMAX) {
2133 throw "Non-convergence _pnt";
2134 }
2135 } else {
2136 tnc = 0.;
2137 }
2138 norm = new NormalDistribution(0,1);
2139 tnc += norm._cdf(-del, /*lower*/true, /*log_p*/ false);
2140
2141 lower_tail = lower_tail != negdel; /* xor */
2142 if(tnc > 1 - 1e-10 && lower_tail) {
2143 //console.warn("precision error _pnt");
2144 }
2145 var res = jstat.fmin2(tnc, 1.);
2146 if(lower_tail) {
2147 if(log_p) {
2148 return Math.log(res);
2149 } else {
2150 return res;
2151 }
2152 } else {
2153 if(log_p) {
2154 return jstat.log1p(-(res));
2155 } else {
2156 return (0.5 - (res) + 0.5);
2157 }
2158 }
2159 },
2160 getDegreesOfFreedom: function() {
2161 return this._dof;
2162 },
2163 getNonCentralityParameter: function() {
2164 return this._mu;
2165 },
2166 getMean: function() {
2167 if(this._dof > 1) {
2168 var ans = (1/2)*Math.log(this._dof/2) + jstat.lgamma((this._dof-1)/2) - jstat.lgamma(this._dof/2)
2169 return Math.exp(ans) * this._mu;
2170 } else {
2171 return Number.NaN;
2172 }
2173 },
2174 getVariance: function() {
2175 if(this._dof > 2) {
2176 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));
2177 return ans;
2178 } else {
2179 return Number.NaN;
2180 }
2181 }
2182 });
2183
2184
2185 /******************************************************************************/
2186 /* jQuery Flot graph objects */
2187 /******************************************************************************/
2188
2189 var Plot = Class.extend({
2190 init: function(id, options) {
2191 this._container = '#' + String(id);
2192 this._plots = [];
2193 this._flotObj = null;
2194 this._locked = false;
2195
2196 if(options != null) {
2197 this._options = options;
2198 } else {
2199 this._options = {
2200 };
2201 }
2202
2203 },
2204 getContainer: function() {
2205 return this._container;
2206 },
2207 getGraph: function() {
2208 return this._flotObj;
2209 },
2210 setData: function(data) {
2211 this._plots = data;
2212 },
2213 clear: function() {
2214 this._plots = [];
2215 //this.render();
2216 },
2217 showLegend: function() {
2218 this._options.legend = {
2219 show: true
2220 }
2221 this.render();
2222 },
2223 hideLegend: function() {
2224 this._options.legend = {
2225 show: false
2226 }
2227 this.render();
2228 },
2229 render: function() {
2230 this._flotObj = null;
2231 this._flotObj = $.plot($(this._container), this._plots, this._options);
2232 }
2233 });
2234
2235 var DistributionPlot = Plot.extend({
2236 init: function(id, distribution, range, options) {
2237 this._super(id, options);
2238 this._showPDF = true;
2239 this._showCDF = false;
2240 this._pdfValues = []; // raw values for pdf
2241 this._cdfValues = []; // raw values for cdf
2242 this._maxY = 1;
2243 this._plotType = 'line'; // line, point, both
2244 this._fill = false;
2245 this._distribution = distribution; // underlying PDF
2246 // Range object for the plot
2247 if(range != null && Range.validate(range)) {
2248 this._range = range;
2249 } else {
2250 this._range = this._distribution.getRange(); // no range supplied, use distribution default
2251 }
2252
2253 // render
2254 if(this._distribution != null) {
2255 this._maxY = this._generateValues(); // create the pdf/cdf values in the ctor
2256 } else {
2257 this._options.xaxis = {
2258 min: range.getMinimum(),
2259 max: range.getMaximum()
2260 }
2261 this._options.yaxis = {
2262 max: 1
2263 }
2264 }
2265
2266
2267
2268 this.render();
2269 },
2270 setHover: function(bool) {
2271 if(bool) {
2272 if(this._options.grid == null) {
2273 this._options.grid = {
2274 hoverable: true,
2275 mouseActiveRadius: 25
2276 }
2277 } else {
2278 this._options.grid.hoverable = true,
2279 this._options.grid.mouseActiveRadius = 25
2280 }
2281 function showTooltip(x, y, contents, color) {
2282 $('<div id="jstat_tooltip">' + contents + '</div>').css( {
2283 position: 'absolute',
2284 display: 'none',
2285 top: y + 15,
2286 'font-size': 'small',
2287 left: x + 5,
2288 border: '1px solid ' + color[1],
2289 color: color[2],
2290 padding: '5px',
2291 'background-color': color[0],
2292 opacity: 0.80
2293 }).appendTo("body").show();
2294 }
2295 var previousPoint = null;
2296 $(this._container).bind("plothover", function(event, pos, item) {
2297 $("#x").text(pos.x.toFixed(2));
2298 $("#y").text(pos.y.toFixed(2));
2299 if (item) {
2300 if (previousPoint != item.datapoint) {
2301 previousPoint = item.datapoint;
2302 $("#jstat_tooltip").remove();
2303 var x = jstat.toSigFig(item.datapoint[0],2), y = jstat.toSigFig(item.datapoint[1], 2);
2304 var text = null;
2305 var color = item.series.color;
2306 if(item.series.label == 'PDF') {
2307 text = "P(" + x + ") = " + y;
2308 color = ["#fee", "#fdd", "#C05F5F"];
2309 } else {
2310 // cdf
2311 text = "F(" + x + ") = " + y;
2312 color = ["#eef", "#ddf", "#4A4AC0"];
2313 }
2314 showTooltip(item.pageX, item.pageY, text, color);
2315 }
2316 }
2317 else {
2318 $("#jstat_tooltip").remove();
2319 previousPoint = null;
2320 }
2321 });
2322 $(this._container).bind("mouseleave", function() {
2323 if($('#jstat_tooltip').is(':visible')) {
2324 $('#jstat_tooltip').remove();
2325 previousPoint = null;
2326 }
2327 });
2328 } else {
2329 // unbind
2330 if(this._options.grid == null) {
2331 this._options.grid = {
2332 hoverable: false
2333 }
2334 } else {
2335 this._options.grid.hoverable = false
2336 }
2337 $(this._container).unbind("plothover");
2338 }
2339
2340 this.render();
2341 },
2342 setType: function(type) {
2343 this._plotType = type;
2344 var lines = {};
2345 var points = {};
2346 if(this._plotType == 'line') {
2347 lines.show = true;
2348 points.show = false;
2349 } else if(this._plotType == 'points') {
2350 lines.show = false;
2351 points.show = true;
2352 } else if(this._plotType == 'both') {
2353 lines.show = true;
2354 points.show = true;
2355 }
2356 if(this._options.series == null) {
2357 this._options.series = {
2358 lines: lines,
2359 points: points
2360 }
2361 } else {
2362 if(this._options.series.lines == null) {
2363 this._options.series.lines = lines;
2364 } else {
2365 this._options.series.lines.show = lines.show;
2366 }
2367 if(this._options.series.points == null) {
2368 this._options.series.points = points;
2369 } else {
2370 this._options.series.points.show = points.show;
2371 }
2372 }
2373
2374 this.render();
2375 },
2376 setFill: function(bool) {
2377 this._fill = bool;
2378 if(this._options.series == null) {
2379 this._options.series = {
2380 lines: {
2381 fill: bool
2382 }
2383 }
2384 } else {
2385 if(this._options.series.lines == null) {
2386 this._options.series.lines = {
2387 fill: bool
2388 }
2389 } else {
2390 this._options.series.lines.fill = bool;
2391 }
2392 }
2393 this.render();
2394 },
2395 clear: function() {
2396 this._super();
2397 this._distribution = null;
2398 this._pdfValues = [];
2399 this._cdfValues = [];
2400 this.render();
2401 },
2402 _generateValues: function() {
2403 this._cdfValues = []; // reinitialize the arrays.
2404 this._pdfValues = [];
2405
2406 var xs = this._range.getPoints();
2407
2408 this._options.xaxis = {
2409 min: xs[0],
2410 max: xs[xs.length-1]
2411 }
2412 var pdfs = this._distribution.density(this._range);
2413 var cdfs = this._distribution.cumulativeDensity(this._range);
2414 for(var i = 0; i < xs.length; i++) {
2415 if(pdfs[i] == Number.POSITIVE_INFINITY || pdfs[i] == Number.NEGATIVE_INFINITY) {
2416 pdfs[i] = null;
2417 }
2418 if(cdfs[i] == Number.POSITIVE_INFINITY || cdfs[i] == Number.NEGATIVE_INFINITY) {
2419 cdfs[i] = null;
2420 }
2421 this._pdfValues.push([xs[i], pdfs[i]]);
2422 this._cdfValues.push([xs[i], cdfs[i]]);
2423 }
2424 return jstat.max(pdfs);
2425 },
2426 showPDF: function() {
2427 this._showPDF = true;
2428 this.render();
2429 },
2430 hidePDF: function() {
2431 this._showPDF = false;
2432 this.render();
2433 },
2434 showCDF: function() {
2435 this._showCDF = true;
2436 this.render();
2437 },
2438 hideCDF: function() {
2439 this._showCDF = false;
2440 this.render();
2441 },
2442 setDistribution: function(distribution, range) {
2443 this._distribution = distribution;
2444 if(range != null) {
2445 this._range = range;
2446 } else {
2447 this._range = distribution.getRange();
2448 }
2449 this._maxY = this._generateValues();
2450 this._options.yaxis = {
2451 max: this._maxY*1.1
2452 }
2453
2454 this.render();
2455 },
2456 getDistribution: function() {
2457 return this._distribution;
2458 },
2459 getRange: function() {
2460 return this._range;
2461 },
2462 setRange: function(range) {
2463 this._range = range;
2464 this._generateValues();
2465 this.render();
2466 },
2467 render: function() {
2468 if(this._distribution != null) {
2469 if(this._showPDF && this._showCDF) {
2470 this.setData([{
2471 yaxis: 1,
2472 data:this._pdfValues,
2473 color: 'rgb(237,194,64)',
2474 clickable: false,
2475 hoverable: true,
2476 label: "PDF"
2477 }, {
2478 yaxis: 2,
2479 data:this._cdfValues,
2480 clickable: false,
2481 color: 'rgb(175,216,248)',
2482 hoverable: true,
2483 label: "CDF"
2484 }]);
2485 this._options.yaxis = {
2486 max: this._maxY*1.1
2487 }
2488 } else if(this._showPDF) {
2489 this.setData([{
2490 data:this._pdfValues,
2491 hoverable: true,
2492 color: 'rgb(237,194,64)',
2493 clickable: false,
2494 label: "PDF"
2495 }]);
2496 this._options.yaxis = {
2497 max: this._maxY*1.1
2498 }
2499 } else if(this._showCDF) {
2500 this.setData([{
2501 data:this._cdfValues,
2502 hoverable: true,
2503 color: 'rgb(175,216,248)',
2504 clickable: false,
2505 label: "CDF"
2506 }]);
2507 this._options.yaxis = {
2508 max: 1.1
2509 }
2510 }
2511 } else {
2512 this.setData([]);
2513 }
2514 this._super(); // Call the parent plot method
2515 }
2516 });
2517
2518 var DistributionFactory = {};
2519 DistributionFactory.build = function(json) {
2520 /*
2521 if(json.name == null) {
2522 try{
2523 json = JSON.parse(json);
2524 }
2525 catch(err) {
2526 throw "invalid JSON";
2527 }
2528
2529 // try to parse it
2530 }*/
2531
2532 /*
2533 if(json.name != null) {
2534 var name = json.name;
2535 } else {
2536 throw "Malformed JSON provided to DistributionBuilder " + json;
2537 }
2538 */
2539 if(json.NormalDistribution) {
2540 if(json.NormalDistribution.mean != null && json.NormalDistribution.standardDeviation != null) {
2541 return new NormalDistribution(json.NormalDistribution.mean[0], json.NormalDistribution.standardDeviation[0]);
2542 } else {
2543 throw "Malformed JSON provided to DistributionBuilder " + json;
2544 }
2545 } else if (json.LogNormalDistribution) {
2546 if(json.LogNormalDistribution.location != null && json.LogNormalDistribution.scale != null) {
2547 return new LogNormalDistribution(json.LogNormalDistribution.location[0], json.LogNormalDistribution.scale[0]);
2548 } else {
2549 throw "Malformed JSON provided to DistributionBuilder " + json;
2550 }
2551 } else if (json.BetaDistribution) {
2552 if(json.BetaDistribution.alpha != null && json.BetaDistribution.beta != null) {
2553 return new BetaDistribution(json.BetaDistribution.alpha[0], json.BetaDistribution.beta[0]);
2554 } else {
2555 throw "Malformed JSON provided to DistributionBuilder " + json;
2556 }
2557 } else if (json.GammaDistribution) {
2558 if(json.GammaDistribution.shape != null && json.GammaDistribution.scale != null) {
2559 return new GammaDistribution(json.GammaDistribution.shape[0], json.GammaDistribution.scale[0]);
2560 } else {
2561 throw "Malformed JSON provided to DistributionBuilder " + json;
2562 }
2563 } else if (json.StudentTDistribution) {
2564 if(json.StudentTDistribution.degreesOfFreedom != null && json.StudentTDistribution.nonCentralityParameter != null) {
2565 return new StudentTDistribution(json.StudentTDistribution.degreesOfFreedom[0], json.StudentTDistribution.nonCentralityParameter[0]);
2566 } else if(json.StudentTDistribution.degreesOfFreedom != null) {
2567 return new StudentTDistribution(json.StudentTDistribution.degreesOfFreedom[0]);
2568 } else {
2569 throw "Malformed JSON provided to DistributionBuilder " + json;
2570 }
2571 } else {
2572 throw "Malformed JSON provided to DistributionBuilder " + json;
2573 }
2574 }
2575
2576