source: Dev/branches/rest-dojo-ui/client/dojox/calc/toFrac.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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                        //      Wrapper to Math.pow(base, exponent) to handle (-27) ^ (1/3)
117                        function isInt(n){
118                                return Math.floor(n) == n;
119                        }
120
121                        if(base>0||isInt(exponent)){
122                                return Math.pow(base, exponent);
123                        }else{
124                                var f = _fracLookup(exponent);
125                                if(base >= 0){
126                                        return (f && f.m == 1)
127                                                ? 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
128                                                : Math.pow(base, exponent);
129                                }else{  // e.g. (1/3) root of -27 = -3, 1 / exponent must be an odd integer for a negative base
130                                        return (f && f.d & 1) ? Math.pow(Math.pow(-Math.pow(-base, 1 / f.d), exponent < 0 ? -f.n : f.n), f.m) : NaN;
131                                }
132                        }
133                }
134        });
135/*
136        function reduceError(number){
137                var f = _fracLookup(number);
138                if(!f){ f = _fracLookup(number); }
139                return f ? ((number < 0 ? -1 : 1) * f.n * f.m / f.d) : number;
140        }
141*/
142});
Note: See TracBrowser for help on using the repository browser.