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