1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// This file relies on the fact that the following declarations have been made
29// in runtime.js:
30// var $Object = global.Object;
31
32// Keep reference to original values of some global properties.  This
33// has the added benefit that the code in this file is isolated from
34// changes to these properties.
35var $floor = MathFloor;
36var $abs = MathAbs;
37
38// Instance class name can only be set on functions. That is the only
39// purpose for MathConstructor.
40function MathConstructor() {}
41var $Math = new MathConstructor();
42
43// -------------------------------------------------------------------
44
45// ECMA 262 - 15.8.2.1
46function MathAbs(x) {
47  if (%_IsSmi(x)) return x >= 0 ? x : -x;
48  x = TO_NUMBER_INLINE(x);
49  if (x === 0) return 0;  // To handle -0.
50  return x > 0 ? x : -x;
51}
52
53// ECMA 262 - 15.8.2.2
54function MathAcos(x) {
55  return %Math_acos(TO_NUMBER_INLINE(x));
56}
57
58// ECMA 262 - 15.8.2.3
59function MathAsin(x) {
60  return %Math_asin(TO_NUMBER_INLINE(x));
61}
62
63// ECMA 262 - 15.8.2.4
64function MathAtan(x) {
65  return %Math_atan(TO_NUMBER_INLINE(x));
66}
67
68// ECMA 262 - 15.8.2.5
69// The naming of y and x matches the spec, as does the order in which
70// ToNumber (valueOf) is called.
71function MathAtan2(y, x) {
72  return %Math_atan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x));
73}
74
75// ECMA 262 - 15.8.2.6
76function MathCeil(x) {
77  return -MathFloor(-x);
78}
79
80// ECMA 262 - 15.8.2.7
81function MathCos(x) {
82  x = MathAbs(x);  // Convert to number and get rid of -0.
83  return TrigonometricInterpolation(x, 1);
84}
85
86// ECMA 262 - 15.8.2.8
87function MathExp(x) {
88  return %Math_exp(TO_NUMBER_INLINE(x));
89}
90
91// ECMA 262 - 15.8.2.9
92function MathFloor(x) {
93  x = TO_NUMBER_INLINE(x);
94  // It's more common to call this with a positive number that's out
95  // of range than negative numbers; check the upper bound first.
96  if (x < 0x80000000 && x > 0) {
97    // Numbers in the range [0, 2^31) can be floored by converting
98    // them to an unsigned 32-bit value using the shift operator.
99    // We avoid doing so for -0, because the result of Math.floor(-0)
100    // has to be -0, which wouldn't be the case with the shift.
101    return TO_UINT32(x);
102  } else {
103    return %Math_floor(x);
104  }
105}
106
107// ECMA 262 - 15.8.2.10
108function MathLog(x) {
109  return %_MathLog(TO_NUMBER_INLINE(x));
110}
111
112// ECMA 262 - 15.8.2.11
113function MathMax(arg1, arg2) {  // length == 2
114  var length = %_ArgumentsLength();
115  if (length == 2) {
116    arg1 = TO_NUMBER_INLINE(arg1);
117    arg2 = TO_NUMBER_INLINE(arg2);
118    if (arg2 > arg1) return arg2;
119    if (arg1 > arg2) return arg1;
120    if (arg1 == arg2) {
121      // Make sure -0 is considered less than +0.
122      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
123    }
124    // All comparisons failed, one of the arguments must be NaN.
125    return NAN;
126  }
127  var r = -INFINITY;
128  for (var i = 0; i < length; i++) {
129    var n = %_Arguments(i);
130    if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
131    // Make sure +0 is considered greater than -0.
132    if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
133      r = n;
134    }
135  }
136  return r;
137}
138
139// ECMA 262 - 15.8.2.12
140function MathMin(arg1, arg2) {  // length == 2
141  var length = %_ArgumentsLength();
142  if (length == 2) {
143    arg1 = TO_NUMBER_INLINE(arg1);
144    arg2 = TO_NUMBER_INLINE(arg2);
145    if (arg2 > arg1) return arg1;
146    if (arg1 > arg2) return arg2;
147    if (arg1 == arg2) {
148      // Make sure -0 is considered less than +0.
149      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
150    }
151    // All comparisons failed, one of the arguments must be NaN.
152    return NAN;
153  }
154  var r = INFINITY;
155  for (var i = 0; i < length; i++) {
156    var n = %_Arguments(i);
157    if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
158    // Make sure -0 is considered less than +0.
159    if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
160      r = n;
161    }
162  }
163  return r;
164}
165
166// ECMA 262 - 15.8.2.13
167function MathPow(x, y) {
168  return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
169}
170
171// ECMA 262 - 15.8.2.14
172var rngstate;  // Initialized to a Uint32Array during genesis.
173function MathRandom() {
174  var r0 = (MathImul(18273, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0;
175  rngstate[0] = r0;
176  var r1 = (MathImul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0;
177  rngstate[1] = r1;
178  var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0;
179  // Division by 0x100000000 through multiplication by reciprocal.
180  return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10;
181}
182
183// ECMA 262 - 15.8.2.15
184function MathRound(x) {
185  return %RoundNumber(TO_NUMBER_INLINE(x));
186}
187
188// ECMA 262 - 15.8.2.16
189function MathSin(x) {
190  x = x * 1;  // Convert to number and deal with -0.
191  if (%_IsMinusZero(x)) return x;
192  return TrigonometricInterpolation(x, 0);
193}
194
195// ECMA 262 - 15.8.2.17
196function MathSqrt(x) {
197  return %_MathSqrt(TO_NUMBER_INLINE(x));
198}
199
200// ECMA 262 - 15.8.2.18
201function MathTan(x) {
202  return MathSin(x) / MathCos(x);
203}
204
205// Non-standard extension.
206function MathImul(x, y) {
207  return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
208}
209
210
211var kInversePiHalf      = 0.636619772367581343;      // 2 / pi
212var kInversePiHalfS26   = 9.48637384723993156e-9;    // 2 / pi / (2^26)
213var kS26                = 1 << 26;
214var kTwoStepThreshold   = 1 << 27;
215// pi / 2 rounded up
216var kPiHalf             = 1.570796326794896780;      // 0x192d4454fb21f93f
217// We use two parts for pi/2 to emulate a higher precision.
218// pi_half_1 only has 26 significant bits for mantissa.
219// Note that pi_half > pi_half_1 + pi_half_2
220var kPiHalf1            = 1.570796325802803040;      // 0x00000054fb21f93f
221var kPiHalf2            = 9.920935796805404252e-10;  // 0x3326a611460b113e
222
223var kSamples;            // Initialized to a number during genesis.
224var kIndexConvert;       // Initialized to kSamples / (pi/2) during genesis.
225var kSinTable;           // Initialized to a Float64Array during genesis.
226var kCosXIntervalTable;  // Initialized to a Float64Array during genesis.
227
228// This implements sine using the following algorithm.
229// 1) Multiplication takes care of to-number conversion.
230// 2) Reduce x to the first quadrant [0, pi/2].
231//    Conveniently enough, in case of +/-Infinity, we get NaN.
232//    Note that we try to use only 26 instead of 52 significant bits for
233//    mantissa to avoid rounding errors when multiplying.  For very large
234//    input we therefore have additional steps.
235// 3) Replace x by (pi/2-x) if x was in the 2nd or 4th quadrant.
236// 4) Do a table lookup for the closest samples to the left and right of x.
237// 5) Find the derivatives at those sampling points by table lookup:
238//    dsin(x)/dx = cos(x) = sin(pi/2-x) for x in [0, pi/2].
239// 6) Use cubic spline interpolation to approximate sin(x).
240// 7) Negate the result if x was in the 3rd or 4th quadrant.
241// 8) Get rid of -0 by adding 0.
242function TrigonometricInterpolation(x, phase) {
243  if (x < 0 || x > kPiHalf) {
244    var multiple;
245    while (x < -kTwoStepThreshold || x > kTwoStepThreshold) {
246      // Let's assume this loop does not terminate.
247      // All numbers x in each loop forms a set S.
248      // (1) abs(x) > 2^27 for all x in S.
249      // (2) abs(multiple) != 0 since (2^27 * inverse_pi_half_s26) > 1
250      // (3) multiple is rounded down in 2^26 steps, so the rounding error is
251      //     at most max(ulp, 2^26).
252      // (4) so for x > 2^27, we subtract at most (1+pi/4)x and at least
253      //     (1-pi/4)x
254      // (5) The subtraction results in x' so that abs(x') <= abs(x)*pi/4.
255      //     Note that this difference cannot be simply rounded off.
256      // Set S cannot exist since (5) violates (1).  Loop must terminate.
257      multiple = MathFloor(x * kInversePiHalfS26) * kS26;
258      x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
259    }
260    multiple = MathFloor(x * kInversePiHalf);
261    x = x - multiple * kPiHalf1 - multiple * kPiHalf2;
262    phase += multiple;
263  }
264  var double_index = x * kIndexConvert;
265  if (phase & 1) double_index = kSamples - double_index;
266  var index = double_index | 0;
267  var t1 = double_index - index;
268  var t2 = 1 - t1;
269  var y1 = kSinTable[index];
270  var y2 = kSinTable[index + 1];
271  var dy = y2 - y1;
272  return (t2 * y1 + t1 * y2 +
273              t1 * t2 * ((kCosXIntervalTable[index] - dy) * t2 +
274                         (dy - kCosXIntervalTable[index + 1]) * t1))
275         * (1 - (phase & 2)) + 0;
276}
277
278// -------------------------------------------------------------------
279
280function SetUpMath() {
281  %CheckIsBootstrapping();
282
283  %SetPrototype($Math, $Object.prototype);
284  %SetProperty(global, "Math", $Math, DONT_ENUM);
285  %FunctionSetInstanceClassName(MathConstructor, 'Math');
286
287  // Set up math constants.
288  // ECMA-262, section 15.8.1.1.
289  %OptimizeObjectForAddingMultipleProperties($Math, 8);
290  %SetProperty($Math,
291               "E",
292               2.7182818284590452354,
293               DONT_ENUM |  DONT_DELETE | READ_ONLY);
294  // ECMA-262, section 15.8.1.2.
295  %SetProperty($Math,
296               "LN10",
297               2.302585092994046,
298               DONT_ENUM |  DONT_DELETE | READ_ONLY);
299  // ECMA-262, section 15.8.1.3.
300  %SetProperty($Math,
301               "LN2",
302               0.6931471805599453,
303               DONT_ENUM |  DONT_DELETE | READ_ONLY);
304  // ECMA-262, section 15.8.1.4.
305  %SetProperty($Math,
306               "LOG2E",
307               1.4426950408889634,
308               DONT_ENUM |  DONT_DELETE | READ_ONLY);
309  %SetProperty($Math,
310               "LOG10E",
311               0.4342944819032518,
312               DONT_ENUM |  DONT_DELETE | READ_ONLY);
313  %SetProperty($Math,
314               "PI",
315               3.1415926535897932,
316               DONT_ENUM |  DONT_DELETE | READ_ONLY);
317  %SetProperty($Math,
318               "SQRT1_2",
319               0.7071067811865476,
320               DONT_ENUM |  DONT_DELETE | READ_ONLY);
321  %SetProperty($Math,
322               "SQRT2",
323               1.4142135623730951,
324               DONT_ENUM |  DONT_DELETE | READ_ONLY);
325  %ToFastProperties($Math);
326
327  // Set up non-enumerable functions of the Math object and
328  // set their names.
329  InstallFunctions($Math, DONT_ENUM, $Array(
330    "random", MathRandom,
331    "abs", MathAbs,
332    "acos", MathAcos,
333    "asin", MathAsin,
334    "atan", MathAtan,
335    "ceil", MathCeil,
336    "cos", MathCos,
337    "exp", MathExp,
338    "floor", MathFloor,
339    "log", MathLog,
340    "round", MathRound,
341    "sin", MathSin,
342    "sqrt", MathSqrt,
343    "tan", MathTan,
344    "atan2", MathAtan2,
345    "pow", MathPow,
346    "max", MathMax,
347    "min", MathMin,
348    "imul", MathImul
349  ));
350
351  %SetInlineBuiltinFlag(MathCeil);
352  %SetInlineBuiltinFlag(MathRandom);
353  %SetInlineBuiltinFlag(MathSin);
354  %SetInlineBuiltinFlag(MathCos);
355  %SetInlineBuiltinFlag(MathTan);
356  %SetInlineBuiltinFlag(TrigonometricInterpolation);
357}
358
359SetUpMath();
360