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