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(function(global, utils) {
6"use strict";
7
8%CheckIsBootstrapping();
9
10// -------------------------------------------------------------------
11// Imports
12
13define kRandomBatchSize = 64;
14// The first two slots are reserved to persist PRNG state.
15define kRandomNumberStart = 2;
16
17var GlobalFloat64Array = global.Float64Array;
18var GlobalMath = global.Math;
19var GlobalObject = global.Object;
20var InternalArray = utils.InternalArray;
21var NaN = %GetRootNaN();
22var nextRandomIndex = kRandomBatchSize;
23var randomNumbers = UNDEFINED;
24var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
25
26//-------------------------------------------------------------------
27
28// ECMA 262 - 15.8.2.1
29function MathAbs(x) {
30  x = +x;
31  return (x > 0) ? x : 0 - x;
32}
33
34// ECMA 262 - 15.8.2.2
35function MathAcosJS(x) {
36  return %_MathAcos(+x);
37}
38
39// ECMA 262 - 15.8.2.3
40function MathAsinJS(x) {
41  return %_MathAsin(+x);
42}
43
44// ECMA 262 - 15.8.2.4
45function MathAtanJS(x) {
46  return %_MathAtan(+x);
47}
48
49// ECMA 262 - 15.8.2.5
50// The naming of y and x matches the spec, as does the order in which
51// ToNumber (valueOf) is called.
52function MathAtan2JS(y, x) {
53  y = +y;
54  x = +x;
55  return %_MathAtan2(y, x);
56}
57
58// ECMA 262 - 15.8.2.6
59function MathCeil(x) {
60  return -%_MathFloor(-x);
61}
62
63// ECMA 262 - 15.8.2.8
64function MathExp(x) {
65  return %MathExpRT(TO_NUMBER(x));
66}
67
68// ECMA 262 - 15.8.2.9
69function MathFloorJS(x) {
70  return %_MathFloor(+x);
71}
72
73// ECMA 262 - 15.8.2.10
74function MathLog(x) {
75  return %_MathLogRT(TO_NUMBER(x));
76}
77
78// ECMA 262 - 15.8.2.11
79function MathMax(arg1, arg2) {  // length == 2
80  var length = %_ArgumentsLength();
81  if (length == 2) {
82    arg1 = TO_NUMBER(arg1);
83    arg2 = TO_NUMBER(arg2);
84    if (arg2 > arg1) return arg2;
85    if (arg1 > arg2) return arg1;
86    if (arg1 == arg2) {
87      // Make sure -0 is considered less than +0.
88      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
89    }
90    // All comparisons failed, one of the arguments must be NaN.
91    return NaN;
92  }
93  var r = -INFINITY;
94  for (var i = 0; i < length; i++) {
95    var n = %_Arguments(i);
96    n = TO_NUMBER(n);
97    // Make sure +0 is considered greater than -0.
98    if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
99      r = n;
100    }
101  }
102  return r;
103}
104
105// ECMA 262 - 15.8.2.12
106function MathMin(arg1, arg2) {  // length == 2
107  var length = %_ArgumentsLength();
108  if (length == 2) {
109    arg1 = TO_NUMBER(arg1);
110    arg2 = TO_NUMBER(arg2);
111    if (arg2 > arg1) return arg1;
112    if (arg1 > arg2) return arg2;
113    if (arg1 == arg2) {
114      // Make sure -0 is considered less than +0.
115      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
116    }
117    // All comparisons failed, one of the arguments must be NaN.
118    return NaN;
119  }
120  var r = INFINITY;
121  for (var i = 0; i < length; i++) {
122    var n = %_Arguments(i);
123    n = TO_NUMBER(n);
124    // Make sure -0 is considered less than +0.
125    if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
126      r = n;
127    }
128  }
129  return r;
130}
131
132// ECMA 262 - 15.8.2.13
133function MathPowJS(x, y) {
134  return %_MathPow(TO_NUMBER(x), TO_NUMBER(y));
135}
136
137// ECMA 262 - 15.8.2.14
138function MathRandom() {
139  if (nextRandomIndex >= kRandomBatchSize) {
140    randomNumbers = %GenerateRandomNumbers(randomNumbers);
141    nextRandomIndex = kRandomNumberStart;
142  }
143  return randomNumbers[nextRandomIndex++];
144}
145
146function MathRandomRaw() {
147  if (nextRandomIndex >= kRandomBatchSize) {
148    randomNumbers = %GenerateRandomNumbers(randomNumbers);
149    nextRandomIndex = kRandomNumberStart;
150  }
151  return %_DoubleLo(randomNumbers[nextRandomIndex++]) & 0x3FFFFFFF;
152}
153
154// ECMA 262 - 15.8.2.15
155function MathRound(x) {
156  return %RoundNumber(TO_NUMBER(x));
157}
158
159// ECMA 262 - 15.8.2.17
160function MathSqrtJS(x) {
161  return %_MathSqrt(+x);
162}
163
164// Non-standard extension.
165function MathImul(x, y) {
166  return %NumberImul(TO_NUMBER(x), TO_NUMBER(y));
167}
168
169// ES6 draft 09-27-13, section 20.2.2.28.
170function MathSign(x) {
171  x = +x;
172  if (x > 0) return 1;
173  if (x < 0) return -1;
174  // -0, 0 or NaN.
175  return x;
176}
177
178// ES6 draft 09-27-13, section 20.2.2.34.
179function MathTrunc(x) {
180  x = +x;
181  if (x > 0) return %_MathFloor(x);
182  if (x < 0) return -%_MathFloor(-x);
183  // -0, 0 or NaN.
184  return x;
185}
186
187// ES6 draft 09-27-13, section 20.2.2.5.
188function MathAsinh(x) {
189  x = TO_NUMBER(x);
190  // Idempotent for NaN, +/-0 and +/-Infinity.
191  if (x === 0 || !NUMBER_IS_FINITE(x)) return x;
192  if (x > 0) return MathLog(x + %_MathSqrt(x * x + 1));
193  // This is to prevent numerical errors caused by large negative x.
194  return -MathLog(-x + %_MathSqrt(x * x + 1));
195}
196
197// ES6 draft 09-27-13, section 20.2.2.3.
198function MathAcosh(x) {
199  x = TO_NUMBER(x);
200  if (x < 1) return NaN;
201  // Idempotent for NaN and +Infinity.
202  if (!NUMBER_IS_FINITE(x)) return x;
203  return MathLog(x + %_MathSqrt(x + 1) * %_MathSqrt(x - 1));
204}
205
206// ES6 draft 09-27-13, section 20.2.2.7.
207function MathAtanh(x) {
208  x = TO_NUMBER(x);
209  // Idempotent for +/-0.
210  if (x === 0) return x;
211  // Returns NaN for NaN and +/- Infinity.
212  if (!NUMBER_IS_FINITE(x)) return NaN;
213  return 0.5 * MathLog((1 + x) / (1 - x));
214}
215
216// ES6 draft 09-27-13, section 20.2.2.17.
217function MathHypot(x, y) {  // Function length is 2.
218  // We may want to introduce fast paths for two arguments and when
219  // normalization to avoid overflow is not necessary.  For now, we
220  // simply assume the general case.
221  var length = %_ArgumentsLength();
222  var args = new InternalArray(length);
223  var max = 0;
224  for (var i = 0; i < length; i++) {
225    var n = %_Arguments(i);
226    n = TO_NUMBER(n);
227    if (n === INFINITY || n === -INFINITY) return INFINITY;
228    n = MathAbs(n);
229    if (n > max) max = n;
230    args[i] = n;
231  }
232
233  // Kahan summation to avoid rounding errors.
234  // Normalize the numbers to the largest one to avoid overflow.
235  if (max === 0) max = 1;
236  var sum = 0;
237  var compensation = 0;
238  for (var i = 0; i < length; i++) {
239    var n = args[i] / max;
240    var summand = n * n - compensation;
241    var preliminary = sum + summand;
242    compensation = (preliminary - sum) - summand;
243    sum = preliminary;
244  }
245  return %_MathSqrt(sum) * max;
246}
247
248// ES6 draft 09-27-13, section 20.2.2.16.
249function MathFroundJS(x) {
250  return %MathFround(TO_NUMBER(x));
251}
252
253// ES6 draft 07-18-14, section 20.2.2.11
254function MathClz32JS(x) {
255  return %_MathClz32(x >>> 0);
256}
257
258// ES6 draft 09-27-13, section 20.2.2.9.
259// Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm
260// Using initial approximation adapted from Kahan's cbrt and 4 iterations
261// of Newton's method.
262function MathCbrt(x) {
263  x = TO_NUMBER(x);
264  if (x == 0 || !NUMBER_IS_FINITE(x)) return x;
265  return x >= 0 ? CubeRoot(x) : -CubeRoot(-x);
266}
267
268macro NEWTON_ITERATION_CBRT(x, approx)
269  (1.0 / 3.0) * (x / (approx * approx) + 2 * approx);
270endmacro
271
272function CubeRoot(x) {
273  var approx_hi = MathFloorJS(%_DoubleHi(x) / 3) + 0x2A9F7893;
274  var approx = %_ConstructDouble(approx_hi | 0, 0);
275  approx = NEWTON_ITERATION_CBRT(x, approx);
276  approx = NEWTON_ITERATION_CBRT(x, approx);
277  approx = NEWTON_ITERATION_CBRT(x, approx);
278  return NEWTON_ITERATION_CBRT(x, approx);
279}
280
281// -------------------------------------------------------------------
282
283%AddNamedProperty(GlobalMath, toStringTagSymbol, "Math", READ_ONLY | DONT_ENUM);
284
285// Set up math constants.
286utils.InstallConstants(GlobalMath, [
287  // ECMA-262, section 15.8.1.1.
288  "E", 2.7182818284590452354,
289  // ECMA-262, section 15.8.1.2.
290  "LN10", 2.302585092994046,
291  // ECMA-262, section 15.8.1.3.
292  "LN2", 0.6931471805599453,
293  // ECMA-262, section 15.8.1.4.
294  "LOG2E", 1.4426950408889634,
295  "LOG10E", 0.4342944819032518,
296  "PI", 3.1415926535897932,
297  "SQRT1_2", 0.7071067811865476,
298  "SQRT2", 1.4142135623730951
299]);
300
301// Set up non-enumerable functions of the Math object and
302// set their names.
303utils.InstallFunctions(GlobalMath, DONT_ENUM, [
304  "random", MathRandom,
305  "abs", MathAbs,
306  "acos", MathAcosJS,
307  "asin", MathAsinJS,
308  "atan", MathAtanJS,
309  "ceil", MathCeil,
310  "exp", MathExp,
311  "floor", MathFloorJS,
312  "log", MathLog,
313  "round", MathRound,
314  "sqrt", MathSqrtJS,
315  "atan2", MathAtan2JS,
316  "pow", MathPowJS,
317  "max", MathMax,
318  "min", MathMin,
319  "imul", MathImul,
320  "sign", MathSign,
321  "trunc", MathTrunc,
322  "asinh", MathAsinh,
323  "acosh", MathAcosh,
324  "atanh", MathAtanh,
325  "hypot", MathHypot,
326  "fround", MathFroundJS,
327  "clz32", MathClz32JS,
328  "cbrt", MathCbrt
329]);
330
331%SetForceInlineFlag(MathAbs);
332%SetForceInlineFlag(MathAcosJS);
333%SetForceInlineFlag(MathAsinJS);
334%SetForceInlineFlag(MathAtanJS);
335%SetForceInlineFlag(MathAtan2JS);
336%SetForceInlineFlag(MathCeil);
337%SetForceInlineFlag(MathClz32JS);
338%SetForceInlineFlag(MathFloorJS);
339%SetForceInlineFlag(MathRandom);
340%SetForceInlineFlag(MathSign);
341%SetForceInlineFlag(MathSqrtJS);
342%SetForceInlineFlag(MathTrunc);
343
344// -------------------------------------------------------------------
345// Exports
346
347utils.Export(function(to) {
348  to.MathAbs = MathAbs;
349  to.MathExp = MathExp;
350  to.MathFloor = MathFloorJS;
351  to.IntRandom = MathRandomRaw;
352  to.MathMax = MathMax;
353  to.MathMin = MathMin;
354});
355
356})
357