1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 /*
41 * JS math package.
42 */
43 #include "jsstddef.h"
44 #include "jslibmath.h"
45 #include <stdlib.h>
46 #include "jstypes.h"
47 #include "jslong.h"
48 #include "prmjtime.h"
49 #include "jsapi.h"
50 #include "jsatom.h"
51 #include "jscntxt.h"
52 #include "jsconfig.h"
53 #include "jslock.h"
54 #include "jsmath.h"
55 #include "jsnum.h"
56 #include "jsobj.h"
57
58 #ifndef M_E
59 #define M_E 2.7182818284590452354
60 #endif
61 #ifndef M_LOG2E
62 #define M_LOG2E 1.4426950408889634074
63 #endif
64 #ifndef M_LOG10E
65 #define M_LOG10E 0.43429448190325182765
66 #endif
67 #ifndef M_LN2
68 #define M_LN2 0.69314718055994530942
69 #endif
70 #ifndef M_LN10
71 #define M_LN10 2.30258509299404568402
72 #endif
73 #ifndef M_PI
74 #define M_PI 3.14159265358979323846
75 #endif
76 #ifndef M_SQRT2
77 #define M_SQRT2 1.41421356237309504880
78 #endif
79 #ifndef M_SQRT1_2
80 #define M_SQRT1_2 0.70710678118654752440
81 #endif
82
83 static JSConstDoubleSpec math_constants[] = {
84 {M_E, "E", 0, {0,0,0}},
85 {M_LOG2E, "LOG2E", 0, {0,0,0}},
86 {M_LOG10E, "LOG10E", 0, {0,0,0}},
87 {M_LN2, "LN2", 0, {0,0,0}},
88 {M_LN10, "LN10", 0, {0,0,0}},
89 {M_PI, "PI", 0, {0,0,0}},
90 {M_SQRT2, "SQRT2", 0, {0,0,0}},
91 {M_SQRT1_2, "SQRT1_2", 0, {0,0,0}},
92 {0,0,0,{0,0,0}}
93 };
94
95 static JSClass math_class = {
96 "Math",
97 0,
98 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
99 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
100 JSCLASS_NO_OPTIONAL_MEMBERS
101 };
102
103 static JSBool
104 math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
105 0 {
106 jsdouble x, z;
107
108 0 if (!js_ValueToNumber(cx, argv[0], &x))
109 0 return JS_FALSE;
110 0 z = fd_fabs(x);
111 0 return js_NewNumberValue(cx, z, rval);
112 }
113
114 static JSBool
115 math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
116 0 {
117 jsdouble x, z;
118
119 0 if (!js_ValueToNumber(cx, argv[0], &x))
120 0 return JS_FALSE;
121 0 z = fd_acos(x);
122 0 return js_NewNumberValue(cx, z, rval);
123 }
124
125 static JSBool
126 math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
127 0 {
128 jsdouble x, z;
129
130 0 if (!js_ValueToNumber(cx, argv[0], &x))
131 0 return JS_FALSE;
132 0 z = fd_asin(x);
133 0 return js_NewNumberValue(cx, z, rval);
134 }
135
136 static JSBool
137 math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
138 0 {
139 jsdouble x, z;
140
141 0 if (!js_ValueToNumber(cx, argv[0], &x))
142 0 return JS_FALSE;
143 0 z = fd_atan(x);
144 0 return js_NewNumberValue(cx, z, rval);
145 }
146
147 static JSBool
148 math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
149 0 {
150 jsdouble x, y, z;
151
152 0 if (!js_ValueToNumber(cx, argv[0], &x))
153 0 return JS_FALSE;
154 0 if (!js_ValueToNumber(cx, argv[1], &y))
155 0 return JS_FALSE;
156 0 z = fd_atan2(x, y);
157 0 return js_NewNumberValue(cx, z, rval);
158 }
159
160 static JSBool
161 math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
162 0 {
163 jsdouble x, z;
164
165 0 if (!js_ValueToNumber(cx, argv[0], &x))
166 0 return JS_FALSE;
167 0 z = fd_ceil(x);
168 0 return js_NewNumberValue(cx, z, rval);
169 }
170
171 static JSBool
172 math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
173 0 {
174 jsdouble x, z;
175
176 0 if (!js_ValueToNumber(cx, argv[0], &x))
177 0 return JS_FALSE;
178 0 z = fd_cos(x);
179 0 return js_NewNumberValue(cx, z, rval);
180 }
181
182 static JSBool
183 math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
184 0 {
185 jsdouble x, z;
186
187 0 if (!js_ValueToNumber(cx, argv[0], &x))
188 0 return JS_FALSE;
189 #ifdef _WIN32
190 if (!JSDOUBLE_IS_NaN(x)) {
191 if (x == *cx->runtime->jsPositiveInfinity) {
192 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
193 return JS_TRUE;
194 }
195 if (x == *cx->runtime->jsNegativeInfinity) {
196 *rval = JSVAL_ZERO;
197 return JS_TRUE;
198 }
199 }
200 #endif
201 0 z = fd_exp(x);
202 0 return js_NewNumberValue(cx, z, rval);
203 }
204
205 static JSBool
206 math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
207 0 {
208 jsdouble x, z;
209
210 0 if (!js_ValueToNumber(cx, argv[0], &x))
211 0 return JS_FALSE;
212 0 z = fd_floor(x);
213 0 return js_NewNumberValue(cx, z, rval);
214 }
215
216 static JSBool
217 math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
218 0 {
219 jsdouble x, z;
220
221 0 if (!js_ValueToNumber(cx, argv[0], &x))
222 0 return JS_FALSE;
223 0 z = fd_log(x);
224 0 return js_NewNumberValue(cx, z, rval);
225 }
226
227 static JSBool
228 math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
229 0 {
230 0 jsdouble x, z = *cx->runtime->jsNegativeInfinity;
231 uintN i;
232
233 0 if (argc == 0) {
234 0 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);
235 0 return JS_TRUE;
236 }
237 0 for (i = 0; i < argc; i++) {
238 0 if (!js_ValueToNumber(cx, argv[i], &x))
239 0 return JS_FALSE;
240 0 if (JSDOUBLE_IS_NaN(x)) {
241 0 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
242 0 return JS_TRUE;
243 }
244 0 if ((x==0)&&(x==z)&&(fd_copysign(1.0,z)==-1))
245 0 z = x;
246 else
247 0 z = (x > z) ? x : z;
248 }
249 0 return js_NewNumberValue(cx, z, rval);
250 }
251
252 static JSBool
253 math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
254 0 {
255 0 jsdouble x, z = *cx->runtime->jsPositiveInfinity;
256 uintN i;
257
258 0 if (argc == 0) {
259 0 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
260 0 return JS_TRUE;
261 }
262 0 for (i = 0; i < argc; i++) {
263 0 if (!js_ValueToNumber(cx, argv[i], &x))
264 0 return JS_FALSE;
265 0 if (JSDOUBLE_IS_NaN(x)) {
266 0 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
267 0 return JS_TRUE;
268 }
269 0 if ((x==0)&&(x==z)&&(fd_copysign(1.0,x)==-1))
270 0 z = x;
271 else
272 0 z = (x < z) ? x : z;
273 }
274 0 return js_NewNumberValue(cx, z, rval);
275 }
276
277 static JSBool
278 math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
279 0 {
280 jsdouble x, y, z;
281
282 0 if (!js_ValueToNumber(cx, argv[0], &x))
283 0 return JS_FALSE;
284 0 if (!js_ValueToNumber(cx, argv[1], &y))
285 0 return JS_FALSE;
286 #if !JS_USE_FDLIBM_MATH
287 /*
288 * Because C99 and ECMA specify different behavior for pow(),
289 * we need to wrap the libm call to make it ECMA compliant.
290 */
291 if (!JSDOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) {
292 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
293 return JS_TRUE;
294 }
295 #endif
296 0 z = fd_pow(x, y);
297 0 return js_NewNumberValue(cx, z, rval);
298 }
299
300 /*
301 * Math.random() support, lifted from java.util.Random.java.
302 */
303 static void
304 random_setSeed(JSRuntime *rt, int64 seed)
305 0 {
306 int64 tmp;
307
308 0 JSLL_I2L(tmp, 1000);
309 0 JSLL_DIV(seed, seed, tmp);
310 0 JSLL_XOR(tmp, seed, rt->rngMultiplier);
311 0 JSLL_AND(rt->rngSeed, tmp, rt->rngMask);
312 }
313
314 static void
315 random_init(JSRuntime *rt)
316 0 {
317 int64 tmp, tmp2;
318
319 /* Do at most once. */
320 0 if (rt->rngInitialized)
321 0 return;
322 0 rt->rngInitialized = JS_TRUE;
323
324 /* rt->rngMultiplier = 0x5DEECE66DL */
325 0 JSLL_ISHL(tmp, 0x5, 32);
326 0 JSLL_UI2L(tmp2, 0xDEECE66DL);
327 0 JSLL_OR(rt->rngMultiplier, tmp, tmp2);
328
329 /* rt->rngAddend = 0xBL */
330 0 JSLL_I2L(rt->rngAddend, 0xBL);
331
332 /* rt->rngMask = (1L << 48) - 1 */
333 0 JSLL_I2L(tmp, 1);
334 0 JSLL_SHL(tmp2, tmp, 48);
335 0 JSLL_SUB(rt->rngMask, tmp2, tmp);
336
337 /* rt->rngDscale = (jsdouble)(1L << 53) */
338 0 JSLL_SHL(tmp2, tmp, 53);
339 0 JSLL_L2D(rt->rngDscale, tmp2);
340
341 /* Finally, set the seed from current time. */
342 0 random_setSeed(rt, PRMJ_Now());
343 }
344
345 static uint32
346 random_next(JSRuntime *rt, int bits)
347 0 {
348 int64 nextseed, tmp;
349 uint32 retval;
350
351 0 JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier);
352 0 JSLL_ADD(nextseed, nextseed, rt->rngAddend);
353 0 JSLL_AND(nextseed, nextseed, rt->rngMask);
354 0 rt->rngSeed = nextseed;
355 0 JSLL_USHR(tmp, nextseed, 48 - bits);
356 0 JSLL_L2I(retval, tmp);
357 0 return retval;
358 }
359
360 static jsdouble
361 random_nextDouble(JSRuntime *rt)
362 0 {
363 int64 tmp, tmp2;
364 jsdouble d;
365
366 0 JSLL_ISHL(tmp, random_next(rt, 26), 27);
367 0 JSLL_UI2L(tmp2, random_next(rt, 27));
368 0 JSLL_ADD(tmp, tmp, tmp2);
369 0 JSLL_L2D(d, tmp);
370 0 return d / rt->rngDscale;
371 }
372
373 static JSBool
374 math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
375 0 {
376 JSRuntime *rt;
377 jsdouble z;
378
379 0 rt = cx->runtime;
380 JS_LOCK_RUNTIME(rt);
381 0 random_init(rt);
382 0 z = random_nextDouble(rt);
383 JS_UNLOCK_RUNTIME(rt);
384 0 return js_NewNumberValue(cx, z, rval);
385 }
386
387 static JSBool
388 math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
389 0 {
390 jsdouble x, z;
391
392 0 if (!js_ValueToNumber(cx, argv[0], &x))
393 0 return JS_FALSE;
394 0 z = fd_copysign(fd_floor(x + 0.5), x);
395 0 return js_NewNumberValue(cx, z, rval);
396 }
397
398 static JSBool
399 math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
400 0 {
401 jsdouble x, z;
402
403 0 if (!js_ValueToNumber(cx, argv[0], &x))
404 0 return JS_FALSE;
405 0 z = fd_sin(x);
406 0 return js_NewNumberValue(cx, z, rval);
407 }
408
409 static JSBool
410 math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
411 0 {
412 jsdouble x, z;
413
414 0 if (!js_ValueToNumber(cx, argv[0], &x))
415 0 return JS_FALSE;
416 0 z = fd_sqrt(x);
417 0 return js_NewNumberValue(cx, z, rval);
418 }
419
420 static JSBool
421 math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
422 0 {
423 jsdouble x, z;
424
425 0 if (!js_ValueToNumber(cx, argv[0], &x))
426 0 return JS_FALSE;
427 0 z = fd_tan(x);
428 0 return js_NewNumberValue(cx, z, rval);
429 }
430
431 #if JS_HAS_TOSOURCE
432 static JSBool
433 math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
434 jsval *rval)
435 0 {
436 0 *rval = ATOM_KEY(cx->runtime->atomState.MathAtom);
437 0 return JS_TRUE;
438 }
439 #endif
440
441 static JSFunctionSpec math_static_methods[] = {
442 #if JS_HAS_TOSOURCE
443 {js_toSource_str, math_toSource, 0, 0, 0},
444 #endif
445 {"abs", math_abs, 1, 0, 0},
446 {"acos", math_acos, 1, 0, 0},
447 {"asin", math_asin, 1, 0, 0},
448 {"atan", math_atan, 1, 0, 0},
449 {"atan2", math_atan2, 2, 0, 0},
450 {"ceil", math_ceil, 1, 0, 0},
451 {"cos", math_cos, 1, 0, 0},
452 {"exp", math_exp, 1, 0, 0},
453 {"floor", math_floor, 1, 0, 0},
454 {"log", math_log, 1, 0, 0},
455 {"max", math_max, 2, 0, 0},
456 {"min", math_min, 2, 0, 0},
457 {"pow", math_pow, 2, 0, 0},
458 {"random", math_random, 0, 0, 0},
459 {"round", math_round, 1, 0, 0},
460 {"sin", math_sin, 1, 0, 0},
461 {"sqrt", math_sqrt, 1, 0, 0},
462 {"tan", math_tan, 1, 0, 0},
463 {0,0,0,0,0}
464 };
465
466 JSObject *
467 js_InitMathClass(JSContext *cx, JSObject *obj)
468 17 {
469 JSObject *Math;
470
471 17 Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0);
472 17 if (!Math)
473 0 return NULL;
474 17 if (!JS_DefineFunctions(cx, Math, math_static_methods))
475 0 return NULL;
476 17 if (!JS_DefineConstDoubles(cx, Math, math_constants))
477 0 return NULL;
478 17 return Math;