[483] | 1 | define([ |
---|
| 2 | "dojo/_base/lang", |
---|
| 3 | "dojox/calc/_Executor" |
---|
| 4 | ], function(lang, calc) { |
---|
| 5 | |
---|
| 6 | var multiples; |
---|
| 7 | |
---|
| 8 | function _fracHashInit(){ |
---|
| 9 | var sqrts = [ |
---|
| 10 | 5,6,7,10,11,13,14,15,17,19,21,22,23,26,29, |
---|
| 11 | 30,31,33,34,35,37,38,39,41,42,43,46,47,51,53,55,57,58,59, |
---|
| 12 | 61,62,65,66,67,69,70,71,73,74,77,78,79,82,83,85,86,87,89,91,93,94,95,97 |
---|
| 13 | ]; |
---|
| 14 | multiples = { "1":1, "\u221A(2)":Math.sqrt(2), "\u221A(3)":Math.sqrt(3), "pi":Math.PI }; |
---|
| 15 | // populate the rest of the multiples array |
---|
| 16 | for(var i in sqrts){ |
---|
| 17 | var n = sqrts[i]; |
---|
| 18 | multiples["\u221A("+n+")"] = Math.sqrt(n); |
---|
| 19 | } |
---|
| 20 | multiples["\u221A(pi)"] = Math.sqrt(Math.PI); |
---|
| 21 | } |
---|
| 22 | |
---|
| 23 | function _fracLookup(number){ |
---|
| 24 | function findSimpleFraction(fraction){ |
---|
| 25 | var denom1Low = Math.floor(1 / fraction); |
---|
| 26 | // fraction <= 1/denom1Low |
---|
| 27 | var quotient = calc.approx(1 / denom1Low); |
---|
| 28 | if(quotient == fraction){ return { n:1, d:denom1Low }; } |
---|
| 29 | var denom1High = denom1Low + 1; |
---|
| 30 | // 1/denom1High <= fraction < 1/denom1Low |
---|
| 31 | quotient = calc.approx(1 / denom1High); |
---|
| 32 | if(quotient == fraction){ return { n:1, d:denom1High }; } |
---|
| 33 | if(denom1Low >= 50){ return null; } // only 1's in the numerator beyond this point |
---|
| 34 | // 1/denom1High < fraction < 1/denom1Low |
---|
| 35 | var denom2 = denom1Low + denom1High; |
---|
| 36 | quotient = calc.approx(2 / denom2); |
---|
| 37 | // 1/denom1High < 2/(denom1Low+denom1High) < 1/denom1Low |
---|
| 38 | if(quotient == fraction){ return { n:2, d:denom2 }; } |
---|
| 39 | if(denom1Low >= 34){ return null; } // only 1's and 2's in the numerator beyond this point |
---|
| 40 | var less2 = fraction < quotient; |
---|
| 41 | // if less2 |
---|
| 42 | // 1/denom1High < fraction < 2/(denom1Low+denom1High) |
---|
| 43 | // else |
---|
| 44 | // 2/(denom1Low+denom1High) < fraction < 1/denom1Low |
---|
| 45 | var denom4 = denom2 * 2 + (less2 ? 1 : -1); |
---|
| 46 | quotient = calc.approx(4 / denom4); |
---|
| 47 | // 1/denom1High < 4/(2*denom1Low+2*denom1High+1) < 2/(denom1Low+denom1High) < 4/(2*denom1Low+2*denom1High-1) < 1/denom1Low |
---|
| 48 | if(quotient == fraction){ return { n:4, d:denom4 }; } |
---|
| 49 | var less4 = fraction < quotient; |
---|
| 50 | // we've already checked for 1, 2 and 4, but now see if we need to check for 3 in the numerator |
---|
| 51 | if((less2 && !less4) || (!less2 && less4)){ |
---|
| 52 | var denom3 = (denom2 + denom4) >> 1; |
---|
| 53 | quotient = calc.approx(3 / denom3); |
---|
| 54 | // 1/denom1High < 4/(2*denom1Low+2*denom1High+1) < 3/((3*denom1Low+3*denom1High+1)/2) < 2/(denom1Low+denom1High) < 3/((3*denom1Low+3*denom1High-1)/2) < 4/(2*denom1Low+2*denom1High-1) < 1/denom1Low |
---|
| 55 | if(quotient == fraction){ return { n:3, d:denom3 }; } |
---|
| 56 | } |
---|
| 57 | if(denom1Low >= 20){ return null; } // only 1's, 2's, 3's, and 4's in the numerator beyond this point |
---|
| 58 | // if less2 |
---|
| 59 | // if less4 |
---|
| 60 | // 1/denom1High < fraction < 4/(2*denom1Low+2*denom1High+1) |
---|
| 61 | // else |
---|
| 62 | // 4/(2*denom1Low+2*denom1High+1) < fraction < 2/(denom1Low+denom1High) |
---|
| 63 | // else |
---|
| 64 | // if less4 |
---|
| 65 | // 2/(denom1Low+denom1High) < fraction < 4/(2*denom1Low+2*denom1High-1) |
---|
| 66 | // else |
---|
| 67 | // 4/(2*denom1Low+2*denom1High-1) < fraction < 1/denom1Low |
---|
| 68 | var smallestDenom = denom2 + denom1Low * 2; |
---|
| 69 | var largestDenom = smallestDenom + 2; |
---|
| 70 | for(var numerator = 5; smallestDenom <= 100; numerator++){ // start with 5 in the numerator |
---|
| 71 | smallestDenom += denom1Low; |
---|
| 72 | largestDenom += denom1High; |
---|
| 73 | var startDenom = less2 ? ((largestDenom + smallestDenom + 1) >> 1) : smallestDenom; |
---|
| 74 | var stopDenom = less2 ? largestDenom : ((largestDenom + smallestDenom - 1) >> 1); |
---|
| 75 | startDenom = less4 ? ((startDenom + stopDenom) >> 1) : startDenom; |
---|
| 76 | stopDenom = less4 ? stopDenom : ((startDenom + stopDenom) >> 1); |
---|
| 77 | for(var thisDenom = startDenom; thisDenom <= stopDenom; thisDenom++){ |
---|
| 78 | if(numerator & 1 == 0 && thisDenom & 1 == 0){ continue; } // skip where n and d are both even |
---|
| 79 | quotient = calc.approx(numerator / thisDenom); |
---|
| 80 | if(quotient == fraction){ return { n:numerator, d:thisDenom }; } |
---|
| 81 | if(quotient < fraction){ break; } // stop since the values will just get smaller |
---|
| 82 | } |
---|
| 83 | } |
---|
| 84 | return null; |
---|
| 85 | } |
---|
| 86 | number = Math.abs(number); |
---|
| 87 | for(var mt in multiples){ |
---|
| 88 | var multiple = multiples[mt]; |
---|
| 89 | var simpleFraction = number / multiple; |
---|
| 90 | var wholeNumber = Math.floor(simpleFraction); |
---|
| 91 | simpleFraction = calc.approx(simpleFraction - wholeNumber); |
---|
| 92 | if(simpleFraction == 0){ |
---|
| 93 | return { mt:mt, m:multiple, n:wholeNumber, d:1 }; |
---|
| 94 | }else{ |
---|
| 95 | var a = findSimpleFraction(simpleFraction); |
---|
| 96 | if(!a){ continue; } |
---|
| 97 | return { mt:mt, m:multiple, n:(wholeNumber * a.d + a.n), d:a.d }; |
---|
| 98 | } |
---|
| 99 | } |
---|
| 100 | return null; |
---|
| 101 | } |
---|
| 102 | |
---|
| 103 | // make the hash |
---|
| 104 | _fracHashInit(); |
---|
| 105 | |
---|
| 106 | // add toFrac to the calculator |
---|
| 107 | return lang.mixin(calc, { |
---|
| 108 | toFrac: function(number){// get a string fraction for a decimal with a set range of numbers, based on the hash |
---|
| 109 | var f = _fracLookup(number); |
---|
| 110 | 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; |
---|
| 111 | //return f ? ((number < 0 ? '-' : '') + (f.m == 1 ? '' : (f.n == 1 ? '' : (f.n + '*'))) + (f.m == 1 ? f.n : f.mt) + '/' + f.d) : number; |
---|
| 112 | }, |
---|
| 113 | pow: function(base, exponent){// pow benefits from toFrac because it can overcome many of the limitations set before the standard Math.pow |
---|
| 114 | // summary: |
---|
| 115 | // Computes base ^ exponent |
---|
| 116 | |
---|
| 117 | // Wrapper to Math.pow(base, exponent) to handle (-27) ^ (1/3) |
---|
| 118 | function isInt(n){ |
---|
| 119 | return Math.floor(n) == n; |
---|
| 120 | } |
---|
| 121 | |
---|
| 122 | if(base>0||isInt(exponent)){ |
---|
| 123 | return Math.pow(base, exponent); |
---|
| 124 | }else{ |
---|
| 125 | var f = _fracLookup(exponent); |
---|
| 126 | if(base >= 0){ |
---|
| 127 | return (f && f.m == 1) |
---|
| 128 | ? 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 |
---|
| 129 | : Math.pow(base, exponent); |
---|
| 130 | }else{ // e.g. (1/3) root of -27 = -3, 1 / exponent must be an odd integer for a negative base |
---|
| 131 | return (f && f.d & 1) ? Math.pow(Math.pow(-Math.pow(-base, 1 / f.d), exponent < 0 ? -f.n : f.n), f.m) : NaN; |
---|
| 132 | } |
---|
| 133 | } |
---|
| 134 | } |
---|
| 135 | }); |
---|
| 136 | /* |
---|
| 137 | function reduceError(number){ |
---|
| 138 | var f = _fracLookup(number); |
---|
| 139 | if(!f){ f = _fracLookup(number); } |
---|
| 140 | return f ? ((number < 0 ? -1 : 1) * f.n * f.m / f.d) : number; |
---|
| 141 | } |
---|
| 142 | */ |
---|
| 143 | }); |
---|