1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5"use strict";
6
7// This file relies on the fact that the following declarations have been made
8// in runtime.js:
9// var $Object = global.Object;
10
11// Keep reference to original values of some global properties.  This
12// has the added benefit that the code in this file is isolated from
13// changes to these properties.
14var $floor = MathFloor;
15var $abs = MathAbs;
16
17// Instance class name can only be set on functions. That is the only
18// purpose for MathConstructor.
19function MathConstructor() {}
20var $Math = new MathConstructor();
21
22// -------------------------------------------------------------------
23
24// ECMA 262 - 15.8.2.1
25function MathAbs(x) {
26  if (%_IsSmi(x)) return x >= 0 ? x : -x;
27  x = TO_NUMBER_INLINE(x);
28  if (x === 0) return 0;  // To handle -0.
29  return x > 0 ? x : -x;
30}
31
32// ECMA 262 - 15.8.2.2
33function MathAcosJS(x) {
34  return %MathAcos(TO_NUMBER_INLINE(x));
35}
36
37// ECMA 262 - 15.8.2.3
38function MathAsinJS(x) {
39  return %MathAsin(TO_NUMBER_INLINE(x));
40}
41
42// ECMA 262 - 15.8.2.4
43function MathAtanJS(x) {
44  return %MathAtan(TO_NUMBER_INLINE(x));
45}
46
47// ECMA 262 - 15.8.2.5
48// The naming of y and x matches the spec, as does the order in which
49// ToNumber (valueOf) is called.
50function MathAtan2JS(y, x) {
51  return %MathAtan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x));
52}
53
54// ECMA 262 - 15.8.2.6
55function MathCeil(x) {
56  return -MathFloor(-x);
57}
58
59// ECMA 262 - 15.8.2.8
60function MathExp(x) {
61  return %MathExpRT(TO_NUMBER_INLINE(x));
62}
63
64// ECMA 262 - 15.8.2.9
65function MathFloor(x) {
66  x = TO_NUMBER_INLINE(x);
67  // It's more common to call this with a positive number that's out
68  // of range than negative numbers; check the upper bound first.
69  if (x < 0x80000000 && x > 0) {
70    // Numbers in the range [0, 2^31) can be floored by converting
71    // them to an unsigned 32-bit value using the shift operator.
72    // We avoid doing so for -0, because the result of Math.floor(-0)
73    // has to be -0, which wouldn't be the case with the shift.
74    return TO_UINT32(x);
75  } else {
76    return %MathFloorRT(x);
77  }
78}
79
80// ECMA 262 - 15.8.2.10
81function MathLog(x) {
82  return %_MathLogRT(TO_NUMBER_INLINE(x));
83}
84
85// ECMA 262 - 15.8.2.11
86function MathMax(arg1, arg2) {  // length == 2
87  var length = %_ArgumentsLength();
88  if (length == 2) {
89    arg1 = TO_NUMBER_INLINE(arg1);
90    arg2 = TO_NUMBER_INLINE(arg2);
91    if (arg2 > arg1) return arg2;
92    if (arg1 > arg2) return arg1;
93    if (arg1 == arg2) {
94      // Make sure -0 is considered less than +0.
95      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
96    }
97    // All comparisons failed, one of the arguments must be NaN.
98    return NAN;
99  }
100  var r = -INFINITY;
101  for (var i = 0; i < length; i++) {
102    var n = %_Arguments(i);
103    if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
104    // Make sure +0 is considered greater than -0.
105    if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
106      r = n;
107    }
108  }
109  return r;
110}
111
112// ECMA 262 - 15.8.2.12
113function MathMin(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 arg1;
119    if (arg1 > arg2) return arg2;
120    if (arg1 == arg2) {
121      // Make sure -0 is considered less than +0.
122      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
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 less than +0.
132    if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
133      r = n;
134    }
135  }
136  return r;
137}
138
139// ECMA 262 - 15.8.2.13
140function MathPow(x, y) {
141  return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
142}
143
144// ECMA 262 - 15.8.2.14
145var rngstate;  // Initialized to a Uint32Array during genesis.
146function MathRandom() {
147  var r0 = (MathImul(18273, rngstate[0] & 0xFFFF) + (rngstate[0] >>> 16)) | 0;
148  rngstate[0] = r0;
149  var r1 = (MathImul(36969, rngstate[1] & 0xFFFF) + (rngstate[1] >>> 16)) | 0;
150  rngstate[1] = r1;
151  var x = ((r0 << 16) + (r1 & 0xFFFF)) | 0;
152  // Division by 0x100000000 through multiplication by reciprocal.
153  return (x < 0 ? (x + 0x100000000) : x) * 2.3283064365386962890625e-10;
154}
155
156// ECMA 262 - 15.8.2.15
157function MathRound(x) {
158  return %RoundNumber(TO_NUMBER_INLINE(x));
159}
160
161// ECMA 262 - 15.8.2.17
162function MathSqrt(x) {
163  return %_MathSqrtRT(TO_NUMBER_INLINE(x));
164}
165
166// Non-standard extension.
167function MathImul(x, y) {
168  return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
169}
170
171// ES6 draft 09-27-13, section 20.2.2.28.
172function MathSign(x) {
173  x = TO_NUMBER_INLINE(x);
174  if (x > 0) return 1;
175  if (x < 0) return -1;
176  // -0, 0 or NaN.
177  return x;
178}
179
180// ES6 draft 09-27-13, section 20.2.2.34.
181function MathTrunc(x) {
182  x = TO_NUMBER_INLINE(x);
183  if (x > 0) return MathFloor(x);
184  if (x < 0) return MathCeil(x);
185  // -0, 0 or NaN.
186  return x;
187}
188
189// ES6 draft 09-27-13, section 20.2.2.33.
190function MathTanh(x) {
191  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
192  // Idempotent for +/-0.
193  if (x === 0) return x;
194  // Returns +/-1 for +/-Infinity.
195  if (!NUMBER_IS_FINITE(x)) return MathSign(x);
196  var exp1 = MathExp(x);
197  var exp2 = MathExp(-x);
198  return (exp1 - exp2) / (exp1 + exp2);
199}
200
201// ES6 draft 09-27-13, section 20.2.2.5.
202function MathAsinh(x) {
203  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
204  // Idempotent for NaN, +/-0 and +/-Infinity.
205  if (x === 0 || !NUMBER_IS_FINITE(x)) return x;
206  if (x > 0) return MathLog(x + MathSqrt(x * x + 1));
207  // This is to prevent numerical errors caused by large negative x.
208  return -MathLog(-x + MathSqrt(x * x + 1));
209}
210
211// ES6 draft 09-27-13, section 20.2.2.3.
212function MathAcosh(x) {
213  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
214  if (x < 1) return NAN;
215  // Idempotent for NaN and +Infinity.
216  if (!NUMBER_IS_FINITE(x)) return x;
217  return MathLog(x + MathSqrt(x + 1) * MathSqrt(x - 1));
218}
219
220// ES6 draft 09-27-13, section 20.2.2.7.
221function MathAtanh(x) {
222  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
223  // Idempotent for +/-0.
224  if (x === 0) return x;
225  // Returns NaN for NaN and +/- Infinity.
226  if (!NUMBER_IS_FINITE(x)) return NAN;
227  return 0.5 * MathLog((1 + x) / (1 - x));
228}
229
230// ES6 draft 09-27-13, section 20.2.2.21.
231function MathLog10(x) {
232  return MathLog(x) * 0.434294481903251828;  // log10(x) = log(x)/log(10).
233}
234
235
236// ES6 draft 09-27-13, section 20.2.2.22.
237function MathLog2(x) {
238  return MathLog(x) * 1.442695040888963407;  // log2(x) = log(x)/log(2).
239}
240
241// ES6 draft 09-27-13, section 20.2.2.17.
242function MathHypot(x, y) {  // Function length is 2.
243  // We may want to introduce fast paths for two arguments and when
244  // normalization to avoid overflow is not necessary.  For now, we
245  // simply assume the general case.
246  var length = %_ArgumentsLength();
247  var args = new InternalArray(length);
248  var max = 0;
249  for (var i = 0; i < length; i++) {
250    var n = %_Arguments(i);
251    if (!IS_NUMBER(n)) n = NonNumberToNumber(n);
252    if (n === INFINITY || n === -INFINITY) return INFINITY;
253    n = MathAbs(n);
254    if (n > max) max = n;
255    args[i] = n;
256  }
257
258  // Kahan summation to avoid rounding errors.
259  // Normalize the numbers to the largest one to avoid overflow.
260  if (max === 0) max = 1;
261  var sum = 0;
262  var compensation = 0;
263  for (var i = 0; i < length; i++) {
264    var n = args[i] / max;
265    var summand = n * n - compensation;
266    var preliminary = sum + summand;
267    compensation = (preliminary - sum) - summand;
268    sum = preliminary;
269  }
270  return MathSqrt(sum) * max;
271}
272
273// ES6 draft 09-27-13, section 20.2.2.16.
274function MathFroundJS(x) {
275  return %MathFround(TO_NUMBER_INLINE(x));
276}
277
278// ES6 draft 07-18-14, section 20.2.2.11
279function MathClz32(x) {
280  x = ToUint32(TO_NUMBER_INLINE(x));
281  if (x == 0) return 32;
282  var result = 0;
283  // Binary search.
284  if ((x & 0xFFFF0000) === 0) { x <<= 16; result += 16; };
285  if ((x & 0xFF000000) === 0) { x <<=  8; result +=  8; };
286  if ((x & 0xF0000000) === 0) { x <<=  4; result +=  4; };
287  if ((x & 0xC0000000) === 0) { x <<=  2; result +=  2; };
288  if ((x & 0x80000000) === 0) { x <<=  1; result +=  1; };
289  return result;
290}
291
292// ES6 draft 09-27-13, section 20.2.2.9.
293// Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm
294// Using initial approximation adapted from Kahan's cbrt and 4 iterations
295// of Newton's method.
296function MathCbrt(x) {
297  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
298  if (x == 0 || !NUMBER_IS_FINITE(x)) return x;
299  return x >= 0 ? CubeRoot(x) : -CubeRoot(-x);
300}
301
302macro NEWTON_ITERATION_CBRT(x, approx)
303  (1.0 / 3.0) * (x / (approx * approx) + 2 * approx);
304endmacro
305
306function CubeRoot(x) {
307  var approx_hi = MathFloor(%_DoubleHi(x) / 3) + 0x2A9F7893;
308  var approx = %_ConstructDouble(approx_hi, 0);
309  approx = NEWTON_ITERATION_CBRT(x, approx);
310  approx = NEWTON_ITERATION_CBRT(x, approx);
311  approx = NEWTON_ITERATION_CBRT(x, approx);
312  return NEWTON_ITERATION_CBRT(x, approx);
313}
314
315// -------------------------------------------------------------------
316
317function SetUpMath() {
318  %CheckIsBootstrapping();
319
320  %InternalSetPrototype($Math, $Object.prototype);
321  %AddNamedProperty(global, "Math", $Math, DONT_ENUM);
322  %FunctionSetInstanceClassName(MathConstructor, 'Math');
323
324  // Set up math constants.
325  InstallConstants($Math, $Array(
326    // ECMA-262, section 15.8.1.1.
327    "E", 2.7182818284590452354,
328    // ECMA-262, section 15.8.1.2.
329    "LN10", 2.302585092994046,
330    // ECMA-262, section 15.8.1.3.
331    "LN2", 0.6931471805599453,
332    // ECMA-262, section 15.8.1.4.
333    "LOG2E", 1.4426950408889634,
334    "LOG10E", 0.4342944819032518,
335    "PI", 3.1415926535897932,
336    "SQRT1_2", 0.7071067811865476,
337    "SQRT2", 1.4142135623730951
338  ));
339
340  // Set up non-enumerable functions of the Math object and
341  // set their names.
342  InstallFunctions($Math, DONT_ENUM, $Array(
343    "random", MathRandom,
344    "abs", MathAbs,
345    "acos", MathAcosJS,
346    "asin", MathAsinJS,
347    "atan", MathAtanJS,
348    "ceil", MathCeil,
349    "cos", MathCos,       // implemented by third_party/fdlibm
350    "exp", MathExp,
351    "floor", MathFloor,
352    "log", MathLog,
353    "round", MathRound,
354    "sin", MathSin,       // implemented by third_party/fdlibm
355    "sqrt", MathSqrt,
356    "tan", MathTan,       // implemented by third_party/fdlibm
357    "atan2", MathAtan2JS,
358    "pow", MathPow,
359    "max", MathMax,
360    "min", MathMin,
361    "imul", MathImul,
362    "sign", MathSign,
363    "trunc", MathTrunc,
364    "sinh", MathSinh,     // implemented by third_party/fdlibm
365    "cosh", MathCosh,     // implemented by third_party/fdlibm
366    "tanh", MathTanh,
367    "asinh", MathAsinh,
368    "acosh", MathAcosh,
369    "atanh", MathAtanh,
370    "log10", MathLog10,
371    "log2", MathLog2,
372    "hypot", MathHypot,
373    "fround", MathFroundJS,
374    "clz32", MathClz32,
375    "cbrt", MathCbrt,
376    "log1p", MathLog1p,   // implemented by third_party/fdlibm
377    "expm1", MathExpm1    // implemented by third_party/fdlibm
378  ));
379
380  %SetInlineBuiltinFlag(MathCeil);
381  %SetInlineBuiltinFlag(MathRandom);
382  %SetInlineBuiltinFlag(MathSin);
383  %SetInlineBuiltinFlag(MathCos);
384}
385
386SetUpMath();
387