1 /*
2 * mathfunc.c - basic mathematical functions for use in math evaluations
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1999 Peter Stephenson
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Peter Stephenson or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Peter Stephenson and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Peter Stephenson and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose. The software
24 * provided hereunder is on an "as is" basis, and Peter Stephenson and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30 #include "mathfunc.mdh"
31 #include "mathfunc.pro"
32
33 #include <math.h>
34
35 enum {
36 MF_ABS,
37 MF_ACOS,
38 MF_ACOSH,
39 MF_ASIN,
40 MF_ASINH,
41 MF_ATAN,
42 MF_ATANH,
43 MF_CBRT,
44 MF_CEIL,
45 MF_COPYSIGN,
46 MF_COS,
47 MF_COSH,
48 MF_ERF,
49 MF_ERFC,
50 MF_EXP,
51 MF_EXPM1,
52 MF_FABS,
53 MF_FLOAT,
54 MF_FLOOR,
55 MF_FMOD,
56 MF_GAMMA,
57 MF_HYPOT,
58 MF_ILOGB,
59 MF_INT,
60 MF_J0,
61 MF_J1,
62 MF_JN,
63 MF_LDEXP,
64 MF_LGAMMA,
65 MF_LOG,
66 MF_LOG10,
67 MF_LOG1P,
68 MF_LOG2,
69 MF_LOGB,
70 MF_NEXTAFTER,
71 MF_RINT,
72 MF_SCALB,
73 #ifdef HAVE_SIGNGAM
74 MF_SIGNGAM,
75 #endif
76 MF_SIN,
77 MF_SINH,
78 MF_SQRT,
79 MF_TAN,
80 MF_TANH,
81 MF_Y0,
82 MF_Y1,
83 MF_YN
84 };
85
86 /* also functions taking a string argument */
87
88 enum {
89 MS_RAND48
90 };
91
92 /*
93 * also to do, but differently argument or returned: abs (no type
94 * conversion), atan2.
95 */
96
97 /*
98 * Flags for type of function: unlike the above, these must
99 * be individually bit-testable.
100 */
101
102 enum {
103 TF_NOCONV = 1, /* don't convert to float */
104 TF_INT1 = 2, /* first argument is integer */
105 TF_INT2 = 4, /* second argument is integer */
106 TF_NOASS = 8 /* don't assign result as double */
107 };
108
109 #define TFLAG(x) ((x) << 8)
110
111
112 static struct mathfunc mftab[] = {
113 NUMMATHFUNC("abs", math_func, 1, 1, MF_ABS |
114 TFLAG(TF_NOCONV|TF_NOASS)),
115 NUMMATHFUNC("acos", math_func, 1, 1, MF_ACOS),
116 NUMMATHFUNC("acosh", math_func, 1, 1, MF_ACOSH),
117 NUMMATHFUNC("asin", math_func, 1, 1, MF_ASIN),
118 NUMMATHFUNC("asinh", math_func, 1, 1, MF_ASINH),
119 NUMMATHFUNC("atan", math_func, 1, 2, MF_ATAN),
120 NUMMATHFUNC("atanh", math_func, 1, 1, MF_ATANH),
121 NUMMATHFUNC("cbrt", math_func, 1, 1, MF_CBRT),
122 NUMMATHFUNC("ceil", math_func, 1, 1, MF_CEIL),
123 NUMMATHFUNC("copysign", math_func, 2, 2, MF_COPYSIGN),
124 NUMMATHFUNC("cos", math_func, 1, 1, MF_COS),
125 NUMMATHFUNC("cosh", math_func, 1, 1, MF_COSH),
126 NUMMATHFUNC("erf", math_func, 1, 1, MF_ERF),
127 NUMMATHFUNC("erfc", math_func, 1, 1, MF_ERFC),
128 NUMMATHFUNC("exp", math_func, 1, 1, MF_EXP),
129 NUMMATHFUNC("expm1", math_func, 1, 1, MF_EXPM1),
130 NUMMATHFUNC("fabs", math_func, 1, 1, MF_FABS),
131 NUMMATHFUNC("float", math_func, 1, 1, MF_FLOAT),
132 NUMMATHFUNC("floor", math_func, 1, 1, MF_FLOOR),
133 NUMMATHFUNC("fmod", math_func, 2, 2, MF_FMOD),
134 NUMMATHFUNC("gamma", math_func, 1, 1, MF_GAMMA),
135 NUMMATHFUNC("hypot", math_func, 2, 2, MF_HYPOT),
136 NUMMATHFUNC("ilogb", math_func, 1, 1, MF_ILOGB | TFLAG(TF_NOASS)),
137 NUMMATHFUNC("int", math_func, 1, 1, MF_INT | TFLAG(TF_NOASS)),
138 NUMMATHFUNC("j0", math_func, 1, 1, MF_J0),
139 NUMMATHFUNC("j1", math_func, 1, 1, MF_J1),
140 NUMMATHFUNC("jn", math_func, 2, 2, MF_JN | TFLAG(TF_INT1)),
141 NUMMATHFUNC("ldexp", math_func, 2, 2, MF_LDEXP | TFLAG(TF_INT2)),
142 NUMMATHFUNC("lgamma", math_func, 1, 1, MF_LGAMMA),
143 NUMMATHFUNC("log", math_func, 1, 1, MF_LOG),
144 NUMMATHFUNC("log10", math_func, 1, 1, MF_LOG10),
145 NUMMATHFUNC("log1p", math_func, 1, 1, MF_LOG1P),
146 NUMMATHFUNC("log2", math_func, 1, 1, MF_LOG2),
147 NUMMATHFUNC("logb", math_func, 1, 1, MF_LOGB),
148 NUMMATHFUNC("nextafter", math_func, 2, 2, MF_NEXTAFTER),
149 #ifdef HAVE_ERAND48
150 STRMATHFUNC("rand48", math_string, MS_RAND48),
151 #endif
152 NUMMATHFUNC("rint", math_func, 1, 1, MF_RINT),
153 NUMMATHFUNC("scalb", math_func, 2, 2, MF_SCALB | TFLAG(TF_INT2)),
154 #ifdef HAVE_SIGNGAM
155 NUMMATHFUNC("signgam", math_func, 0, 0, MF_SIGNGAM | TFLAG(TF_NOASS)),
156 #endif
157 NUMMATHFUNC("sin", math_func, 1, 1, MF_SIN),
158 NUMMATHFUNC("sinh", math_func, 1, 1, MF_SINH),
159 NUMMATHFUNC("sqrt", math_func, 1, 1, MF_SQRT),
160 NUMMATHFUNC("tan", math_func, 1, 1, MF_TAN),
161 NUMMATHFUNC("tanh", math_func, 1, 1, MF_TANH),
162 NUMMATHFUNC("y0", math_func, 1, 1, MF_Y0),
163 NUMMATHFUNC("y1", math_func, 1, 1, MF_Y1),
164 NUMMATHFUNC("yn", math_func, 2, 2, MF_YN | TFLAG(TF_INT1))
165 };
166
167 /**/
168 static mnumber
math_func(UNUSED (char * name),int argc,mnumber * argv,int id)169 math_func(UNUSED(char *name), int argc, mnumber *argv, int id)
170 {
171 mnumber ret;
172 double argd = 0, argd2 = 0, retd = 0;
173 int argi = 0;
174
175 if (argc && !(id & TFLAG(TF_NOCONV))) {
176 if (id & TFLAG(TF_INT1))
177 argi = (argv->type == MN_FLOAT) ? (zlong)argv->u.d : argv->u.l;
178 else
179 argd = (argv->type == MN_INTEGER) ? (double)argv->u.l : argv->u.d;
180 if (argc > 1) {
181 if (id & TFLAG(TF_INT2))
182 argi = (argv[1].type == MN_FLOAT) ? (zlong)argv[1].u.d :
183 argv[1].u.l;
184 else
185 argd2 = (argv[1].type == MN_INTEGER) ? (double)argv[1].u.l :
186 argv[1].u.d;
187 }
188 }
189
190 ret.type = MN_FLOAT;
191 ret.u.d = 0;
192
193 if (errflag)
194 return ret;
195
196 switch (id & 0xff) {
197 case MF_ABS:
198 ret.type = argv->type;
199 if (argv->type == MN_INTEGER)
200 ret.u.l = (argv->u.l < 0) ? - argv->u.l : argv->u.l;
201 else
202 ret.u.d = fabs(argv->u.d);
203 break;
204
205 case MF_ACOS:
206 retd = acos(argd);
207 break;
208
209 case MF_ACOSH:
210 retd = acosh(argd);
211 break;
212
213 case MF_ASIN:
214 retd = asin(argd);
215 break;
216
217 case MF_ASINH:
218 retd = asinh(argd);
219 break;
220
221 case MF_ATAN:
222 if (argc == 2)
223 retd = atan2(argd, argd2);
224 else
225 retd = atan(argd);
226 break;
227
228 case MF_ATANH:
229 retd = atanh(argd);
230 break;
231
232 case MF_CBRT:
233 retd = cbrt(argd);
234 break;
235
236 case MF_CEIL:
237 retd = ceil(argd);
238 break;
239
240 case MF_COPYSIGN:
241 retd = copysign(argd, argd2);
242 break;
243
244 case MF_COS:
245 retd = cos(argd);
246 break;
247
248 case MF_COSH:
249 retd = cosh(argd);
250 break;
251
252 case MF_ERF:
253 retd = erf(argd);
254 break;
255
256 case MF_ERFC:
257 retd = erfc(argd);
258 break;
259
260 case MF_EXP:
261 retd = exp(argd);
262 break;
263
264 case MF_EXPM1:
265 retd = expm1(argd);
266 break;
267
268 case MF_FABS:
269 retd = fabs(argd);
270 break;
271
272 case MF_FLOAT:
273 retd = argd;
274 break;
275
276 case MF_FLOOR:
277 retd = floor(argd);
278 break;
279
280 case MF_FMOD:
281 retd = fmod(argd, argd2);
282 break;
283
284 case MF_GAMMA:
285 #ifdef HAVE_TGAMMA
286 retd = tgamma(argd);
287 #else
288 #ifdef HAVE_SIGNGAM
289 retd = lgamma(argd);
290 retd = signgam*exp(retd);
291 #else /*XXX assume gamma(x) returns Gamma(x), not log(|Gamma(x)|) */
292 retd = gamma(argd);
293 #endif
294 #endif
295 break;
296
297 case MF_HYPOT:
298 retd = hypot(argd, argd2);
299 break;
300
301 case MF_ILOGB:
302 ret.type = MN_INTEGER;
303 ret.u.l = ilogb(argd);
304 break;
305
306 case MF_INT:
307 ret.type = MN_INTEGER;
308 ret.u.l = (zlong)argd;
309 break;
310
311 case MF_J0:
312 retd = j0(argd);
313 break;
314
315 case MF_J1:
316 retd = j1(argd);
317 break;
318
319 case MF_JN:
320 retd = jn(argi, argd2);
321 break;
322
323 case MF_LDEXP:
324 retd = ldexp(argd, argi);
325 break;
326
327 case MF_LGAMMA:
328 retd = lgamma(argd);
329 break;
330
331 case MF_LOG:
332 retd = log(argd);
333 break;
334
335 case MF_LOG10:
336 retd = log10(argd);
337 break;
338
339 case MF_LOG1P:
340 retd = log1p(argd);
341 break;
342
343 case MF_LOG2:
344 #ifdef HAVE_LOG2
345 retd = log2(argd);
346 #else
347 retd = log(argd) / log(2);
348 #endif
349 break;
350
351 case MF_LOGB:
352 retd = logb(argd);
353 break;
354
355 case MF_NEXTAFTER:
356 retd = nextafter(argd, argd2);
357 break;
358
359 case MF_RINT:
360 retd = rint(argd);
361 break;
362
363 case MF_SCALB:
364 #ifdef HAVE_SCALBN
365 retd = scalbn(argd, argi);
366 #else
367 retd = scalb(argd, argi);
368 #endif
369 break;
370
371 #ifdef HAVE_SIGNGAM
372 case MF_SIGNGAM:
373 ret.type = MN_INTEGER;
374 ret.u.l = signgam;
375 break;
376 #endif
377
378 case MF_SIN:
379 retd = sin(argd);
380 break;
381
382 case MF_SINH:
383 retd = sinh(argd);
384 break;
385
386 case MF_SQRT:
387 retd = sqrt(argd);
388 break;
389
390 case MF_TAN:
391 retd = tan(argd);
392 break;
393
394 case MF_TANH:
395 retd = tanh(argd);
396 break;
397
398 case MF_Y0:
399 retd = y0(argd);
400 break;
401
402 case MF_Y1:
403 retd = y1(argd);
404 break;
405
406 case MF_YN:
407 retd = yn(argi, argd2);
408 break;
409
410 #ifdef DEBUG
411 default:
412 fprintf(stderr, "BUG: mathfunc type not handled: %d", id);
413 break;
414 #endif
415 }
416
417 if (!(id & TFLAG(TF_NOASS)))
418 ret.u.d = retd;
419
420 return ret;
421 }
422
423 /**/
424 static mnumber
math_string(UNUSED (char * name),char * arg,int id)425 math_string(UNUSED(char *name), char *arg, int id)
426 {
427 mnumber ret = zero_mnumber;
428 char *send;
429 /*
430 * Post-process the string argument, which is just passed verbatim.
431 * Not clear if any other functions that use math_string() will
432 * want this, but assume so for now.
433 */
434 while (iblank(*arg))
435 arg++;
436 send = arg + strlen(arg);
437 while (send > arg && iblank(send[-1]))
438 send--;
439 *send = '\0';
440
441 switch (id)
442 {
443 #ifdef HAVE_ERAND48
444 case MS_RAND48:
445 {
446 static unsigned short seedbuf[3];
447 static int seedbuf_init;
448 unsigned short tmp_seedbuf[3], *seedbufptr;
449 int do_init = 1;
450
451 if (*arg) {
452 /* Seed is contained in parameter named by arg */
453 char *seedstr;
454 seedbufptr = tmp_seedbuf;
455 if ((seedstr = getsparam(arg)) && strlen(seedstr) >= 12) {
456 int i, j;
457 do_init = 0;
458 /*
459 * Decode three sets of four hex digits corresponding
460 * to each unsigned short.
461 */
462 for (i = 0; i < 3 && !do_init; i++) {
463 unsigned short *seedptr = seedbufptr + i;
464 *seedptr = 0;
465 for (j = 0; j < 4; j++) {
466 if (idigit(*seedstr))
467 *seedptr += *seedstr - '0';
468 else if (tolower(*seedstr) >= 'a' &&
469 tolower(*seedstr) <= 'f')
470 *seedptr += tolower(*seedstr) - 'a' + 10;
471 else {
472 do_init = 1;
473 break;
474 }
475 seedstr++;
476 if (j < 3)
477 *seedptr *= 16;
478 }
479 }
480 }
481 else if (errflag)
482 break;
483 }
484 else
485 {
486 /* Use default seed: must be initialised. */
487 seedbufptr = seedbuf;
488 if (!seedbuf_init)
489 seedbuf_init = 1;
490 else
491 do_init = 1;
492 }
493 if (do_init) {
494 seedbufptr[0] = (unsigned short)rand();
495 seedbufptr[1] = (unsigned short)rand();
496 seedbufptr[2] = (unsigned short)rand();
497 /*
498 * Some implementations of rand48() need initialization.
499 * This is likely to be harmless elsewhere, since
500 * according to the documentation erand48() normally
501 * doesn't look at the seed set in this way.
502 */
503 (void)seed48(seedbufptr);
504 }
505 ret.type = MN_FLOAT;
506 ret.u.d = erand48(seedbufptr);
507
508 if (*arg)
509 {
510 char outbuf[13];
511 sprintf(outbuf, "%04x%04x%04x", (int)seedbufptr[0],
512 (int)seedbufptr[1], (int)seedbufptr[2]);
513 setsparam(arg, ztrdup(outbuf));
514 }
515 }
516 break;
517 #endif
518 }
519
520 return ret;
521 }
522
523
524 static struct features module_features = {
525 NULL, 0,
526 NULL, 0,
527 mftab, sizeof(mftab)/sizeof(*mftab),
528 NULL, 0,
529 0
530 };
531
532 /**/
533 int
setup_(UNUSED (Module m))534 setup_(UNUSED(Module m))
535 {
536 return 0;
537 }
538
539 /**/
540 int
features_(Module m,char *** features)541 features_(Module m, char ***features)
542 {
543 *features = featuresarray(m, &module_features);
544 return 0;
545 }
546
547 /**/
548 int
enables_(Module m,int ** enables)549 enables_(Module m, int **enables)
550 {
551 return handlefeatures(m, &module_features, enables);
552 }
553
554 /**/
555 int
boot_(UNUSED (Module m))556 boot_(UNUSED(Module m))
557 {
558 return 0;
559 }
560
561 /**/
562 int
cleanup_(Module m)563 cleanup_(Module m)
564 {
565 return setfeatureenables(m, &module_features, NULL);
566 }
567
568 /**/
569 int
finish_(UNUSED (Module m))570 finish_(UNUSED(Module m))
571 {
572 return 0;
573 }
574