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 z = fd_asin(x);
133 return js_NewNumberValue(cx, z, rval);
134 }
136 static JSBool
137 math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
138 {
139 jsdouble x, z;
141 if (!js_ValueToNumber(cx, argv[0], &x))
142 return JS_FALSE;
143 z = fd_atan(x);
144 return js_NewNumberValue(cx, z, rval);
145 }
147 static JSBool
148 math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
149 {
150 jsdouble x, y, z;
152 if (!js_ValueToNumber(cx, argv[0], &x))
153 return JS_FALSE;
154 if (!js_ValueToNumber(cx, argv[1], &y))
155 return JS_FALSE;
156 z = fd_atan2(x, y);
157 return js_NewNumberValue(cx, z, rval);
158 }
160 static JSBool
161 math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
162 {
163 jsdouble x, z;
165 if (!js_ValueToNumber(cx, argv[0], &x))
166 return JS_FALSE;
167 z = fd_ceil(x);
168 return js_NewNumberValue(cx, z, rval);
169 }
171 static JSBool
172 math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
173 {
174 jsdouble x, z;
176 if (!js_ValueToNumber(cx, argv[0], &x))
177 return JS_FALSE;
178 z = fd_cos(x);
179 return js_NewNumberValue(cx, z, rval);
180 }
182 static JSBool
183 math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
184 {
185 jsdouble x, z;
187 if (!js_ValueToNumber(cx, argv[0], &x))
188 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 z = fd_exp(x);
202 return js_NewNumberValue(cx, z, rval);
203 }
205 static JSBool
206 math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
207 {
208 jsdouble x, z;
210 if (!js_ValueToNumber(cx, argv[0], &x))
211 return JS_FALSE;
212 z = fd_floor(x);
213 return js_NewNumberValue(cx, z, rval);
214 }
216 static JSBool
217 math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
218 {
219 jsdouble x, z;
221 if (!js_ValueToNumber(cx, argv[0], &x))
222 return JS_FALSE;
223 z = fd_log(x);
224 return js_NewNumberValue(cx, z, rval);
225 }
227 static JSBool
228 math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
229 {
230 jsdouble x, z = *cx->runtime->jsNegativeInfinity;
231 uintN i;
233 if (argc == 0) {
234 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);
235 return JS_TRUE;
236 }
237 for (i = 0; i < argc; i++) {
238 if (!js_ValueToNumber(cx, argv[i], &x))
239 return JS_FALSE;
240 if (JSDOUBLE_IS_NaN(x)) {
241 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
242 return JS_TRUE;
243 }
244 if ((x==0)&&(x==z)&&(fd_copysign(1.0,z)==-1))
245 z = x;
246 else
247 z = (x > z) ? x : z;
248 }
249 return js_NewNumberValue(cx, z, rval);
250 }
252 static JSBool
253 math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
254 {
255 jsdouble x, z = *cx->runtime->jsPositiveInfinity;
256 uintN i;
258 if (argc == 0) {
259 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
260 return JS_TRUE;
261 }
262 for (i = 0; i < argc; i++) {
263 if (!js_ValueToNumber(cx, argv[i], &x))
264 return JS_FALSE;
265 if (JSDOUBLE_IS_NaN(x)) {
266 *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
267 return JS_TRUE;
268 }
269 if ((x==0)&&(x==z)&&(fd_copysign(1.0,x)==-1))
270 z = x;
271 else
272 z = (x < z) ? x : z;
273 }
274 return js_NewNumberValue(cx, z, rval);
275 }
277 static JSBool
278 math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
279 {
280 jsdouble x, y, z;
282 if (!js_ValueToNumber(cx, argv[0], &x))
283 return JS_FALSE;
284 if (!js_ValueToNumber(cx, argv[1], &y))
285 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 z = fd_pow(x, y);
297 return js_NewNumberValue(cx, z, rval);
298 }
300 /*
301 * Math.random() support, lifted from java.util.Random.java.
302 */
303 static void
304 random_setSeed(JSRuntime *rt, int64 seed)
305 {
306 int64 tmp;
308 JSLL_I2L(tmp, 1000);
309 JSLL_DIV(seed, seed, tmp);
310 JSLL_XOR(tmp, seed, rt->rngMultiplier);
311 JSLL_AND(rt->rngSeed, tmp, rt->rngMask);
312 }
314 static void
315 random_init(JSRuntime *rt)
316 {
317 int64 tmp, tmp2;
319 /* Do at most once. */
320 if (rt->rngInitialized)
321 return;
322 rt->rngInitialized = JS_TRUE;
324 /* rt->rngMultiplier = 0x5DEECE66DL */
325 JSLL_ISHL(tmp, 0x5, 32);
326 JSLL_UI2L(tmp2, 0xDEECE66DL);
327 JSLL_OR(rt->rngMultiplier, tmp, tmp2);
329 /* rt->rngAddend = 0xBL */
330 JSLL_I2L(rt->rngAddend, 0xBL);
332 /* rt->rngMask = (1L << 48) - 1 */
333 JSLL_I2L(tmp, 1);
334 JSLL_SHL(tmp2, tmp, 48);
335 JSLL_SUB(rt->rngMask, tmp2, tmp);
337 /* rt->rngDscale = (jsdouble)(1L << 53) */
338 JSLL_SHL(tmp2, tmp, 53);
339 JSLL_L2D(rt->rngDscale, tmp2);
341 /* Finally, set the seed from current time. */
342 random_setSeed(rt, PRMJ_Now());
343 }
345 static uint32
346 random_next(JSRuntime *rt, int bits)
347 {
348 int64 nextseed, tmp;
349 uint32 retval;
351 JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier);
352 JSLL_ADD(nextseed, nextseed, rt->rngAddend);
353 JSLL_AND(nextseed, nextseed, rt->rngMask);
354 rt->rngSeed = nextseed;
355 JSLL_USHR(tmp, nextseed, 48 - bits);
356 JSLL_L2I(retval, tmp);
357 return retval;
358 }
360 static jsdouble
361 random_nextDouble(JSRuntime *rt)
362 {
363 int64 tmp, tmp2;
364 jsdouble d;
366 JSLL_ISHL(tmp, random_next(rt, 26), 27);
367 JSLL_UI2L(tmp2, random_next(rt, 27));
368 JSLL_ADD(tmp, tmp, tmp2);
369 JSLL_L2D(d, tmp);
370 return d / rt->rngDscale;
371 }
373 static JSBool
374 math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
375 {
376 JSRuntime *rt;
377 jsdouble z;
379 rt = cx->runtime;
380 JS_LOCK_RUNTIME(rt);
381 random_init(rt);
382 z = random_nextDouble(rt);
383 JS_UNLOCK_RUNTIME(rt);
384 return js_NewNumberValue(cx, z, rval);
385 }
387 static JSBool
388 math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
389 {
390 jsdouble x, z;
392 if (!js_ValueToNumber(cx, argv[0], &x))
393 return JS_FALSE;
394 z = fd_copysign(fd_floor(x + 0.5), x);
395 return js_NewNumberValue(cx, z, rval);
396 }
398 static JSBool
399 math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
400 {
401 jsdouble x, z;
403 if (!js_ValueToNumber(cx, argv[0], &x))
404 return JS_FALSE;
405 z = fd_sin(x);
406 return js_NewNumberValue(cx, z, rval);
407 }
409 static JSBool
410 math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
411 {
412 jsdouble x, z;
414 if (!js_ValueToNumber(cx, argv[0], &x))
415 return JS_FALSE;
416 z = fd_sqrt(x);
417 return js_NewNumberValue(cx, z, rval);
418 }
420 static JSBool
421 math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
422 {
423 jsdouble x, z;
425 if (!js_ValueToNumber(cx, argv[0], &x))
426 return JS_FALSE;
427 z = fd_tan(x);
428 return js_NewNumberValue(cx, z, rval);
429 }
431 #if JS_HAS_TOSOURCE
432 static JSBool
433 math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
434 jsval *rval)
435 {
436 *rval = ATOM_KEY(cx->runtime->atomState.MathAtom);
437 return JS_TRUE;
438 }
439 #endif
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 };
466 JSObject *
467 js_InitMathClass(JSContext *cx, JSObject *obj)
468 {
469 JSObject *Math;
471 Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0);
472 if (!Math)
473 return NULL;
474 if (!JS_DefineFunctions(cx, Math, math_static_methods))
475 return NULL;
476 if (!JS_DefineConstDoubles(cx, Math, math_constants))
477 return NULL;
478 return Math;
479 }