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 ***** */
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"
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
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 };
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 };
103 static JSBool
104 math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
105 {
106 jsdouble x, z;
108 if (!js_ValueToNumber(cx, argv[0], &x))
109 return JS_FALSE;
110 z = fd_fabs(x);
111 return js_NewNumberValue(cx, z, rval);
112 }
114 static JSBool
115 math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
116 {
117 jsdouble x, z;
119 if (!js_ValueToNumber(cx, argv[0], &x))
120 return JS_FALSE;
121 z = fd_acos(x);
122 return js_NewNumberValue(cx, z, rval);
123 }
125 static JSBool
126 math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
127 {
128 jsdouble x, z;
130 if (!js_ValueToNumber(cx, argv[0], &x))
131 return JS_FALSE;
132 #ifdef XP_MAC
133 if (x == 0)
134 return js_NewNumberValue(cx, x, rval);
135 #endif
136 z = fd_asin(x);
137 return js_NewNumberValue(cx, z, rval);
138 }
140 static JSBool
141 math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
142 {
143 jsdouble x, z;
145 if (!js_ValueToNumber(cx, argv[0], &x))
146 return JS_FALSE;
147 #ifdef XP_MAC
148 if (x == 0)
149 return js_NewNumberValue(cx, x, rval);
150 #endif
151 z = fd_atan(x);
152 return js_NewNumberValue(cx, z, rval);
153 }
155 static JSBool
156 math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
157 {
158 jsdouble x, y, z;
160 if (!js_ValueToNumber(cx, argv[0], &x))
161 return JS_FALSE;
162 if (!js_ValueToNumber(cx, argv[1], &y))
163 return JS_FALSE;
164 z = fd_atan2(x, y);
165 return js_NewNumberValue(cx, z, rval);
166 }
168 static JSBool
169 math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
170 {
171 jsdouble x, z;
173 if (!js_ValueToNumber(cx, argv[0], &x))
174 return JS_FALSE;
175 z = fd_ceil(x);
176 return js_NewNumberValue(cx, z, rval);
177 }
179 static JSBool
180 math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
181 {
182 jsdouble x, z;
184 if (!js_ValueToNumber(cx, argv[0], &x))
185 return JS_FALSE;
186 z = fd_cos(x);
187 return js_NewNumberValue(cx, z, rval);
188 }
190 static JSBool
191 math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
192 {
193 jsdouble x, z;
195 if (!js_ValueToNumber(cx, argv[0], &x))
196 return JS_FALSE;
197 #ifdef _WIN32
198 if (!JSDOUBLE_IS_NaN(x)) {
199 if (x == *cx->runtime->jsPositiveInfinity) {
200 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
201 return JS_TRUE;
202 }
203 if (x == *cx->runtime->jsNegativeInfinity) {
204 *rval = JSVAL_ZERO;
205 return JS_TRUE;
206 }
207 }
208 #endif
209 z = fd_exp(x);
210 return js_NewNumberValue(cx, z, rval);
211 }
213 static JSBool
214 math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
215 {
216 jsdouble x, z;
218 if (!js_ValueToNumber(cx, argv[0], &x))
219 return JS_FALSE;
220 z = fd_floor(x);
221 return js_NewNumberValue(cx, z, rval);
222 }
224 static JSBool
225 math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
226 {
227 jsdouble x, z;
229 if (!js_ValueToNumber(cx, argv[0], &x))
230 return JS_FALSE;
231 z = fd_log(x);
232 return js_NewNumberValue(cx, z, rval);
233 }
235 static JSBool
236 math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
237 {
238 jsdouble x, z = *cx->runtime->jsNegativeInfinity;
239 uintN i;
241 if (argc == 0) {
242 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);
243 return JS_TRUE;
244 }
245 for (i = 0; i < argc; i++) {
246 if (!js_ValueToNumber(cx, argv[i], &x))
247 return JS_FALSE;
248 if (JSDOUBLE_IS_NaN(x)) {
249 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
250 return JS_TRUE;
251 }
252 if ((x==0)&&(x==z)&&(fd_copysign(1.0,z)==-1))
253 z = x;
254 else
255 z = (x > z) ? x : z;
256 }
257 return js_NewNumberValue(cx, z, rval);
258 }
260 static JSBool
261 math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
262 {
263 jsdouble x, z = *cx->runtime->jsPositiveInfinity;
264 uintN i;
266 if (argc == 0) {
267 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
268 return JS_TRUE;
269 }
270 for (i = 0; i < argc; i++) {
271 if (!js_ValueToNumber(cx, argv[i], &x))
272 return JS_FALSE;
273 if (JSDOUBLE_IS_NaN(x)) {
274 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
275 return JS_TRUE;
276 }
277 if ((x==0)&&(x==z)&&(fd_copysign(1.0,x)==-1))
278 z = x;
279 else
280 z = (x < z) ? x : z;
281 }
282 return js_NewNumberValue(cx, z, rval);
283 }
285 static JSBool
286 math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
287 {
288 jsdouble x, y, z;
290 if (!js_ValueToNumber(cx, argv[0], &x))
291 return JS_FALSE;
292 if (!js_ValueToNumber(cx, argv[1], &y))
293 return JS_FALSE;
294 z = fd_pow(x, y);
295 return js_NewNumberValue(cx, z, rval);
296 }
298 /*
299 * Math.random() support, lifted from java.util.Random.java.
300 */
301 static void
302 random_setSeed(JSRuntime *rt, int64 seed)
303 {
304 int64 tmp;
306 JSLL_I2L(tmp, 1000);
307 JSLL_DIV(seed, seed, tmp);
308 JSLL_XOR(tmp, seed, rt->rngMultiplier);
309 JSLL_AND(rt->rngSeed, tmp, rt->rngMask);
310 }
312 static void
313 random_init(JSRuntime *rt)
314 {
315 int64 tmp, tmp2;
317 /* Do at most once. */
318 if (rt->rngInitialized)
319 return;
320 rt->rngInitialized = JS_TRUE;
322 /* rt->rngMultiplier = 0x5DEECE66DL */
323 JSLL_ISHL(tmp, 0x5, 32);
324 JSLL_UI2L(tmp2, 0xDEECE66DL);
325 JSLL_OR(rt->rngMultiplier, tmp, tmp2);
327 /* rt->rngAddend = 0xBL */
328 JSLL_I2L(rt->rngAddend, 0xBL);
330 /* rt->rngMask = (1L << 48) - 1 */
331 JSLL_I2L(tmp, 1);
332 JSLL_SHL(tmp2, tmp, 48);
333 JSLL_SUB(rt->rngMask, tmp2, tmp);
335 /* rt->rngDscale = (jsdouble)(1L << 53) */
336 JSLL_SHL(tmp2, tmp, 53);
337 JSLL_L2D(rt->rngDscale, tmp2);
339 /* Finally, set the seed from current time. */
340 random_setSeed(rt, PRMJ_Now());
341 }
343 static uint32
344 random_next(JSRuntime *rt, int bits)
345 {
346 int64 nextseed, tmp;
347 uint32 retval;
349 JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier);
350 JSLL_ADD(nextseed, nextseed, rt->rngAddend);
351 JSLL_AND(nextseed, nextseed, rt->rngMask);
352 rt->rngSeed = nextseed;
353 JSLL_USHR(tmp, nextseed, 48 - bits);
354 JSLL_L2I(retval, tmp);
355 return retval;
356 }
358 static jsdouble
359 random_nextDouble(JSRuntime *rt)
360 {
361 int64 tmp, tmp2;
362 jsdouble d;
364 JSLL_ISHL(tmp, random_next(rt, 26), 27);
365 JSLL_UI2L(tmp2, random_next(rt, 27));
366 JSLL_ADD(tmp, tmp, tmp2);
367 JSLL_L2D(d, tmp);
368 return d / rt->rngDscale;
369 }
371 static JSBool
372 math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
373 {
374 JSRuntime *rt;
375 jsdouble z;
377 rt = cx->runtime;
378 JS_LOCK_RUNTIME(rt);
379 random_init(rt);
380 z = random_nextDouble(rt);
381 JS_UNLOCK_RUNTIME(rt);
382 return js_NewNumberValue(cx, z, rval);
383 }
385 static JSBool
386 math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
387 {
388 jsdouble x, z;
390 if (!js_ValueToNumber(cx, argv[0], &x))
391 return JS_FALSE;
392 z = fd_copysign(fd_floor(x + 0.5), x);
393 return js_NewNumberValue(cx, z, rval);
394 }
396 static JSBool
397 math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
398 {
399 jsdouble x, z;
401 if (!js_ValueToNumber(cx, argv[0], &x))
402 return JS_FALSE;
403 z = fd_sin(x);
404 return js_NewNumberValue(cx, z, rval);
405 }
407 static JSBool
408 math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
409 {
410 jsdouble x, z;
412 if (!js_ValueToNumber(cx, argv[0], &x))
413 return JS_FALSE;
414 z = fd_sqrt(x);
415 return js_NewNumberValue(cx, z, rval);
416 }
418 static JSBool
419 math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
420 {
421 jsdouble x, z;
423 if (!js_ValueToNumber(cx, argv[0], &x))
424 return JS_FALSE;
425 z = fd_tan(x);
426 return js_NewNumberValue(cx, z, rval);
427 }
429 #if JS_HAS_TOSOURCE
430 static JSBool
431 math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
432 jsval *rval)
433 {
434 *rval = ATOM_KEY(cx->runtime->atomState.MathAtom);
435 return JS_TRUE;
436 }
437 #endif
439 static JSFunctionSpec math_static_methods[] = {
440 #if JS_HAS_TOSOURCE
441 {js_toSource_str, math_toSource, 0, 0, 0},
442 #endif
443 {"abs", math_abs, 1, 0, 0},
444 {"acos", math_acos, 1, 0, 0},
445 {"asin", math_asin, 1, 0, 0},
446 {"atan", math_atan, 1, 0, 0},
447 {"atan2", math_atan2, 2, 0, 0},
448 {"ceil", math_ceil, 1, 0, 0},
449 {"cos", math_cos, 1, 0, 0},
450 {"exp", math_exp, 1, 0, 0},
451 {"floor", math_floor, 1, 0, 0},
452 {"log", math_log, 1, 0, 0},
453 {"max", math_max, 2, 0, 0},
454 {"min", math_min, 2, 0, 0},
455 {"pow", math_pow, 2, 0, 0},
456 {"random", math_random, 0, 0, 0},
457 {"round", math_round, 1, 0, 0},
458 {"sin", math_sin, 1, 0, 0},
459 {"sqrt", math_sqrt, 1, 0, 0},
460 {"tan", math_tan, 1, 0, 0},
461 {0,0,0,0,0}
462 };
464 JSObject *
465 js_InitMathClass(JSContext *cx, JSObject *obj)
466 {
467 JSObject *Math;
469 Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0);
470 if (!Math)
471 return NULL;
472 if (!JS_DefineFunctions(cx, Math, math_static_methods))
473 return NULL;
474 if (!JS_DefineConstDoubles(cx, Math, math_constants))
475 return NULL;
476 return Math;
477 }