toFrac.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.calc.toFrac"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.calc.toFrac"] = true;
  8. dojo.provide("dojox.calc.toFrac");
  9. (function(){
  10. var a = [];
  11. var sqrts = [2,3,5,6,7,10,11,13,14,15,17,19,21,22,23,26,29,
  12. 30,31,33,34,35,37,38,39,41,42,43,46,47,51,53,55,57,58,59,
  13. 61,62,65,66,67,69,70,71,73,74,77,78,79,82,83,85,86,87,89,91,93,94,95,97];
  14. var _fracHashInitialized = false;
  15. var i = -3;
  16. var d = 2;
  17. var epsilon = 1e-15 / 9;
  18. function _fracHashInit(searchNumber){
  19. // summary
  20. // make a fairly large hash table of some fractions, sqrts, etc
  21. var m, mt;
  22. while(i < sqrts.length){
  23. switch(i){
  24. case -3:
  25. m = 1;
  26. mt = '';
  27. break;
  28. case -2:
  29. m = Math.PI;
  30. mt = 'pi';
  31. break;
  32. case -1:
  33. m = Math.sqrt(Math.PI);
  34. mt = '\u221A(pi)';
  35. break;
  36. default:
  37. m = Math.sqrt(sqrts[i]);
  38. mt = "\u221A(" + sqrts[i] + ")";
  39. }
  40. while(d <= 100){
  41. for(n = 1; n < (m == 1 ? d : 100); n++){
  42. var r = m * n / d;
  43. var f = dojox.calc.approx(r);
  44. if(!(f in a)){
  45. // make sure that it is simplified so that toFrac(pi) doesn't get 2*pi/2
  46. if(n==d){
  47. n=1;
  48. d=1;
  49. }
  50. a[f] = {n:n, d:d, m:m, mt:mt};
  51. if(f == searchNumber){ searchNumber = undefined; } // found number, so return and finish hash in nbackground
  52. }
  53. }
  54. d++;
  55. if(searchNumber == undefined){
  56. setTimeout(function(){ _fracHashInit() }, 1);
  57. return;
  58. }
  59. }
  60. d = 2;
  61. i++;
  62. }
  63. _fracHashInitialized = true;
  64. }
  65. // this 1 is standard and the other is advanced and could be a
  66. // separate dojo.require if the user wants the function (and slow init)
  67. function isInt(n){
  68. return Math.floor(n) == n;
  69. }
  70. // make the hash
  71. _fracHashInit();
  72. // advanced _fracLookup
  73. function _fracLookup(number){
  74. function retryWhenInitialized(){
  75. _fracHashInit(number);
  76. return _fracLookup(number);
  77. }
  78. number = Math.abs(number);
  79. var f = a[dojox.calc.approx(number)];
  80. if(!f && !_fracHashInitialized){
  81. return retryWhenInitialized();
  82. }
  83. if(!f){
  84. var i = Math.floor(number);
  85. if(i == 0) { return _fracHashInitialized ? null : retryWhenInitialized(); }
  86. var n = number % 1;
  87. if(n == 0){
  88. return { m: 1, mt: 1, n: number, d: 1 }
  89. }
  90. f = a[dojox.calc.approx(n)];
  91. if(!f || f.m != 1){
  92. var inv = dojox.calc.approx(1 / n);
  93. return isInt(inv) ? { m: 1, mt: 1, n: 1, d: inv } : (_fracHashInitialized ? null : retryWhenInitialized());
  94. }else{
  95. return { m: 1, mt: 1, n: (i * f.d + f.n), d: f.d };
  96. }
  97. }
  98. return f;
  99. }
  100. // add toFrac to the calculator
  101. dojo.mixin(dojox.calc, {
  102. toFrac: function(number){// get a string fraction for a decimal with a set range of numbers, based on the hash
  103. var f = _fracLookup(number);
  104. return f ? ((number < 0 ? '-' : '') + (f.m == 1 ? '' : (f.n == 1 ? '' : (f.n + '*'))) + (f.m == 1 ? f.n : f.mt) + ((f.d == 1 ? '' : '/' + f.d))) : number;
  105. //return f ? ((number < 0 ? '-' : '') + (f.m == 1 ? '' : (f.n == 1 ? '' : (f.n + '*'))) + (f.m == 1 ? f.n : f.mt) + '/' + f.d) : number;
  106. },
  107. pow: function(base, exponent){// pow benefits from toFrac because it can overcome many of the limitations set before the standard Math.pow
  108. // summary:
  109. // Computes base ^ exponent
  110. // Wrapper to Math.pow(base, exponent) to handle (-27) ^ (1/3)
  111. if(base>0||isInt(exponent)){
  112. return Math.pow(base, exponent);
  113. }else{
  114. var f = _fracLookup(exponent);
  115. if(base >= 0){
  116. return (f && f.m == 1)
  117. ? Math.pow(Math.pow(base, 1 / f.d), exponent < 0 ? -f.n : f.n) // 32 ^ (2/5) is much more accurate if done as (32 ^ (1/5)) ^ 2
  118. : Math.pow(base, exponent);
  119. }else{ // e.g. (1/3) root of -27 = -3, 1 / exponent must be an odd integer for a negative base
  120. return (f && f.d & 1) ? Math.pow(Math.pow(-Math.pow(-base, 1 / f.d), exponent < 0 ? -f.n : f.n), f.m) : NaN;
  121. }
  122. }
  123. }
  124. });
  125. /*
  126. function reduceError(number){
  127. var f = _fracLookup(number);
  128. if(!f){ f = _fracLookup(number); }
  129. return f ? ((number < 0 ? -1 : 1) * f.n * f.m / f.d) : number;
  130. }
  131. */
  132. })();
  133. }