artdaq_node_server  v1_00_07
 All Classes Namespaces Files Variables Pages
JSRootCore.js
Go to the documentation of this file.
1 
6 
7 (function(){
8 
9  if (typeof JSROOT == "object") {
10  var e1 = new Error("JSROOT is already defined");
11  e1.source = "JSRootCore.js";
12  throw e1;
13  }
14 
15  JSROOT = {};
16 
17  JSROOT.version = "3.1a 9/12/2014";
18 
19  JSROOT.source_dir = function(){
20  var scripts = document.getElementsByTagName('script');
21 
22  for (var n in scripts) {
23  if (scripts[n]['type'] != 'text/javascript') continue;
24 
25  var src = scripts[n]['src'];
26  if ((src == null) || (src.length == 0)) continue;
27 
28  var pos = src.indexOf("scripts/JSRootCore.js");
29  if (pos<0) continue;
30 
31  console.log("Set JSROOT.source_dir to " + src.substr(0, pos));
32  return src.substr(0, pos);
33  }
34  return "";
35  }();
36 
37  // TODO: all jQuery-related functions should go into extra script
38  JSROOT.clone = function(obj) {
39  return jQuery.extend(true, {}, obj);
40  }
41 
42  JSROOT.id_counter = 0;
43 
44  JSROOT.function_list = []; // do we really need it here?
45 
46  JSROOT.BIT = function(n) { return 1 << (n); }
47 
48  // TH1 status bits
49  JSROOT.TH1StatusBits = {
50  kNoStats : JSROOT.BIT(9), // don't draw stats box
51  kUserContour : JSROOT.BIT(10), // user specified contour levels
52  kCanRebin : JSROOT.BIT(11), // can rebin axis
53  kLogX : JSROOT.BIT(15), // X-axis in log scale
54  kIsZoomed : JSROOT.BIT(16), // bit set when zooming on Y axis
55  kNoTitle : JSROOT.BIT(17), // don't draw the histogram title
56  kIsAverage : JSROOT.BIT(18) // Bin contents are average (used by Add)
57  };
58 
59  JSROOT.EAxisBits = {
60  kTickPlus : JSROOT.BIT(9),
61  kTickMinus : JSROOT.BIT(10),
62  kAxisRange : JSROOT.BIT(11),
63  kCenterTitle : JSROOT.BIT(12),
64  kCenterLabels : JSROOT.BIT(14),
65  kRotateTitle : JSROOT.BIT(15),
66  kPalette : JSROOT.BIT(16),
67  kNoExponent : JSROOT.BIT(17),
68  kLabelsHori : JSROOT.BIT(18),
69  kLabelsVert : JSROOT.BIT(19),
70  kLabelsDown : JSROOT.BIT(20),
71  kLabelsUp : JSROOT.BIT(21),
72  kIsInteger : JSROOT.BIT(22),
73  kMoreLogLabels : JSROOT.BIT(23),
74  kDecimals : JSROOT.BIT(11)
75  };
76 
77  // This is part of the JSON-R code, found on
78  // https://github.com/graniteds/jsonr
79  // Only unref part was used, arrays are not accounted as objects
80  // Should be used to reintroduce objects references, produced by TBufferJSON
81 
82  JSROOT.JSONR_unref = function(value, dy)
83  {
84  var c, i, k, ks;
85  if (!dy) dy = [];
86 
87  switch (typeof value) {
88  case 'string':
89  if ((value.length > 5) && (value.substr(0, 5) == "$ref:")) {
90  c = parseInt(value.substr(5));
91  if (!isNaN(c) && (c < dy.length)) {
92  value = dy[c];
93  // console.log("replace index " + c + " name = " + value.fName);
94  }
95  }
96  break;
97 
98  case 'object':
99  if (value !== null) {
100 
101  if (Object.prototype.toString.apply(value) === '[object Array]') {
102  for (i = 0; i < value.length; i++) {
103  value[i] = JSROOT.JSONR_unref(value[i], dy);
104  }
105  } else {
106 
107  // account only objects in ref table
108  if (dy.indexOf(value) === -1) {
109  //if (dy.length<10) console.log("Add object " + value._typename + " $ref:" + dy.length);
110  dy.push(value);
111  }
112 
113  // add methods to all objects, where _typename is specified
114  if (('_typename' in value) && (typeof JSROOT == "object"))
115  JSROOT.addMethods(value);
116 
117  ks = Object.keys(value);
118  for (i = 0; i < ks.length; i++) {
119  k = ks[i];
120  //if (dy.length<10) console.log("Check field " + k);
121  value[k] = JSROOT.JSONR_unref(value[k], dy);
122  }
123  }
124  }
125  break;
126  }
127 
128  return value;
129  }
130 
131  JSROOT.parse = function(arg) {
132  if ((arg==null) || (arg=="")) return null;
133  var obj = JSON.parse(arg);
134  if (obj!=null) obj = JSROOT.JSONR_unref(obj)
135  return obj;
136  }
137 
138  JSROOT.GetUrlOption = function(opt, url, dflt) {
139  // analyzes document.URL and extracts options after '?' mark
140  // following options supported ?opt1&opt2=3
141  // In case of opt1 empty string will be returned, in case of opt2 '3'
142  // If option not found, null is returned (or provided default value)
143 
144  if ((opt==null) || (typeof opt != 'string') || (opt.length==0)) return dflt;
145 
146  if (!url) url = document.URL;
147 
148  var pos = url.indexOf("?");
149  if (pos<0) return null;
150  url = url.slice(pos+1);
151 
152  while (url.length>0) {
153 
154  if (url==opt) return "";
155 
156  pos = url.indexOf("&");
157  if (pos < 0) pos = url.length;
158 
159  if (url.indexOf(opt) == 0) {
160  if (url.charAt(opt.length)=="&") return "";
161 
162  // replace several symbols which are known to make a problem
163  if (url.charAt(opt.length)=="=")
164  return url.slice(opt.length+1, pos).replace(/%27/g, "'").replace(/%22/g, '"').replace(/%20/g, ' ').replace(/%3C/g, '<').replace(/%3E/g, '>');
165  }
166 
167  url = url.slice(pos+1);
168  }
169  return dflt;
170  }
171 
172  JSROOT.NewHttpRequest = function(url, kind, user_call_back) {
173  // Create asynchronous XMLHttpRequest object.
174  // One should call req.send() to submit request
175  // kind of the request can be:
176  // "bin" - abstract binary data (default)
177  // "text" - returns req.responseText
178  // "object" - returns JSROOT.parse(req.responseText)
179  // "xml" - returns res.responseXML
180  // "head" - returns request itself, uses "HEAD" method
181  // Result will be returned to the callback functions
182  // If failed, request returns null
183 
184  function callback(res) {
185  if (typeof user_call_back == 'function') user_call_back(res);
186  }
187 
188  var xhr = new XMLHttpRequest();
189 
190 // if (typeof ActiveXObject == "function") {
191  if (window.ActiveXObject) {
192  // console.log(" Create IE request");
193 
194  xhr.onreadystatechange = function() {
195  // console.log(" Ready IE request");
196  if (xhr.readyState != 4) return;
197 
198  if (xhr.status != 200 && xhr.status != 206) {
199  // error
200  return callback(null);
201  }
202 
203  if (kind == "xml") return callback(xhr.responseXML);
204 
205  if (kind == "text") return callback(xhr.responseText);
206 
207  if (kind == "object") return callback(JSROOT.parse(xhr.responseText));
208 
209  if (kind == "head") return callback(xhr);
210 
211  var filecontent = new String("");
212  var array = new VBArray(xhr.responseBody).toArray();
213  for (var i = 0; i < array.length; i++) {
214  filecontent = filecontent + String.fromCharCode(array[i]);
215  }
216 
217  callback(filecontent);
218  filecontent = null;
219  }
220 
221  xhr.open(kind == 'head' ? 'HEAD' : 'GET', url, true);
222 
223  } else {
224 
225  xhr.onreadystatechange = function() {
226  if (xhr.readyState != 4) return;
227 
228  if (xhr.status != 0 && xhr.status != 200 && xhr.status != 206) {
229  return callback(null);
230  }
231  if (kind == "xml") return callback(xhr.responseXML);
232  if (kind == "text") return callback(xhr.responseText);
233  if (kind == "object") return callback(JSROOT.parse(xhr.responseText));
234  if (kind == "head") return callback(xhr);
235 
236  var HasArrayBuffer = ('ArrayBuffer' in window && 'Uint8Array' in window);
237  var Buf, filecontent;
238  if (HasArrayBuffer && 'mozResponse' in xhr) {
239  Buf = xhr.mozResponse;
240  } else if (HasArrayBuffer && xhr.mozResponseArrayBuffer) {
241  Buf = xhr.mozResponseArrayBuffer;
242  } else if ('responseType' in xhr) {
243  Buf = xhr.response;
244  } else {
245  Buf = xhr.responseText;
246  HasArrayBuffer = false;
247  }
248 
249  if (HasArrayBuffer) {
250  filecontent = new String("");
251  var bLen = Buf.byteLength;
252  var u8Arr = new Uint8Array(Buf, 0, bLen);
253  for (var i = 0; i < u8Arr.length; i++) {
254  filecontent = filecontent + String.fromCharCode(u8Arr[i]);
255  }
256  delete u8Arr;
257  } else {
258  filecontent = Buf;
259  }
260 
261  callback(filecontent);
262 
263  filecontent = null;
264  }
265 
266  xhr.open(kind == 'head' ? 'HEAD' : 'GET', url, true);
267 
268  if (kind == "bin") {
269  var HasArrayBuffer = ('ArrayBuffer' in window && 'Uint8Array' in window);
270  if (HasArrayBuffer && 'mozResponseType' in xhr) {
271  xhr.mozResponseType = 'arraybuffer';
272  } else if (HasArrayBuffer && 'responseType' in xhr) {
273  xhr.responseType = 'arraybuffer';
274  } else {
275  //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
276  xhr.overrideMimeType("text/plain; charset=x-user-defined");
277  }
278  }
279  }
280  return xhr;
281  }
282 
283  JSROOT.loadScript = function(urllist, callback, debugout) {
284  // dynamic script loader using callback
285  // (as loading scripts may be asynchronous)
286  // one could specify list of scripts or style files, separated by semicolon ';'
287  // one can prepend file name with '$$$' - than file will be loaded from JSROOT location
288  // This location can be set by JSROOT.source_dir or it will be detected automatically
289  // by the position of JSRootCore.js file, which must be loaded by normal methods:
290  // <script type="text/javascript" src="scripts/JSRootCore.js"></script>
291 
292  function debug(str) {
293  if (debugout)
294  document.getElementById(debugout).innerHTML = str;
295  else
296  console.log(str);
297  }
298 
299  function completeLoad() {
300  if ((urllist!=null) && (urllist.length>0))
301  return JSROOT.loadScript(urllist, callback, debugout);
302 
303  if (debugout)
304  document.getElementById(debugout).innerHTML = "";
305 
306  if (typeof callback == 'function') callback();
307  }
308 
309  if ((urllist==null) || (urllist.length==0))
310  return completeLoad();
311 
312  var filename = urllist;
313  var separ = filename.indexOf(";");
314  if (separ>0) {
315  filename = filename.substr(0, separ);
316  urllist = urllist.slice(separ+1);
317  } else {
318  urllist = "";
319  }
320 
321  var isrootjs = false;
322  if (filename.indexOf("$$$")==0) {
323  isrootjs = true;
324  filename = filename.slice(3);
325  }
326  var isstyle = filename.indexOf('.css') > 0;
327 
328  if (isstyle) {
329  var styles = document.getElementsByTagName('link');
330  for (var n in styles) {
331  if ((styles[n]['type'] != 'text/css') || (styles[n]['rel'] != 'stylesheet')) continue;
332 
333  var href = styles[n]['href'];
334  if ((href == null) || (href.length == 0)) continue;
335 
336  if (href.indexOf(filename)>=0) {
337  console.log("style "+ filename + " already loaded");
338  return completeLoad();
339  }
340  }
341 
342  } else {
343  var scripts = document.getElementsByTagName('script');
344 
345  for (var n in scripts) {
346  if (scripts[n]['type'] != 'text/javascript') continue;
347 
348  var src = scripts[n]['src'];
349  if ((src == null) || (src.length == 0)) continue;
350 
351  if (src.indexOf(filename)>=0) {
352  // debug("script "+ filename + " already loaded");
353  return completeLoad();
354  }
355  }
356  }
357 
358  if (isrootjs && (JSROOT.source_dir!=null)) filename = JSROOT.source_dir + filename;
359 
360  var element = null;
361 
362  debug("loading " + filename + " ...");
363 
364  if (isstyle) {
365  element = document.createElement("link");
366  element.setAttribute("rel", "stylesheet");
367  element.setAttribute("type", "text/css");
368  element.setAttribute("href", filename);
369  } else {
370  element = document.createElement("script");
371  element.setAttribute('type', "text/javascript");
372  element.setAttribute('src', filename);//+ "?r=" + rnd;
373  }
374 
375  if (element.readyState) { // Internet Explorer specific
376  element.onreadystatechange = function() {
377  if (element.readyState == "loaded" || element.readyState == "complete") {
378  element.onreadystatechange = null;
379  completeLoad();
380  }
381  }
382  } else { // Other browsers
383  element.onload = function() {
384  element.onload = null;
385  completeLoad();
386  }
387  }
388 
389  document.getElementsByTagName("head")[0].appendChild(element);
390  }
391 
392  JSROOT.AssertPrerequisites = function(kind, andThan, debugout) {
393  // one could specify kind of requirements
394  // 'io' for I/O functionality (default)
395  // '2d' for 2d graphic
396  // '3d' for 3d graphic
397  // 'simple' for basic user interface
398  // 'user:' list of user-specific scripts at the end of kind string
399 
400  if (typeof kind == 'function') { andThan = kind; kind = null; }
401 
402  if (typeof kind != 'string') kind = "2d";
403  if (kind.charAt(kind.length-1)!=";") kind+=";";
404 
405  // file names should be separated with ';'
406  var allfiles = '$$$scripts/jquery.min.js';
407 
408  if (kind.indexOf('io;')>=0)
409  allfiles += ";$$$scripts/rawinflate.js" +
410  ";$$$scripts/JSRootIOEvolution.js";
411 
412  if (kind.indexOf('2d;')>=0)
413  allfiles += ';$$$style/jquery-ui.css' +
414  ';$$$scripts/jquery-ui.min.js' +
415  ';$$$scripts/d3.v3.min.js' +
416  ';$$$scripts/JSRootPainter.js' +
417  ';$$$style/JSRootPainter.css';
418 
419  if (kind.indexOf("3d;")>=0)
420  allfiles += ";$$$scripts/jquery.mousewheel.js" +
421  ";$$$scripts/three.min.js" +
422  ";$$$scripts/helvetiker_regular.typeface.js" +
423  ";$$$scripts/helvetiker_bold.typeface.js" +
424  ";$$$scripts/JSRoot3DPainter.js";
425 
426  if (kind.indexOf("simple;")>=0)
427  allfiles += ';$$$scripts/JSRootInterface.js' +
428  ';$$$style/JSRootInterface.css';
429 
430  var pos = kind.indexOf("user:");
431  if (pos>0)
432  allfiles += ";" + kind.slice(pos+5);
433 
434  JSROOT.loadScript(allfiles, andThan, debugout);
435  }
436 
437  JSROOT.BuildSimpleGUI = function(user_scripts, andThen) {
438  if (typeof user_scripts == 'function') {
439  andThen = user_scripts;
440  user_scripts = null;
441  }
442 
443  var debugout = null;
444 
445  var requirements = "2d;io;simple;";
446 
447  if (document.getElementById('simpleGUI')) debugout = 'simpleGUI'; else
448  if (document.getElementById('onlineGUI')) { debugout = 'onlineGUI'; requirements = "2d;simple;"; }
449 
450  if (user_scripts == null)
451  user_scripts = JSROOT.GetUrlOption("autoload");
452 
453  if (user_scripts != null)
454  requirements += "user:" + user_scripts + ";";
455 
456  JSROOT.AssertPrerequisites(requirements, function() {
457  if (typeof BuildSimpleGUI == 'function') BuildSimpleGUI();
458  if (typeof andThen == 'function') andThen();
459  }, debugout);
460  }
461 
462  JSROOT.addFormula = function(obj) {
463  var formula = obj['fTitle'];
464  formula = formula.replace('abs(', 'Math.abs(');
465  formula = formula.replace('sin(', 'Math.sin(');
466  formula = formula.replace('cos(', 'Math.cos(');
467  var code = obj['fName'] + " = function(x) { return " + formula + " };";
468  eval(code);
469  var sig = obj['fName']+'(x)';
470 
471  var pos = JSROOT.function_list.indexOf(sig);
472  if (pos >= 0) {
473  JSROOT.function_list.splice(pos, 1);
474  }
475  JSROOT.function_list.push(sig);
476  }
477 
478  JSROOT.Create = function(typename, target) {
479  var obj = target;
480  if (obj == null)
481  obj = { _typename: typename };
482 
483  if (typename == 'TObject')
484  jQuery.extend(obj, { fUniqueID: 0, fBits: 0x3000008 });
485  else
486  if (typename == 'TNamed')
487  jQuery.extend(obj, { fUniqueID: 0, fBits: 0x3000008, fName: "", fTitle: "" });
488  else
489  if (typename == 'TList')
490  jQuery.extend(obj, { name: "TList", arr : [], opt : [] });
491  else
492  if (typename == 'TAttAxis') {
493  jQuery.extend(obj, { fNdivisions: 510, fAxisColor: 1,
494  fLabelColor: 1, fLabelFont: 42, fLabelOffset: 0.05, fLabelSize: 0.035, fTickLength: 0.03,
495  fTitleOffset: 1, fTitleSize: 0.035, fTitleColor: 1, fTitleFont : 42 });
496  } else
497  if (typename == 'TAxis') {
498  JSROOT.Create("TNamed", obj);
499  JSROOT.Create("TAttAxis", obj);
500  jQuery.extend(obj, { fNbins: 0, fXmin: 0, fXmax: 0, fXbins : [], fFirst: 0, fLast: 0,
501  fBits2: 0, fTimeDisplay: false, fTimeFormat: "", fLabels: null });
502  } else
503  if (typename == 'TAttLine') {
504  jQuery.extend(obj, { fLineColor: 1, fLineStyle : 1, fLineWidth : 1 });
505  } else
506  if (typename == 'TAttFill') {
507  jQuery.extend(obj, { fFillColor: 0, fFillStyle : 0 } );
508  } else
509  if (typename == 'TAttMarker') {
510  jQuery.extend(obj, { fMarkerColor: 1, fMarkerStyle : 1, fMarkerSize : 1. });
511  } else
512  if (typename == 'TBox') {
513  JSROOT.Create("TObject", obj);
514  JSROOT.Create("TAttLine", obj);
515  JSROOT.Create("TAttFill", obj);
516  jQuery.extend(obj, { fX1: 0, fY1: 0, fX2: 1, fY2: 1 });
517  } else
518  if (typename == 'TPave') {
519  JSROOT.Create("TBox", obj);
520  jQuery.extend(obj, { fX1NDC : 0., fY1NDC: 0, fX2NDC: 1, fY2NDC: 1,
521  fBorderSize: 0, fInit: 1, fShadowColor: 1,
522  fCornerRadius: 0, fOption: "blNDC", fName: "title" });
523  } else
524  if (typename == 'TAttText') {
525  jQuery.extend(obj, { fTextAngle: 0, fTextSize: 0, fTextAlign: 22, fTextColor: 1, fTextFont: 42});
526  } else
527  if (typename == 'TPaveText') {
528  JSROOT.Create("TPave", obj);
529  JSROOT.Create("TAttText", obj);
530  jQuery.extend(obj, { fLabel: "", fLongest: 27, fMargin: 0.05, fLines: JSROOT.Create("TList") });
531  } else
532  if (typename == 'TPaveStats') {
533  JSROOT.Create("TPaveText", obj);
534  jQuery.extend(obj, { fOptFit: 0, fOptStat: 0, fFitFormat: "", fStatFormat: "", fParent: null });
535  } else
536  if (typename == 'TH1') {
537  JSROOT.Create("TNamed", obj);
538  JSROOT.Create("TAttLine", obj);
539  JSROOT.Create("TAttFill", obj);
540  JSROOT.Create("TAttMarker", obj);
541 
542  jQuery.extend(obj, {
543  fNcells : 0,
544  fXaxis: JSROOT.Create("TAxis"),
545  fYaxis: JSROOT.Create("TAxis"),
546  fZaxis: JSROOT.Create("TAxis"),
547  fBarOffset : 0, fBarWidth : 1000, fEntries : 0.,
548  fTsumw : 0., fTsumw2 : 0., fTsumwx : 0., fTsumwx2 : 0.,
549  fMaximum : -1111., fMinimum : -1111, fNormFactor : 0., fContour : [],
550  fSumw2 : [], fOption : "",
551  fFunctions : JSROOT.Create("TList"),
552  fBufferSize : 0, fBuffer : [], fBinStatErrOpt : 0 });
553  } else
554  if (typename == 'TH1I' || typename == 'TH1F' || typename == 'TH1D' || typename == 'TH1S' || typename == 'TH1C') {
555  JSROOT.Create("TH1", obj);
556  jQuery.extend(obj, { fN : 0, fArray: [] });
557  } else
558  if (typename == 'TH2') {
559  JSROOT.Create("TH1", obj);
560  jQuery.extend(obj, { fScalefactor: 1., fTsumwy: 0., fTsumwy2: 0, fTsumwxy : 0});
561  } else
562  if (typename == 'TH2I' || typename == 'TH2F' || typename == 'TH2D' || typename == 'TH2S' || typename == 'TH2C') {
563  JSROOT.Create("TH2", obj);
564  jQuery.extend(obj, { fN : 0, fArray: [] });
565  } else
566  if (typename == 'TGraph') {
567  JSROOT.Create("TNamed", obj);
568  JSROOT.Create("TAttLine", obj);
569  JSROOT.Create("TAttFill", obj);
570  JSROOT.Create("TAttMarker", obj);
571  jQuery.extend(obj, { fFunctions: JSROOT.Create("TList"), fHistogram: JSROOT.CreateTH1(),
572  fMaxSize: 0, fMaximum:0, fMinimum:0, fNpoints: 0, fX: [], fY: [] });
573  }
574 
575  JSROOT.addMethods(obj, typename);
576  return obj;
577  }
578 
579  // obsolete functions, can be removed by next JSROOT release
580  JSROOT.CreateTList = function() { return JSROOT.Create("TList"); }
581  JSROOT.CreateTAxis = function() { return JSROOT.Create("TAxis"); }
582 
583  JSROOT.CreateTH1 = function(nbinsx) {
584  var histo = JSROOT.Create("TH1I");
585  jQuery.extend(histo, { fName: "dummy_histo_" + this.id_counter++, fTitle: "dummytitle" });
586 
587  if (nbinsx!=null) {
588  histo['fN'] = histo['fNcells'] = nbinsx+2;
589  for (var i=0;i<histo['fNcells'];i++) histo['fArray'].push(0);
590  jQuery.extend(histo['fXaxis'], { fNbins: nbinsx, fXmin: 0, fXmax: nbinsx });
591  }
592  return histo;
593  }
594 
595  JSROOT.CreateTH2 = function(nbinsx, nbinsy) {
596  var histo = JSROOT.Create("TH2I");
597  jQuery.extend(histo, { fName: "dummy_histo_" + this.id_counter++, fTitle: "dummytitle" });
598 
599  if ((nbinsx!=null) && (nbinsy!=null)) {
600  histo['fN'] = histo['fNcells'] = (nbinsx+2) * (nbinsy+2);
601  for (var i=0;i<histo['fNcells'];i++) histo['fArray'].push(0);
602  jQuery.extend(histo['fXaxis'], { fNbins: nbinsx, fXmin: 0, fXmax: nbinsx });
603  jQuery.extend(histo['fYaxis'], { fNbins: nbinsy, fXmin: 0, fXmax: nbinsy });
604  }
605  return histo;
606  }
607 
608  JSROOT.CreateTGraph = function(npoints) {
609  var graph = JSROOT.Create("TGraph");
610  jQuery.extend(graph, { fBits: 0x3000408, fName: "dummy_graph_" + this.id_counter++, fTitle: "dummytitle" });
611 
612  if (npoints>0) {
613  graph['fMaxSize'] = graph['fNpoints'] = npoints;
614  for (var i=0;i<npoints;i++) {
615  graph['fX'].push(i);
616  graph['fY'].push(i);
617  }
618  JSROOT.AdjustTGraphRanges(graph);
619  }
620 
621  return graph;
622  }
623 
624  JSROOT.AdjustTGraphRanges = function(graph) {
625  if (graph['fNpoints']==0) return;
626 
627  var minx = graph['fX'][0], maxx = minx;
628  var miny = graph['fY'][0], maxy = miny;
629 
630  for (var i=1;i<graph['fNpoints'];i++) {
631  if (graph['fX'][i] < minx) minx = graph['fX'][i];
632  if (graph['fX'][i] > maxx) maxx = graph['fX'][i];
633  if (graph['fY'][i] < miny) miny = graph['fY'][i];
634  if (graph['fY'][i] > maxy) maxy = graph['fY'][i];
635  }
636 
637  if (miny==maxy) maxy = miny + 1;
638 
639  graph['fHistogram']['fXaxis']['fXmin'] = minx;
640  graph['fHistogram']['fXaxis']['fXmax'] = maxx;
641 
642  graph['fHistogram']['fYaxis']['fXmin'] = miny;
643  graph['fHistogram']['fYaxis']['fXmax'] = maxy;
644  }
645 
646  JSROOT.addMethods = function(obj, obj_typename) {
647  // check object type and add methods if needed
648  if (('fBits' in obj) && !('TestBit' in obj)) {
649  obj['TestBit'] = function (f) {
650  return ((obj['fBits'] & f) != 0);
651  };
652  }
653 
654  if (!obj_typename) {
655  if (!('_typename' in obj)) return;
656  obj_typename = obj['_typename'];
657  }
658 
659  var EBinErrorOpt = {
660  kNormal : 0, // errors with Normal (Wald) approximation: errorUp=errorLow= sqrt(N)
661  kPoisson : 1, // errors from Poisson interval at 68.3% (1 sigma)
662  kPoisson2 : 2 // errors from Poisson interval at 95% CL (~ 2 sigma)
663  };
664 
665  var EErrorType = {
666  kERRORMEAN : 0,
667  kERRORSPREAD : 1,
668  kERRORSPREADI : 2,
669  kERRORSPREADG : 3
670  };
671 
672  if (obj_typename.indexOf("TAxis") == 0) {
673  obj['getFirst'] = function() {
674  if (!this.TestBit(JSROOT.EAxisBits.kAxisRange)) return 1;
675  return this['fFirst'];
676  };
677  obj['getLast'] = function() {
678  if (!this.TestBit(JSROOT.EAxisBits.kAxisRange)) return this['fNbins'];
679  return this['fLast'];
680  };
681  obj['getBinCenter'] = function(bin) {
682  // Return center of bin
683  var binwidth;
684  if (!this['fN'] || bin < 1 || bin > this['fNbins']) {
685  binwidth = (this['fXmax'] - this['fXmin']) / this['fNbins'];
686  return this['fXmin'] + (bin-1) * binwidth + 0.5*binwidth;
687  } else {
688  binwidth = this['fXbins'][bin] - this['fXbins'][bin-1];
689  return this['fXbins'][bin-1] + 0.5*binwidth;
690  }
691  };
692  }
693 
694  if (obj_typename == "TList") {
695  obj['Clear'] = function() {
696  this['arr'] = new Array;
697  this['opt'] = new Array;
698  }
699  obj['Add'] = function(obj,opt) {
700  this['arr'].push(obj);
701  this['opt'].push((typeof opt=='string') ? opt : "");
702  }
703  }
704 
705  if ((obj_typename == "TPaveText") || (obj_typename == "TPaveStats")) {
706  obj['AddText'] = function(txt) {
707  this['fLines'].Add({'fTitle' : txt, "fTextColor" : 1 });
708  }
709  obj['Clear'] = function() {
710  this['fLines'].Clear();
711  }
712  }
713 
714  if ((obj_typename.indexOf("TFormula") != -1) ||
715  (obj_typename.indexOf("TF1") == 0)) {
716  obj['evalPar'] = function(x) {
717  var i, _function = this['fTitle'];
718  _function = _function.replace('TMath::Exp(', 'Math.exp(');
719  _function = _function.replace('TMath::Abs(', 'Math.abs(');
720  _function = _function.replace('gaus(', 'JSROOT.Math.gaus(this, ' + x + ', ');
721  _function = _function.replace('gausn(', 'JSROOT.Math.gausn(this, ' + x + ', ');
722  _function = _function.replace('expo(', 'JSROOT.Math.expo(this, ' + x + ', ');
723  _function = _function.replace('landau(', 'JSROOT.Math.landau(this, ' + x + ', ');
724  _function = _function.replace('landaun(', 'JSROOT.Math.landaun(this, ' + x + ', ');
725  _function = _function.replace('pi', 'Math.PI');
726  for (i=0;i<this['fNpar'];++i) {
727  while(_function.indexOf('['+i+']') != -1)
728  _function = _function.replace('['+i+']', this['fParams'][i])
729  }
730  for (i=0;i<JSROOT.function_list.length;++i) {
731  var f = JSROOT.function_list[i].substring(0, JSROOT.function_list[i].indexOf('('));
732  if (_function.indexOf(f) != -1) {
733  var fa = JSROOT.function_list[i].replace('(x)', '(' + x + ')');
734  _function = _function.replace(f, fa);
735  }
736  }
737  // use regex to replace ONLY the x variable (i.e. not 'x' in Math.exp...)
738  _function = _function.replace(/\b(x)\b/gi, x)
739  _function = _function.replace(/\b(sin)\b/gi, 'Math.sin')
740  _function = _function.replace(/\b(cos)\b/gi, 'Math.cos')
741  _function = _function.replace(/\b(tan)\b/gi, 'Math.tan')
742  var ret = eval(_function);
743  return ret;
744  };
745  }
746  if ((obj_typename.indexOf("TGraph") == 0) || (obj_typename == "TCutG")) {
747  obj['ComputeRange'] = function() {
748  // Compute the x/y range of the points in this graph
749  var res = { xmin: 0, xmax: 0, ymin: 0, ymax: 0 };
750  if (this['fNpoints'] > 0) {
751  res.xmin = res.xmax = this['fX'][0];
752  res.ymin = res.ymax = this['fY'][0];
753  for (var i=1; i<this['fNpoints']; i++) {
754  if (this['fX'][i] < res.xmin) res.xmin = this['fX'][i];
755  if (this['fX'][i] > res.xmax) res.xmax = this['fX'][i];
756  if (this['fY'][i] < res.ymin) res.ymin = this['fY'][i];
757  if (this['fY'][i] > res.ymax) res.ymax = this['fY'][i];
758  }
759  }
760  return res;
761  };
762  // check if point inside figure specified by the TGrpah
763  obj['IsInside'] = function(xp,yp) {
764  var j = this['fNpoints'] - 1 ;
765  var x = this['fX'], y = this['fY'];
766  var oddNodes = false;
767 
768  for (var i=0; i<this['fNpoints']; i++) {
769  if ((y[i]<yp && y[j]>=yp) || (y[j]<yp && y[i]>=yp)) {
770  if (x[i]+(yp-y[i])/(y[j]-y[i])*(x[j]-x[i])<xp) {
771  oddNodes = !oddNodes;
772  }
773  }
774  j=i;
775  }
776 
777  return oddNodes;
778  };
779  }
780  if (obj_typename.indexOf("TH1") == 0 ||
781  obj_typename.indexOf("TH2") == 0 ||
782  obj_typename.indexOf("TH3") == 0) {
783  obj['getBinError'] = function(bin) {
784  // -*-*-*-*-*Return value of error associated to bin number bin*-*-*-*-*
785  // if the sum of squares of weights has been defined (via Sumw2),
786  // this function returns the sqrt(sum of w2).
787  // otherwise it returns the sqrt(contents) for this bin.
788  if (bin < 0) bin = 0;
789  if (bin >= this['fNcells']) bin = this['fNcells'] - 1;
790  if (this['fN'] && this['fSumw2'].length > 0) {
791  var err2 = this['fSumw2'][bin];
792  return Math.sqrt(err2);
793  }
794  var error2 = Math.abs(this['fArray'][bin]);
795  return Math.sqrt(error2);
796  };
797  obj['getBinErrorLow'] = function(bin) {
798  // -*-*-*-*-*Return lower error associated to bin number bin*-*-*-*-*
799  // The error will depend on the statistic option used will return
800  // the binContent - lower interval value
801  if (this['fBinStatErrOpt'] == EBinErrorOpt.kNormal || this['fN']) return this.getBinError(bin);
802  if (bin < 0) bin = 0;
803  if (bin >= this['fNcells']) bin = this['fNcells'] - 1;
804  var alpha = 1.0 - 0.682689492;
805  if (this['fBinStatErrOpt'] == EBinErrorOpt.kPoisson2) alpha = 0.05;
806  var c = this['fArray'][bin];
807  var n = Math.round(c);
808  if (n < 0) {
809  alert("GetBinErrorLow : Histogram has negative bin content-force usage to normal errors");
810  this['fBinStatErrOpt'] = EBinErrorOpt.kNormal;
811  return this.getBinError(bin);
812  }
813  if (n == 0) return 0;
814  return c - JSROOT.Math.gamma_quantile( alpha/2, n, 1.);
815  };
816  obj['getBinErrorUp'] = function(bin) {
817  // -*-*-*-*-*Return lower error associated to bin number bin*-*-*-*-*
818  // The error will depend on the statistic option used will return
819  // the binContent - lower interval value
820  if (this['fBinStatErrOpt'] == EBinErrorOpt.kNormal || this['fN']) return this.getBinError(bin);
821  if (bin < 0) bin = 0;
822  if (bin >= this['fNcells']) bin = this['fNcells'] - 1;
823  var alpha = 1.0 - 0.682689492;
824  if (this['fBinStatErrOpt'] == EBinErrorOpt.kPoisson2) alpha = 0.05;
825  var c = this['fArray'][bin];
826  var n = Math.round(c);
827  if (n < 0) {
828  alert("GetBinErrorLow : Histogram has negative bin content-force usage to normal errors");
829  this['fBinStatErrOpt'] = EBinErrorOpt.kNormal;
830  return this.getBinError(bin);
831  }
832  // for N==0 return an upper limit at 0.68 or (1-alpha)/2 ?
833  // decide to return always (1-alpha)/2 upper interval
834  //if (n == 0) return ROOT::Math::gamma_quantile_c(alpha,n+1,1);
835  return JSROOT.Math.gamma_quantile_c( alpha/2, n+1, 1) - c;
836  };
837  obj['getBinLowEdge'] = function(bin) {
838  // Return low edge of bin
839  if (this['fXaxis']['fXbins']['fN'] && bin > 0 && bin <= this['fXaxis']['fNbins'])
840  return this['fXaxis']['fXbins']['fArray'][bin-1];
841  var binwidth = (this['fXaxis']['fXmax'] - this['fXaxis']['fXmin']) / this['fXaxis']['fNbins'];
842  return this['fXaxis']['fXmin'] + (bin-1) * binwidth;
843  };
844  obj['getBinUpEdge'] = function(bin) {
845  // Return up edge of bin
846  var binwidth;
847  if (!this['fXaxis']['fXbins']['fN'] || bin < 1 || bin > this['fXaxis']['fNbins']) {
848  binwidth = (this['fXaxis']['fXmax'] - this['fXaxis']['fXmin']) / this['fXaxis']['fNbins'];
849  return this['fXaxis']['fXmin'] + bin * binwidth;
850  } else {
851  binwidth = this['fArray'][bin] - this['fArray'][bin-1];
852  return this['fArray'][bin-1] + binwidth;
853  }
854  };
855  obj['getBinWidth'] = function(bin) {
856  // Return bin width
857  if (this['fXaxis']['fNbins'] <= 0) return 0;
858  if (this['fXaxis']['fXbins']['fN'] <= 0)
859  return (this['fXaxis']['fXmax'] - this['fXaxis']['fXmin']) / this['fXaxis']['fNbins'];
860  if (bin > this['fXaxis']['fNbins']) bin = this['fXaxis']['fNbins'];
861  if (bin < 1) bin = 1;
862  return this['fArray'][bin] - this['fArray'][bin-1];
863  };
864  obj['add'] = function(h1, c1) {
865  // Performs the operation: this = this + c1*h1
866  // if errors are defined (see TH1::Sumw2), errors are also recalculated.
867  // Note that if h1 has Sumw2 set, Sumw2 is automatically called for this
868  // if not already set.
869  if (!h1 || typeof(h1) == 'undefined') {
870  alert("Add : Attempt to add a non-existing histogram");
871  return false;
872  }
873  if (!c1 || typeof(c1) == 'undefined') c1 = 1;
874  var nbinsx = this['fXaxis']['fNbins'],
875  nbinsy = this['fYaxis']['fNbins'],
876  nbinsz = this['fZaxis']['fNbins'];
877 
878  if (this['fDimension'] < 2) nbinsy = -1;
879  if (this['fDimension'] < 3) nbinsz = -1;
880 
881  // Create Sumw2 if h1 has Sumw2 set
882  if (this['fSumw2']['fN'] == 0 && h1['fSumw2']['fN'] != 0) this.sumw2();
883 
884  // - Add statistics
885  if (this['fEntries'] == NaN) this['fEntries'] = 0;
886  var entries = Math.abs( this['fEntries'] + c1 * h1['fEntries'] );
887 
888  // statistics can be preserved only in case of positive coefficients
889  // otherwise with negative c1 (histogram subtraction) one risks to get negative variances
890  var resetStats = (c1 < 0);
891  var s1, s2;
892  if (!resetStats) {
893  // need to initialize to zero s1 and s2 since
894  // GetStats fills only used elements depending on dimension and type
895  s1 = this.getStats();
896  s2 = h1.getStats();
897  }
898  this['fMinimum'] = -1111;
899  this['fMaximum'] = -1111;
900 
901  // - Loop on bins (including underflows/overflows)
902  var bin, binx, biny, binz;
903  var cu, factor = 1;
904  if (Math.abs(h1['fNormFactor']) > 2e-308) factor = h1['fNormFactor'] / h1.getSumOfWeights();
905  for (binz=0;binz<=nbinsz+1;binz++) {
906  for (biny=0;biny<=nbinsy+1;biny++) {
907  for (binx=0;binx<=nbinsx+1;binx++) {
908  bin = binx +(nbinsx+2)*(biny + (nbinsy+2)*binz);
909  //special case where histograms have the kIsAverage bit set
910  if (this.TestBit(JSROOT.TH1StatusBits.kIsAverage)
911  && h1.TestBit(JSROOT.TH1StatusBits.kIsAverage)) {
912  var y1 = h1.getBinContent(bin),
913  y2 = this.getBinContent(bin),
914  e1 = h1.getBinError(bin),
915  e2 = this.getBinError(bin),
916  w1 = 1, w2 = 1;
917  // consider all special cases when bin errors are zero
918  // see http://root.cern.ch/phpBB3//viewtopic.php?f=3&t=13299
919  if (e1 > 0)
920  w1 = 1.0 / (e1 * e1);
921  else if (h1['fSumw2']['fN']) {
922  w1 = 1.E200; // use an arbitrary huge value
923  if (y1 == 0) {
924  // use an estimated error from the global histogram scale
925  var sf = (s2[0] != 0) ? s2[1] / s2[0] : 1;
926  w1 = 1.0 / (sf * sf);
927  }
928  }
929  if (e2 > 0)
930  w2 = 1.0 / (e2 * e2);
931  else if (this['fSumw2']['fN']) {
932  w2 = 1.E200; // use an arbitrary huge value
933  if (y2 == 0) {
934  // use an estimated error from the global histogram scale
935  var sf = (s1[0] != 0) ? s1[1] / s1[0] : 1;
936  w2 = 1.0 / (sf * sf);
937  }
938  }
939  var y = (w1 * y1 + w2 * y2) / (w1 + w2);
940  this.setBinContent(bin, y);
941  if (this['fSumw2']['fN']) {
942  var err2 = 1.0 / (w1 + w2);
943  if (err2 < 1.E-200) err2 = 0; // to remove arbitrary value when e1=0 AND e2=0
944  this['fSumw2']['fArray'][bin] = err2;
945  }
946  }
947  //normal case of addition between histograms
948  else {
949  cu = c1 * factor * h1.getBinContent(bin);
950  this['fArray'][bin] += cu;
951  if (this['fSumw2']['fN']) {
952  var e1 = factor * h1.getBinError(bin);
953  this['fSumw2']['fArray'][bin] += c1 * c1 * e1 * e1;
954  }
955  }
956  }
957  }
958  }
959  // update statistics (do here to avoid changes by SetBinContent)
960  if (resetStats) {
961  // statistics need to be reset in case coefficient are negative
962  this.resetStats();
963  }
964  else {
965  var kNstat = 13;
966  for (var i=0;i<kNstat;i++) {
967  if (i == 1) s1[i] += c1 * c1 * s2[i];
968  else s1[i] += c1 * s2[i];
969  }
970  //this.putStats(s1);
971  this['fTsumw'] = s1[0];
972  this['fTsumw2'] = s1[1];
973  this['fTsumwx'] = s1[2];
974  this['fTsumwx2'] = s1[3];
975  this['fEntries'] = entries;
976  }
977  return true;
978  };
979  obj['getBin'] = function(binx, biny, binz) {
980  // -*-*-*-*Return Global bin number corresponding to binx,y,z*-*-*-*-*-*-*
981  var nx, ny, nz;
982  if (this['fDimension'] < 2) {
983  nx = this['fXaxis']['fNbins']+2;
984  if (binx < 0) binx = 0;
985  if (binx >= nx) binx = nx-1;
986  return binx;
987  }
988  if (this['fDimension'] < 3) {
989  nx = this['fXaxis']['fNbins']+2;
990  if (binx < 0) binx = 0;
991  if (binx >= nx) binx = nx-1;
992  ny = this['fYaxis']['fNbins']+2;
993  if (biny < 0) biny = 0;
994  if (biny >= ny) biny = ny-1;
995  return binx + nx*biny;
996  }
997  if (this['fDimension'] < 4) {
998  nx = this['fXaxis']['fNbins']+2;
999  if (binx < 0) binx = 0;
1000  if (binx >= nx) binx = nx-1;
1001  ny = this['fYaxis']['fNbins']+2;
1002  if (biny < 0) biny = 0;
1003  if (biny >= ny) biny = ny-1;
1004  nz = this['fZaxis']['fNbins']+2;
1005  if (binz < 0) binz = 0;
1006  if (binz >= nz) binz = nz-1;
1007  return binx + nx*(biny +ny*binz);
1008  }
1009  return -1;
1010  };
1011  obj['getBinXYZ'] = function(binglobal) {
1012  // return binx, biny, binz corresponding to the global bin number globalbin
1013  // see TH1::GetBin function above
1014  var binx, biny, binz;
1015  var nx = this['fXaxis']['fNbins']+2;
1016  var ny = this['fYaxis']['fNbins']+2;
1017  if (this['fDimension'] < 2) {
1018  binx = binglobal%nx;
1019  biny = -1;
1020  binz = -1;
1021  }
1022  if (this['fDimension'] < 3) {
1023  binx = binglobal%nx;
1024  biny = ((binglobal-binx)/nx)%ny;
1025  binz = -1;
1026  }
1027  if (this['fDimension'] < 4) {
1028  binx = binglobal%nx;
1029  biny = ((binglobal-binx)/nx)%ny;
1030  binz = ((binglobal-binx)/nx -biny)/ny;
1031  }
1032  return { binsx: binx, biny: biny, binz: binz };
1033  };
1034  obj['getMaximum'] = function(maxval) {
1035  // Return maximum value smaller than maxval of bins in the range,
1036  // unless the value has been overridden by TH1::SetMaximum,
1037  // in which case it returns that value. (This happens, for example,
1038  // when the histogram is drawn and the y or z axis limits are changed
1039  //
1040  // To get the maximum value of bins in the histogram regardless of
1041  // whether the value has been overridden, use
1042  // h->GetBinContent(h->GetMaximumBin())
1043 
1044  if (this['fMaximum'] != -1111) return this['fMaximum'];
1045  if (!maxval || typeof(maxval) == 'undefined') maxval = Number.MAX_VALUE;
1046  var bin, binx, biny, binz;
1047  var xfirst = this['fXaxis'].getFirst();
1048  xlast = this['fXaxis'].getLast(),
1049  yfirst = this['fYaxis'].getFirst(),
1050  ylast = this['fYaxis'].getLast(),
1051  zfirst = this['fZaxis'].getFirst(),
1052  zlast = this['fZaxis'].getLast();
1053  var maximum = -Number.MAX_VALUE, val;
1054  for (binz=zfirst;binz<=zlast;binz++) {
1055  for (biny=yfirst;biny<=ylast;biny++) {
1056  for (binx=xfirst;binx<=xlast;binx++) {
1057  bin = this.getBin(binx,biny,binz);
1058  val = this.getBinContent(bin);
1059  if (val > maximum && val < maxval) maximum = val;
1060  }
1061  }
1062  }
1063  return maximum;
1064  };
1065  obj['getMinimum'] = function(minval) {
1066  // Return minimum value smaller than maxval of bins in the range,
1067  // unless the value has been overridden by TH1::SetMinimum,
1068  // in which case it returns that value. (This happens, for example,
1069  // when the histogram is drawn and the y or z axis limits are changed
1070  if (this['fMinimum'] != -1111) return this['fMinimum'];
1071  if (!minval || typeof(minval) == 'undefined') minval = -Number.MAX_VALUE;
1072  var bin, binx, biny, binz;
1073  var xfirst = this['fXaxis'].getFirst();
1074  xlast = this['fXaxis'].getLast(),
1075  yfirst = this['fYaxis'].getFirst(),
1076  ylast = this['fYaxis'].getLast(),
1077  zfirst = this['fZaxis'].getFirst(),
1078  zlast = this['fZaxis'].getLast();
1079  var minimum = Number.MAX_VALUE, val;
1080  for (binz=zfirst;binz<=zlast;binz++) {
1081  for (biny=yfirst;biny<=ylast;biny++) {
1082  for (binx=xfirst;binx<=xlast;binx++) {
1083  bin = this.getBin(binx,biny,binz);
1084  val = this.getBinContent(bin);
1085  if (val < minimum && val > minval) minimum = val;
1086  }
1087  }
1088  }
1089  return minimum;
1090  };
1091  obj['getSumOfWeights'] = function() {
1092  // -*-*-*-*-*-*Return the sum of weights excluding under/overflows*-*-*-*-*
1093  var bin, binx, biny, binz, sum = 0;
1094  for (binz=1; binz<=this['fZaxis']['fXbins']['fN']; binz++) {
1095  for (biny=1; biny<=this['fYaxis']['fXbins']['fN']; biny++) {
1096  for (binx=1; binx<=this['fXaxis']['fXbins']['fN']; binx++) {
1097  bin = this.getBin(binx,biny,binz);
1098  sum += this.getBinContent(bin);
1099  }
1100  }
1101  }
1102  return sum;
1103  };
1104  obj['labelsInflate'] = function(ax) {
1105  // Double the number of bins for axis.
1106  // Refill histogram
1107 
1108  var axis = null;
1109  var achoice = ax[0].toUpperCase();
1110  if (achoice == 'X') axis = this['fXaxis'];
1111  if (achoice == 'Y') axis = this['fYaxis'];
1112  if (achoice == 'Z') axis = this['fZaxis'];
1113  if (axis == null) return;
1114 
1115  var hold = JSROOT.clone(this);
1116 
1117  var timedisp = axis['fTimeDisplay'];
1118  var nbxold = this['fXaxis']['fNbins'];
1119  var nbyold = this['fYaxis']['fNbins'];
1120  var nbzold = this['fZaxis']['fNbins'];
1121  var nbins = axis['fNbins'];
1122  var xmin = axis['fXmin'];
1123  var xmax = axis['fXmax'];
1124  xmax = xmin + 2 * (xmax - xmin);
1125  axis['fFirst'] = 1;
1126  axis['fLast'] = axis['fNbins'];
1127  this['fBits'] &= ~(JSROOT.EAxisBits.kAxisRange & 0x00ffffff); // SetBit(kAxisRange, 0);
1128  // double the bins and recompute ncells
1129  axis['fNbins'] = 2*nbins;
1130  axis['fXmin'] = xmin;
1131  axis['fXmax'] = xmax;
1132  this['fNcells'] = -1;
1133  this['fArray'].length = -1;
1134  var errors = this['fSumw2']['fN'];
1135  if (errors) ['fSumw2'].length = this['fNcells'];
1136  axis['fTimeDisplay'] = timedisp;
1137 
1138  Reset("ICE"); // reset content and error
1139  this['fSumw2'].splice(0, this['fSumw2'].length);
1140  this['fMinimum'] = -1111;
1141  this['fMaximum'] = -1111;
1142 
1143  //now loop on all bins and refill
1144  var oldEntries = this['fEntries'];
1145  var bin, ibin, bins;
1146  for (ibin = 0; ibin < this['fNcells']; ibin++) {
1147  bins = this.getBinXYZ(ibin);
1148  bin = hold.getBin(bins['binx'],bins['biny'],bins['binz']);
1149  // NOTE that overflow in hold will be not considered
1150  if (bins['binx'] > nbxold || bins['biny'] > nbyold || bins['binz'] > nbzold) bin = -1;
1151  if (bin > 0) {
1152  var cu = hold.getBinContent(bin);
1153  this['fArray'][bin] += cu;
1154  if (errors) this['fSumw2']['fArray'][ibin] += hold['fSumw2']['fArray'][bin];
1155  }
1156  }
1157  this['fEntries'] = oldEntries;
1158  delete hold;
1159  };
1160  obj['resetStats'] = function() {
1161  // Reset the statistics including the number of entries
1162  // and replace with values calculates from bin content
1163  // The number of entries is set to the total bin content or (in case of weighted histogram)
1164  // to number of effective entries
1165  this['fTsumw'] = 0;
1166  this['fEntries'] = 1; // to force re-calculation of the statistics in TH1::GetStats
1167  var stats = this.getStats();
1168  this['fTsumw'] = stats[0];
1169  this['fTsumw2'] = stats[1];
1170  this['fTsumwx'] = stats[2];
1171  this['fTsumwx2'] = stats[3];
1172  this['fEntries'] = Math.abs(this['fTsumw']);
1173  // use effective entries for weighted histograms: (sum_w) ^2 / sum_w2
1174  if (this['fSumw2']['fN'] > 0 && this['fTsumw'] > 0 && stats[1] > 0 )
1175  this['fEntries'] = stats[0] * stats[0] / stats[1];
1176  }
1177  obj['setBinContent'] = function(bin, content) {
1178  // Set bin content
1179  // see convention for numbering bins in TH1::GetBin
1180  // In case the bin number is greater than the number of bins and
1181  // the timedisplay option is set or the kCanRebin bit is set,
1182  // the number of bins is automatically doubled to accommodate the new bin
1183 
1184  this['fEntries']++;
1185  this['fTsumw'] = 0;
1186  if (bin < 0) return;
1187  if (bin >= this['fNcells']-1) {
1188  if (this['fXaxis']['fTimeDisplay'] || this.TestBit(JSROOT.TH1StatusBits.kCanRebin) ) {
1189  while (bin >= this['fNcells']-1) this.labelsInflate();
1190  } else {
1191  if (bin == this['fNcells']-1) this['fArray'][bin] = content;
1192  return;
1193  }
1194  }
1195  this['fArray'][bin] = content;
1196  };
1197  obj['sumw2'] = function() {
1198  // Create structure to store sum of squares of weights*-*-*-*-*-*-*-*
1199  //
1200  // if histogram is already filled, the sum of squares of weights
1201  // is filled with the existing bin contents
1202  //
1203  // The error per bin will be computed as sqrt(sum of squares of weight)
1204  // for each bin.
1205  //
1206  // This function is automatically called when the histogram is created
1207  // if the static function TH1::SetDefaultSumw2 has been called before.
1208 
1209  if (this['fSumw2']['fN'] == this['fNcells']) {
1210  return;
1211  }
1212  this['fSumw2'].length = this['fNcells'];
1213  if ( this['fEntries'] > 0 ) {
1214  for (var bin=0; bin<this['fNcells']; bin++) {
1215  this['fSumw2']['fArray'][bin] = Math.abs(this.getBinContent(bin));
1216  }
1217  }
1218  };
1219  }
1220  if (obj_typename.indexOf("TH1") == 0) {
1221  obj['fDimension'] = 1;
1222  obj['getBinContent'] = function(bin) {
1223  if (bin < 0) bin = 0;
1224  if (bin >= this['fNcells']) bin = this['fNcells']-1;
1225  return this['fArray'][bin];
1226  };
1227  obj['getStats'] = function() {
1228  // fill the array stats from the contents of this histogram
1229  // The array stats must be correctly dimensioned in the calling program.
1230  // stats[0] = sumw
1231  // stats[1] = sumw2
1232  // stats[2] = sumwx
1233  // stats[3] = sumwx2
1234  // Loop on bins (possibly including underflows/overflows)
1235  var bin, binx, w, err, x, stats = new Array(0,0,0,0,0);
1236  // case of labels with rebin of axis set
1237  // statistics in x does not make any sense - set to zero
1238  if (this['fXaxis']['fLabels'] && this.TestBit(JSROOT.TH1StatusBits.kCanRebin) ) {
1239  stats[0] = this['fTsumw'];
1240  stats[1] = this['fTsumw2'];
1241  stats[2] = 0;
1242  stats[3] = 0;
1243  }
1244  else if ((this['fTsumw'] == 0 && this['fEntries'] > 0) ||
1245  this['fXaxis'].TestBit(JSROOT.EAxisBits.kAxisRange)) {
1246  for (bin=0;bin<4;bin++) stats[bin] = 0;
1247 
1248  var firstBinX = this['fXaxis'].getFirst();
1249  var lastBinX = this['fXaxis'].getLast();
1250  for (binx = firstBinX; binx <= lastBinX; binx++) {
1251  x = this['fXaxis'].getBinCenter(binx);
1252  w = this.getBinContent(binx);
1253  err = Math.abs(this.getBinError(binx));
1254  stats[0] += w;
1255  stats[1] += err*err;
1256  stats[2] += w*x;
1257  stats[3] += w*x*x;
1258  }
1259  } else {
1260  stats[0] = this['fTsumw'];
1261  stats[1] = this['fTsumw2'];
1262  stats[2] = this['fTsumwx'];
1263  stats[3] = this['fTsumwx2'];
1264  }
1265  return stats;
1266  };
1267  }
1268  if (obj_typename.indexOf("TH2") == 0) {
1269  obj['fDimension'] = 2;
1270  obj['getBin'] = function(x, y) {
1271  var nx = this['fXaxis']['fNbins']+2;
1272  return (x + nx * y);
1273  };
1274  obj['getBinContent'] = function(x, y) {
1275  return this['fArray'][this.getBin(x, y)];
1276  };
1277  obj['getStats'] = function() {
1278  var bin, binx, biny, stats = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0);
1279  if ((this['fTsumw'] == 0 && this['fEntries'] > 0) || this['fXaxis'].TestBit(JSROOT.EAxisBits.kAxisRange) || this['fYaxis'].TestBit(JSROOT.EAxisBits.kAxisRange)) {
1280  var firstBinX = this['fXaxis'].getFirst();
1281  var lastBinX = this['fXaxis'].getLast();
1282  var firstBinY = this['fYaxis'].getFirst();
1283  var lastBinY = this['fYaxis'].getLast();
1284  // include underflow/overflow if TH1::StatOverflows(kTRUE) in case no range is set on the axis
1285  if (this['fgStatOverflows']) {
1286  if ( !this['fXaxis'].TestBit(JSROOT.EAxisBits.kAxisRange) ) {
1287  if (firstBinX == 1) firstBinX = 0;
1288  if (lastBinX == this['fXaxis']['fNbins'] ) lastBinX += 1;
1289  }
1290  if ( !this['fYaxis'].TestBit(JSROOT.EAxisBits.kAxisRange) ) {
1291  if (firstBinY == 1) firstBinY = 0;
1292  if (lastBinY == this['fYaxis']['fNbins'] ) lastBinY += 1;
1293  }
1294  }
1295  for (biny = firstBinY; biny <= lastBinY; biny++) {
1296  y = this['fYaxis'].getBinCenter(biny);
1297  for (binx = firstBinX; binx <= lastBinX; binx++) {
1298  bin = this.getBin(binx,biny);
1299  x = this['fXaxis'].getBinCenter(binx);
1300  w = this.GetBinContent(bin);
1301  err = Math.abs(this.getBinError(bin));
1302  stats[0] += w;
1303  stats[1] += err*err;
1304  stats[2] += w*x;
1305  stats[3] += w*x*x;
1306  stats[4] += w*y;
1307  stats[5] += w*y*y;
1308  stats[6] += w*x*y;
1309  }
1310  }
1311  } else {
1312  stats[0] = this['fTsumw'];
1313  stats[1] = this['fTsumw2'];
1314  stats[2] = this['fTsumwx'];
1315  stats[3] = this['fTsumwx2'];
1316  stats[4] = this['fTsumwy'];
1317  stats[5] = this['fTsumwy2'];
1318  stats[6] = this['fTsumwxy'];
1319  }
1320  return stats;
1321  };
1322  }
1323  if (obj_typename.indexOf("TH3") == 0) {
1324  obj['fDimension'] = 3;
1325  obj['getBin'] = function(x, y, z) {
1326  var nx = this['fXaxis']['fNbins']+2;
1327  if (x < 0) x = 0;
1328  if (x >= nx) x = nx-1;
1329  var ny = this['fYaxis']['fNbins']+2;
1330  if (y < 0) y = 0;
1331  if (y >= ny) y = ny-1;
1332  return (x + nx * (y + ny * z));
1333  };
1334  obj['getBinContent'] = function(x, y, z) {
1335  return this['fArray'][this.getBin(x, y, z)];
1336  };
1337  obj['getStats'] = function() {
1338  var bin, binx, biny, binz, stats = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0);
1339  if ((obj['fTsumw'] == 0 && obj['fEntries'] > 0) || obj['fXaxis'].TestBit(JSROOT.EAxisBits.kAxisRange) || obj['fYaxis'].TestBit(JSROOT.EAxisBits.kAxisRange) || obj['fZaxis'].TestBit(JSROOT.EAxisBits.kAxisRange)) {
1340  var firstBinX = obj['fXaxis'].getFirst();
1341  var lastBinX = obj['fXaxis'].getLast();
1342  var firstBinY = obj['fYaxis'].getFirst();
1343  var lastBinY = obj['fYaxis'].getLast();
1344  var firstBinZ = obj['fZaxis'].getFirst();
1345  var lastBinZ = obj['fZaxis'].getLast();
1346  // include underflow/overflow if TH1::StatOverflows(kTRUE) in case no range is set on the axis
1347  if (obj['fgStatOverflows']) {
1348  if ( !obj['fXaxis'].TestBit(JSROOT.EAxisBits.kAxisRange) ) {
1349  if (firstBinX == 1) firstBinX = 0;
1350  if (lastBinX == obj['fXaxis']['fNbins'] ) lastBinX += 1;
1351  }
1352  if ( !obj['fYaxis'].TestBit(JSROOT.EAxisBits.kAxisRange) ) {
1353  if (firstBinY == 1) firstBinY = 0;
1354  if (lastBinY == obj['fYaxis']['fNbins'] ) lastBinY += 1;
1355  }
1356  if ( !obj['fZaxis'].TestBit(JSROOT.EAxisBits.kAxisRange) ) {
1357  if (firstBinZ == 1) firstBinZ = 0;
1358  if (lastBinZ == obj['fZaxis']['fNbins'] ) lastBinZ += 1;
1359  }
1360  }
1361  for (binz = firstBinZ; binz <= lastBinZ; binz++) {
1362  z = obj['fZaxis'].getBinCenter(binz);
1363  for (biny = firstBinY; biny <= lastBinY; biny++) {
1364  y = obj['fYaxis'].getBinCenter(biny);
1365  for (binx = firstBinX; binx <= lastBinX; binx++) {
1366  bin = obj.getBin(binx,biny,binz);
1367  x = obj['fXaxis'].getBinCenter(binx);
1368  w = obj.GetBinContent(bin);
1369  err = Math.abs(obj.getBinError(bin));
1370  stats[0] += w;
1371  stats[1] += err*err;
1372  stats[2] += w*x;
1373  stats[3] += w*x*x;
1374  stats[4] += w*y;
1375  stats[5] += w*y*y;
1376  stats[6] += w*x*y;
1377  stats[7] += w*z;
1378  stats[8] += w*z*z;
1379  stats[9] += w*x*z;
1380  stats[10] += w*y*z;
1381  }
1382  }
1383  }
1384  } else {
1385  stats[0] = obj['fTsumw'];
1386  stats[1] = obj['fTsumw2'];
1387  stats[2] = obj['fTsumwx'];
1388  stats[3] = obj['fTsumwx2'];
1389  stats[4] = obj['fTsumwy'];
1390  stats[5] = obj['fTsumwy2'];
1391  stats[6] = obj['fTsumwxy'];
1392  stats[7] = obj['fTsumwz'];
1393  stats[8] = obj['fTsumwz2'];
1394  stats[9] = obj['fTsumwxz'];
1395  stats[10] =obj['fTsumwyz'];
1396  }
1397  return stats;
1398  };
1399  }
1400  if (obj_typename.indexOf("THStack") == 0) {
1401  obj['buildStack'] = function() {
1402  // build sum of all histograms
1403  // Build a separate list fStack containing the running sum of all histograms
1404  if ('fStack' in this) return;
1405  if (!'fHists' in this) return;
1406  var nhists = this['fHists'].arr.length;
1407  if (nhists <= 0) return;
1408  this['fStack'] = JSROOT.Create("TList");
1409  var h = JSROOT.clone(this['fHists'].arr[0]);
1410  this['fStack'].arr.push(h);
1411  for (var i=1;i<nhists;i++) {
1412  h = JSROOT.clone(this['fHists'].arr[i]);
1413  h.add(this['fStack'].arr[i-1]);
1414  this['fStack'].arr.splice(i, 1, h);
1415  }
1416  };
1417  obj['getMaximum'] = function(option) {
1418  // returns the maximum of all added histograms
1419  // returns the maximum of all histograms if option "nostack".
1420  var opt = option.toLowerCase();
1421  var lerr = false;
1422  if (opt.indexOf("e") != -1) lerr = true;
1423  var them = 0, themax = -1e300, c1, e1;
1424  if (!'fHists' in this) return 0;
1425  var nhists = this['fHists'].arr.length;
1426  var first, last;
1427  if (opt.indexOf("nostack") == -1) {
1428  this.buildStack();
1429  var h = this['fStack'].arr[nhists-1];
1430  themax = h.getMaximum();
1431  } else {
1432  for (var i=0;i<nhists;i++) {
1433  h = this['fHists'].arr[i];
1434  them = h.getMaximum();
1435  if (them > themax) themax = them;
1436  }
1437  }
1438  if (lerr) {
1439  for (var i=0;i<nhists;i++) {
1440  h = this['fHists'].arr[i];
1441  first = h['fXaxis'].getFirst();
1442  last = h['fXaxis'].getLast();
1443  for (var j=first; j<=last;j++) {
1444  e1 = h.getBinError(j);
1445  c1 = h.getBinContent(j);
1446  themax = Math.max(themax, c1+e1);
1447  }
1448  }
1449  }
1450  return themax;
1451  };
1452  obj['getMinimum'] = function(option, pad) {
1453  // returns the minimum of all added histograms
1454  // returns the minimum of all histograms if option "nostack".
1455  var opt = option.toLowerCase();
1456  var lerr = false;
1457  if (opt.indexOf("e") == -1) lerr = true;
1458  var them = 0, themin = 1e300, c1, e1;
1459  if (!'fHists' in this) return 0;
1460  var nhists = this['fHists'].arr.length;
1461  var first, last;
1462  if (opt.indexOf("nostack") == -1) {
1463  this.buildStack();
1464  var h = this['fStack'].arr[nhists-1];
1465  themin = h.getMinimum();
1466  } else {
1467  for (var i=0;i<nhists;i++) {
1468  h = this['fHists'].arr[i];
1469  them = h.getMinimum();
1470  if (them <= 0 && pad && pad['fLogy']) them = h.getMinimum(0);
1471  if (them < themin) themin = them;
1472  }
1473  }
1474  if (lerr) {
1475  for (var i=0;i<nhists;i++) {
1476  h = this['fHists'].arr[i];
1477  first = h['fXaxis'].getFirst();
1478  last = h['fXaxis'].getLast();
1479  for (var j=first;j<=last;j++) {
1480  e1 = h.getBinError(j);
1481  c1 = h.getBinContent(j);
1482  themin = Math.min(themin, c1 - e1);
1483  }
1484  }
1485  }
1486  return themin;
1487  };
1488  }
1489  if ((obj_typename.indexOf("TH1") == 0) ||
1490  (obj_typename.indexOf("TH2") == 0) ||
1491  (obj_typename.indexOf("TH3") == 0) ||
1492  (obj_typename.indexOf("TProfile") == 0)) {
1493  obj['getMean'] = function(axis) {
1494  if (axis < 1 || (axis > 3 && axis < 11) || axis > 13) return 0;
1495  var stats = this.getStats();
1496  if (stats[0] == 0) return 0;
1497  var ax = new Array(2,4,7);
1498  return stats[ax[axis-1]]/stats[0];
1499  };
1500  obj['getRMS'] = function(axis) {
1501  if (axis < 1 || (axis > 3 && axis < 11) || axis > 13) return 0;
1502  var stats = this.getStats();
1503  if (stats[0] == 0) return 0;
1504  var ax = new Array(2,4,7);
1505  var axm = ax[axis%10 - 1];
1506  var x = stats[axm]/stats[0];
1507  var rms2 = Math.abs(stats[axm+1]/stats[0] -x*x);
1508  return Math.sqrt(rms2);
1509  };
1510  }
1511  if (obj_typename.indexOf("TProfile") == 0) {
1512  obj['getBinContent'] = function(bin) {
1513  if (bin < 0 || bin >= this['fNcells']) return 0;
1514  if (this['fBinEntries'][bin] < 1e-300) return 0;
1515  if (!this['fArray']) return 0;
1516  return this['fArray'][bin]/this['fBinEntries'][bin];
1517  };
1518  obj['getBinEffectiveEntries'] = function(bin) {
1519  if (bin < 0 || bin >= this['fNcells']) return 0;
1520  var sumOfWeights = this['fBinEntries'][bin];
1521  if ( this['fBinSumw2'].length == 0 || this['fBinSumw2'].length != this['fNcells']) {
1522  // this can happen when reading an old file
1523  return sumOfWeights;
1524  }
1525  var sumOfWeightsSquare = this['fSumw2'][bin];
1526  return ( sumOfWeightsSquare > 0 ? sumOfWeights * sumOfWeights / sumOfWeightsSquare : 0 );
1527  };
1528  obj['getStats'] = function() {
1529  var bin, binx, stats = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0);
1530  if (this['fTsumw'] < 1e-300 || this['fXaxis'].TestBit(JSROOT.EAxisBits.kAxisRange)) {
1531  var firstBinX = this['fXaxis'].getFirst();
1532  var lastBinX = this['fXaxis'].getLast();
1533  for (binx = this['firstBinX']; binx <= lastBinX; binx++) {
1534  var w = onj['fBinEntries'][binx];
1535  var w2 = (this['fN'] ? this['fBinSumw2'][binx] : w);
1536  var x = fXaxis.GetBinCenter(binx);
1537  stats[0] += w;
1538  stats[1] += w2;
1539  stats[2] += w*x;
1540  stats[3] += w*x*x;
1541  stats[4] += this['fArray'][binx];
1542  stats[5] += this['fSumw2'][binx];
1543  }
1544  } else {
1545  if (this['fTsumwy'] < 1e-300 && this['fTsumwy2'] < 1e-300) {
1546  //this case may happen when processing TProfiles with version <=3
1547  for (binx=this['fXaxis'].getFirst();binx<=this['fXaxis'].getLast();binx++) {
1548  this['fTsumwy'] += this['fArray'][binx];
1549  this['fTsumwy2'] += this['fSumw2'][binx];
1550  }
1551  }
1552  stats[0] = this['fTsumw'];
1553  stats[1] = this['fTsumw2'];
1554  stats[2] = this['fTsumwx'];
1555  stats[3] = this['fTsumwx2'];
1556  stats[4] = this['fTsumwy'];
1557  stats[5] = this['fTsumwy2'];
1558  }
1559  return stats;
1560  };
1561  obj['getBinError'] = function(bin) {
1562  if (bin < 0 || bin >= this['fNcells']) return 0;
1563  var cont = this['fArray'][bin]; // sum of bin w *y
1564  var sum = this['fBinEntries'][bin]; // sum of bin weights
1565  var err2 = this['fSumw2'][bin]; // sum of bin w * y^2
1566  var neff = this.getBinEffectiveEntries(bin); // (sum of w)^2 / (sum of w^2)
1567  if (sum < 1e-300) return 0; // for empty bins
1568  // case the values y are gaussian distributed y +/- sigma and w = 1/sigma^2
1569  if (this['fErrorMode'] == EErrorType.kERRORSPREADG) {
1570  return (1.0/Math.sqrt(sum));
1571  }
1572  // compute variance in y (eprim2) and standard deviation in y (eprim)
1573  var contsum = cont/sum;
1574  var eprim2 = Math.abs(err2/sum - contsum*contsum);
1575  var eprim = Math.sqrt(eprim2);
1576  if (this['fErrorMode'] == EErrorType.kERRORSPREADI) {
1577  if (eprim != 0) return eprim/Math.sqrt(neff);
1578  // in case content y is an integer (so each my has an error +/- 1/sqrt(12)
1579  // when the std(y) is zero
1580  return (1.0/Math.sqrt(12*neff));
1581  }
1582  // if approximate compute the sums (of w, wy and wy2) using all the bins
1583  // when the variance in y is zero
1584  var testing = 1;
1585  if (err2 != 0 && neff < 5) testing = eprim2*sum/err2;
1586  if (this['fgApproximate'] && (testing < 1.e-4 || eprim2 < 1e-6)) { //3.04
1587  var stats = this.getStats();
1588  var ssum = stats[0];
1589  // for 1D profile
1590  var idx = 4; // index in the stats array for 1D
1591  var scont = stats[idx];
1592  var serr2 = stats[idx+1];
1593  // compute mean and variance in y
1594  var scontsum = scont/ssum; // global mean
1595  var seprim2 = Math.abs(serr2/ssum - scontsum*scontsum); // global variance
1596  eprim = 2*Math.sqrt(seprim2); // global std (why factor of 2 ??)
1597  sum = ssum;
1598  }
1599  sum = Math.abs(sum);
1600  // case option "S" return standard deviation in y
1601  if (this['fErrorMode'] == EErrorType.kERRORSPREAD) return eprim;
1602  // default case : fErrorMode = kERRORMEAN
1603  // return standard error on the mean of y
1604  return (eprim/Math.sqrt(neff));
1605  };
1606  }
1607  };
1608 
1609 
1610  // math methods for Javascript ROOT
1611 
1612  JSROOT.Math = {};
1613 
1614 
1615  JSROOT.Math.lgam = function( x ) {
1616  var p, q, u, w, z;
1617  var i;
1618 
1619  var sgngam = 1;
1620 
1621  if (x >= Number.POSITIVE_INFINITY)
1622  return(Number.POSITIVE_INFINITY);
1623 
1624  if ( x < -34.0 ) {
1625  q = -x;
1626  w = this.lgam(q);
1627  p = Math.floor(q);
1628  if ( p==q )//_unur_FP_same(p,q)
1629  return (Number.POSITIVE_INFINITY);
1630  i = Math.round(p);
1631  if ( (i & 1) == 0 )
1632  sgngam = -1;
1633  else
1634  sgngam = 1;
1635  z = q - p;
1636  if ( z > 0.5 ) {
1637  p += 1.0;
1638  z = p - q;
1639  }
1640  z = q * Math.sin( Math.PI * z );
1641  if ( z < 1e-300 )
1642  return (Number.POSITIVE_INFINITY);
1643  z = Math.log(Math.PI) - Math.log( z ) - w;
1644  return( z );
1645  }
1646  if ( x < 13.0 ) {
1647  z = 1.0;
1648  p = 0.0;
1649  u = x;
1650  while ( u >= 3.0 ) {
1651  p -= 1.0;
1652  u = x + p;
1653  z *= u;
1654  }
1655  while ( u < 2.0 ) {
1656  if ( u < 1e-300 )
1657  return (Number.POSITIVE_INFINITY);
1658  z /= u;
1659  p += 1.0;
1660  u = x + p;
1661  }
1662  if ( z < 0.0 ) {
1663  sgngam = -1;
1664  z = -z;
1665  }
1666  else
1667  sgngam = 1;
1668  if ( u == 2.0 )
1669  return( Math.log(z) );
1670  p -= 2.0;
1671  x = x + p;
1672  p = x * this.Polynomialeval(x, B, 5 ) / this.Polynomial1eval( x, C, 6);
1673  return( Math.log(z) + p );
1674  }
1675  if ( x > kMAXLGM )
1676  return( sgngam * Number.POSITIVE_INFINITY );
1677 
1678  q = ( x - 0.5 ) * Math.log(x) - x + LS2PI;
1679  if ( x > 1.0e8 )
1680  return( q );
1681 
1682  p = 1.0/(x*x);
1683  if ( x >= 1000.0 )
1684  q += ((7.9365079365079365079365e-4 * p
1685  - 2.7777777777777777777778e-3) *p
1686  + 0.0833333333333333333333) / x;
1687  else
1688  q += this.Polynomialeval( p, A, 4 ) / x;
1689  return( q );
1690  };
1691 
1692  /*
1693  * calculates a value of a polynomial of the form:
1694  * a[0]x^N+a[1]x^(N-1) + ... + a[N]
1695  */
1696  JSROOT.Math.Polynomialeval = function(x, a, N) {
1697  if (N==0) return a[0];
1698  else {
1699  var pom = a[0];
1700  for (var i=1; i <= N; i++)
1701  pom = pom *x + a[i];
1702  return pom;
1703  }
1704  };
1705 
1706  /*
1707  * calculates a value of a polynomial of the form:
1708  * x^N+a[0]x^(N-1) + ... + a[N-1]
1709  */
1710  JSROOT.Math.Polynomial1eval = function(x, a, N) {
1711  if (N==0) return a[0];
1712  else {
1713  var pom = x + a[0];
1714  for (var i=1; i < N; i++)
1715  pom = pom *x + a[i];
1716  return pom;
1717  }
1718  };
1719 
1720  JSROOT.Math.ndtri = function( y0 ) {
1721  if ( y0 <= 0.0 )
1722  return( Number.NEGATIVE_INFINITY );
1723  if ( y0 >= 1.0 )
1724  return( Number.POSITIVE_INFINITY );
1725 
1726  var P0 = new Array(
1727  -5.99633501014107895267E1,
1728  9.80010754185999661536E1,
1729  -5.66762857469070293439E1,
1730  1.39312609387279679503E1,
1731  -1.23916583867381258016E0
1732  );
1733 
1734  var Q0 = new Array(
1735  1.95448858338141759834E0,
1736  4.67627912898881538453E0,
1737  8.63602421390890590575E1,
1738  -2.25462687854119370527E2,
1739  2.00260212380060660359E2,
1740  -8.20372256168333339912E1,
1741  1.59056225126211695515E1,
1742  -1.18331621121330003142E0
1743  );
1744 
1745  var P1 = new Array(
1746  4.05544892305962419923E0,
1747  3.15251094599893866154E1,
1748  5.71628192246421288162E1,
1749  4.40805073893200834700E1,
1750  1.46849561928858024014E1,
1751  2.18663306850790267539E0,
1752  -1.40256079171354495875E-1,
1753  -3.50424626827848203418E-2,
1754  -8.57456785154685413611E-4
1755  );
1756 
1757  var Q1 = new Array(
1758  1.57799883256466749731E1,
1759  4.53907635128879210584E1,
1760  4.13172038254672030440E1,
1761  1.50425385692907503408E1,
1762  2.50464946208309415979E0,
1763  -1.42182922854787788574E-1,
1764  -3.80806407691578277194E-2,
1765  -9.33259480895457427372E-4
1766  );
1767 
1768  var P2 = new Array(
1769  3.23774891776946035970E0,
1770  6.91522889068984211695E0,
1771  3.93881025292474443415E0,
1772  1.33303460815807542389E0,
1773  2.01485389549179081538E-1,
1774  1.23716634817820021358E-2,
1775  3.01581553508235416007E-4,
1776  2.65806974686737550832E-6,
1777  6.23974539184983293730E-9
1778  );
1779 
1780  var Q2 = new Array(
1781  6.02427039364742014255E0,
1782  3.67983563856160859403E0,
1783  1.37702099489081330271E0,
1784  2.16236993594496635890E-1,
1785  1.34204006088543189037E-2,
1786  3.28014464682127739104E-4,
1787  2.89247864745380683936E-6,
1788  6.79019408009981274425E-9
1789  );
1790 
1791  var s2pi = 2.50662827463100050242e0;
1792  var code = 1;
1793  var y = y0;
1794  var x, z, y2, x0, x1;
1795 
1796  if ( y > (1.0 - 0.13533528323661269189) ) {
1797  y = 1.0 - y;
1798  code = 0;
1799  }
1800  if ( y > 0.13533528323661269189 ) {
1801  y = y - 0.5;
1802  y2 = y * y;
1803  x = y + y * (y2 * this.Polynomialeval( y2, P0, 4)/ this.Polynomial1eval( y2, Q0, 8 ));
1804  x = x * s2pi;
1805  return(x);
1806  }
1807  x = Math.sqrt( -2.0 * Math.log(y) );
1808  x0 = x - Math.log(x)/x;
1809  z = 1.0/x;
1810  if ( x < 8.0 )
1811  x1 = z * this.Polynomialeval( z, P1, 8 )/ this.Polynomial1eval( z, Q1, 8 );
1812  else
1813  x1 = z * this.Polynomialeval( z, P2, 8 )/ this.Polynomial1eval( z, Q2, 8 );
1814  x = x0 - x1;
1815  if ( code != 0 )
1816  x = -x;
1817  return( x );
1818  };
1819 
1820  JSROOT.Math.igami = function(a, y0) {
1821  var x0, x1, x, yl, yh, y, d, lgm, dithresh;
1822  var i, dir;
1823  var kMACHEP = 1.11022302462515654042363166809e-16;
1824 
1825  // check the domain
1826  if (a <= 0) {
1827  alert("igami : Wrong domain for parameter a (must be > 0)");
1828  return 0;
1829  }
1830  if (y0 <= 0) {
1831  return Number.POSITIVE_INFINITY;
1832  }
1833  if (y0 >= 1) {
1834  return 0;
1835  }
1836  /* bound the solution */
1837  var kMAXNUM = Number.MAX_VALUE;
1838  x0 = kMAXNUM;
1839  yl = 0;
1840  x1 = 0;
1841  yh = 1.0;
1842  dithresh = 5.0 * kMACHEP;
1843 
1844  /* approximation to inverse function */
1845  d = 1.0/(9.0*a);
1846  y = ( 1.0 - d - this.ndtri(y0) * Math.sqrt(d) );
1847  x = a * y * y * y;
1848 
1849  lgm = this.lgam(a);
1850 
1851  for( i=0; i<10; i++ ) {
1852  if ( x > x0 || x < x1 )
1853  break;
1854  y = igamc(a,x);
1855  if ( y < yl || y > yh )
1856  break;
1857  if ( y < y0 ) {
1858  x0 = x;
1859  yl = y;
1860  }
1861  else {
1862  x1 = x;
1863  yh = y;
1864  }
1865  /* compute the derivative of the function at this point */
1866  d = (a - 1.0) * Math.log(x) - x - lgm;
1867  if ( d < -kMAXLOG )
1868  break;
1869  d = -Math.exp(d);
1870  /* compute the step to the next approximation of x */
1871  d = (y - y0)/d;
1872  if ( Math.abs(d/x) < kMACHEP )
1873  return( x );
1874  x = x - d;
1875  }
1876  /* Resort to interval halving if Newton iteration did not converge. */
1877  d = 0.0625;
1878  if ( x0 == kMAXNUM ) {
1879  if ( x <= 0.0 )
1880  x = 1.0;
1881  while ( x0 == kMAXNUM ) {
1882  x = (1.0 + d) * x;
1883  y = igamc( a, x );
1884  if ( y < y0 ) {
1885  x0 = x;
1886  yl = y;
1887  break;
1888  }
1889  d = d + d;
1890  }
1891  }
1892  d = 0.5;
1893  dir = 0;
1894 
1895  for( i=0; i<400; i++ ) {
1896  x = x1 + d * (x0 - x1);
1897  y = igamc( a, x );
1898  lgm = (x0 - x1)/(x1 + x0);
1899  if ( Math.abs(lgm) < dithresh )
1900  break;
1901  lgm = (y - y0)/y0;
1902  if ( Math.abs(lgm) < dithresh )
1903  break;
1904  if ( x <= 0.0 )
1905  break;
1906  if ( y >= y0 ) {
1907  x1 = x;
1908  yh = y;
1909  if ( dir < 0 ) {
1910  dir = 0;
1911  d = 0.5;
1912  }
1913  else if ( dir > 1 )
1914  d = 0.5 * d + 0.5;
1915  else
1916  d = (y0 - yl)/(yh - yl);
1917  dir += 1;
1918  }
1919  else {
1920  x0 = x;
1921  yl = y;
1922  if ( dir > 0 ) {
1923  dir = 0;
1924  d = 0.5;
1925  }
1926  else if ( dir < -1 )
1927  d = 0.5 * d;
1928  else
1929  d = (y0 - yl)/(yh - yl);
1930  dir -= 1;
1931  }
1932  }
1933  return( x );
1934  };
1935 
1936  JSROOT.Math.gamma_quantile_c = function(z, alpha, theta) {
1937  return theta * this.igami( alpha, z);
1938  };
1939 
1940  JSROOT.Math.gamma_quantile = function(z, alpha, theta) {
1941  return theta * this.igami( alpha, 1.- z);
1942  };
1943 
1944  JSROOT.Math.log10 = function(n) {
1945  return Math.log(n) / Math.log(10);
1946  };
1947 
1948  JSROOT.Math.landau_pdf = function(x, xi, x0) {
1949  // LANDAU pdf : algorithm from CERNLIB G110 denlan
1950  // same algorithm is used in GSL
1951  if (xi <= 0) return 0;
1952  var v = (x - x0)/xi;
1953  var u, ue, us, denlan;
1954  var p1 = new Array(0.4259894875,-0.1249762550, 0.03984243700, -0.006298287635, 0.001511162253);
1955  var q1 = new Array(1.0 ,-0.3388260629, 0.09594393323, -0.01608042283, 0.003778942063);
1956  var p2 = new Array(0.1788541609, 0.1173957403, 0.01488850518, -0.001394989411, 0.0001283617211);
1957  var q2 = new Array(1.0 , 0.7428795082, 0.3153932961, 0.06694219548, 0.008790609714);
1958  var p3 = new Array(0.1788544503, 0.09359161662,0.006325387654, 0.00006611667319,-0.000002031049101);
1959  var q3 = new Array(1.0 , 0.6097809921, 0.2560616665, 0.04746722384, 0.006957301675);
1960  var p4 = new Array(0.9874054407, 118.6723273, 849.2794360, -743.7792444, 427.0262186);
1961  var q4 = new Array(1.0 , 106.8615961, 337.6496214, 2016.712389, 1597.063511);
1962  var p5 = new Array(1.003675074, 167.5702434, 4789.711289, 21217.86767, -22324.94910);
1963  var q5 = new Array(1.0 , 156.9424537, 3745.310488, 9834.698876, 66924.28357);
1964  var p6 = new Array(1.000827619, 664.9143136, 62972.92665, 475554.6998, -5743609.109);
1965  var q6 = new Array(1.0 , 651.4101098, 56974.73333, 165917.4725, -2815759.939);
1966  var a1 = new Array(0.04166666667,-0.01996527778, 0.02709538966);
1967  var a2 = new Array(-1.845568670,-4.284640743);
1968 
1969  if (v < -5.5) {
1970  u = Math.exp(v+1.0);
1971  if (u < 1e-10) return 0.0;
1972  ue = Math.exp(-1/u);
1973  us = Math.sqrt(u);
1974  denlan = 0.3989422803*(ue/us)*(1+(a1[0]+(a1[1]+a1[2]*u)*u)*u);
1975  } else if(v < -1) {
1976  u = Math.exp(-v-1);
1977  denlan = Math.exp(-u)*Math.sqrt(u)*
1978  (p1[0]+(p1[1]+(p1[2]+(p1[3]+p1[4]*v)*v)*v)*v)/
1979  (q1[0]+(q1[1]+(q1[2]+(q1[3]+q1[4]*v)*v)*v)*v);
1980  } else if(v < 1) {
1981  denlan = (p2[0]+(p2[1]+(p2[2]+(p2[3]+p2[4]*v)*v)*v)*v)/
1982  (q2[0]+(q2[1]+(q2[2]+(q2[3]+q2[4]*v)*v)*v)*v);
1983  } else if(v < 5) {
1984  denlan = (p3[0]+(p3[1]+(p3[2]+(p3[3]+p3[4]*v)*v)*v)*v)/
1985  (q3[0]+(q3[1]+(q3[2]+(q3[3]+q3[4]*v)*v)*v)*v);
1986  } else if(v < 12) {
1987  u = 1/v;
1988  denlan = u*u*(p4[0]+(p4[1]+(p4[2]+(p4[3]+p4[4]*u)*u)*u)*u)/
1989  (q4[0]+(q4[1]+(q4[2]+(q4[3]+q4[4]*u)*u)*u)*u);
1990  } else if(v < 50) {
1991  u = 1/v;
1992  denlan = u*u*(p5[0]+(p5[1]+(p5[2]+(p5[3]+p5[4]*u)*u)*u)*u)/
1993  (q5[0]+(q5[1]+(q5[2]+(q5[3]+q5[4]*u)*u)*u)*u);
1994  } else if(v < 300) {
1995  u = 1/v;
1996  denlan = u*u*(p6[0]+(p6[1]+(p6[2]+(p6[3]+p6[4]*u)*u)*u)*u)/
1997  (q6[0]+(q6[1]+(q6[2]+(q6[3]+q6[4]*u)*u)*u)*u);
1998  } else {
1999  u = 1/(v-v*Math.log(v)/(v+1));
2000  denlan = u*u*(1+(a2[0]+a2[1]*u)*u);
2001  }
2002  return denlan/xi;
2003  };
2004 
2005  JSROOT.Math.Landau = function(x, mpv, sigma, norm) {
2006  if (sigma <= 0) return 0;
2007  var den = JSROOT.Math.landau_pdf((x - mpv) / sigma, 1, 0);
2008  if (!norm) return den;
2009  return den/sigma;
2010  };
2011 
2012  JSROOT.Math.gaus = function(f, x, i) {
2013  return f['fParams'][i+0] * Math.exp(-0.5 * Math.pow((x-f['fParams'][i+1]) / f['fParams'][i+2], 2));
2014  };
2015 
2016  JSROOT.Math.gausn = function(f, x, i) {
2017  return JSROOT.Math.gaus(f, x, i)/(Math.sqrt(2 * Math.PI) * f['fParams'][i+2]);
2018  };
2019 
2020  JSROOT.Math.expo = function(f, x, i) {
2021  return Math.exp(f['fParams'][i+0] + f['fParams'][i+1] * x);
2022  };
2023 
2024  JSROOT.Math.landau = function(f, x, i) {
2025  return JSROOT.Math.Landau(x, f['fParams'][i+1],f['fParams'][i+2], false);
2026  };
2027 
2028  JSROOT.Math.landaun = function(f, x, i) {
2029  return JSROOT.Math.Landau(x, f['fParams'][i+1],f['fParams'][i+2], true);
2030  };
2031 
2032 })();
2033 
2035