source: Dev/trunk/src/client/dojox/calc/toFrac.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 5.8 KB
Line 
1define([
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});
Note: See TracBrowser for help on using the repository browser.