1 #include <math.h>
2 #include <string.h>
3 
4 #include "nasal.h"
5 
6 // Toss a runtime error for any NaN or Inf values produced.  Note that
7 // this assumes an IEEE 754 format.
8 #define VALIDATE(r) (valid(r.num) ? (r) : die(c, __FUNCTION__+2))
9 
valid(double d)10 static int valid(double d)
11 {
12     return isfinite(d);
13 }
14 
die(naContext c,const char * fn)15 static naRef die(naContext c, const char* fn)
16 {
17     naRuntimeError(c, "floating point error in math.%s()", fn);
18     return naNil();
19 }
20 
f_sin(naContext c,naRef me,int argc,naRef * args)21 static naRef f_sin(naContext c, naRef me, int argc, naRef* args)
22 {
23     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
24     if(naIsNil(a))
25         naRuntimeError(c, "non numeric argument to sin()");
26     a.num = sin(a.num);
27     return VALIDATE(a);
28 }
29 
f_cos(naContext c,naRef me,int argc,naRef * args)30 static naRef f_cos(naContext c, naRef me, int argc, naRef* args)
31 {
32     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
33     if(naIsNil(a))
34         naRuntimeError(c, "non numeric argument to cos()");
35     a.num = cos(a.num);
36     return VALIDATE(a);
37 }
38 
f_exp(naContext c,naRef me,int argc,naRef * args)39 static naRef f_exp(naContext c, naRef me, int argc, naRef* args)
40 {
41     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
42     if(naIsNil(a))
43         naRuntimeError(c, "non numeric argument to exp()");
44     a.num = exp(a.num);
45     return VALIDATE(a);
46 }
47 
f_ln(naContext c,naRef me,int argc,naRef * args)48 static naRef f_ln(naContext c, naRef me, int argc, naRef* args)
49 {
50     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
51     if(naIsNil(a))
52         naRuntimeError(c, "non numeric argument to ln()");
53     a.num = log(a.num);
54     return VALIDATE(a);
55 }
56 
f_sqrt(naContext c,naRef me,int argc,naRef * args)57 static naRef f_sqrt(naContext c, naRef me, int argc, naRef* args)
58 {
59     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
60     if(naIsNil(a))
61         naRuntimeError(c, "non numeric argument to sqrt()");
62     a.num = sqrt(a.num);
63     return VALIDATE(a);
64 }
65 
f_pow(naContext c,naRef me,int argc,naRef * args)66 static naRef f_pow(naContext c, naRef me, int argc, naRef* args)
67 {
68     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
69     naRef b = naNumValue(argc > 1 ? args[1] : naNil());
70     if(naIsNil(a) || naIsNil(b))
71         naRuntimeError(c, "non numeric argument to pow()");
72     a.num = pow(a.num, b.num);
73     return VALIDATE(a);
74 }
75 
f_atan2(naContext c,naRef me,int argc,naRef * args)76 static naRef f_atan2(naContext c, naRef me, int argc, naRef* args)
77 {
78     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
79     naRef b = naNumValue(argc > 1 ? args[1] : naNil());
80     if(naIsNil(a) || naIsNil(b))
81         naRuntimeError(c, "non numeric argument to atan2()");
82     a.num = atan2(a.num, b.num);
83     return VALIDATE(a);
84 }
85 
f_floor(naContext c,naRef me,int argc,naRef * args)86 static naRef f_floor(naContext c, naRef me, int argc, naRef* args)
87 {
88     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
89     if(naIsNil(a))
90         naRuntimeError(c, "non numeric argument to floor()");
91     a.num = floor(a.num);
92     return VALIDATE(a);
93 }
94 
f_ceil(naContext c,naRef me,int argc,naRef * args)95 static naRef f_ceil(naContext c, naRef me, int argc, naRef* args)
96 {
97     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
98     if(naIsNil(a))
99         naRuntimeError(c, "non numeric argument to ceil()");
100     a.num = ceil(a.num);
101     return VALIDATE(a);
102 }
103 
f_fmod(naContext c,naRef me,int argc,naRef * args)104 static naRef f_fmod(naContext c, naRef me, int argc, naRef* args)
105 {
106     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
107     naRef b = naNumValue(argc > 1 ? args[1] : naNil());
108     if(naIsNil(a) || naIsNil(b))
109         naRuntimeError(c, "non numeric arguments to fmod()");
110 
111     a.num = fmod(a.num, b.num);
112     return VALIDATE(a);
113 }
114 
f_clamp(naContext c,naRef me,int argc,naRef * args)115 static naRef f_clamp(naContext c, naRef me, int argc, naRef* args)
116 {
117     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
118     naRef min = naNumValue(argc > 1 ? args[1] : naNil());
119     naRef max = naNumValue(argc > 2 ? args[2] : naNil());
120 
121     if(naIsNil(a) || naIsNil(min) || naIsNil(max))
122         naRuntimeError(c, "non numeric arguments to clamp()");
123 
124     a.num = a.num < min.num ? min.num : ( a.num > max.num ? max.num : a.num );
125     return VALIDATE(a);
126 }
127 
f_periodic(naContext c,naRef me,int argc,naRef * args)128 static naRef f_periodic(naContext c, naRef me, int argc, naRef* args)
129 {
130     double range;
131     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
132     naRef b = naNumValue(argc > 1 ? args[1] : naNil());
133     naRef x = naNumValue(argc > 2 ? args[2] : naNil());
134 
135     if(naIsNil(a) || naIsNil(b) || naIsNil(x))
136         naRuntimeError(c, "non numeric arguments to periodic()");
137 
138     range = b.num - a.num;
139     x.num = x.num - range*floor((x.num - a.num)/range);
140     // two security checks that can only happen due to roundoff
141     if (x.num <= a.num)
142         x.num = a.num;
143     if (b.num <= x.num)
144         x.num = b.num;
145     return VALIDATE(x);
146 
147 //    x.num = SGMiscd::normalizePeriodic(a, b, x);
148     return VALIDATE(x);
149 }
150 
f_round(naContext c,naRef me,int argc,naRef * args)151 static naRef f_round(naContext c, naRef me, int argc, naRef* args)
152 {
153     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
154     naRef b = naNumValue(argc > 1 ? args[1] : naNil());
155 #ifdef _MSC_VER
156     double x,y;
157 #endif
158     if(naIsNil(a))
159         naRuntimeError(c, "non numeric arguments to round()");
160     if (naIsNil(b))
161         b.num = 1.0;
162 
163 #ifdef _MSC_VER // MSVC is not C99-compatible, no round() in math.h
164     y = a.num / b.num;
165     x = floor(y + 0.5);
166 #else
167     double x = round(a.num / b.num);
168 #endif
169     a.num = x * b.num;
170 
171     return VALIDATE(a);
172 }
173 
174 
f_tan(naContext c,naRef me,int argc,naRef * args)175 static naRef f_tan(naContext c, naRef me, int argc, naRef* args)
176 {
177     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
178     if(naIsNil(a))
179         naRuntimeError(c, "non numeric arguments to tan()");
180 
181    a.num = tan(a.num);
182    return VALIDATE(a);
183 }
184 
f_atan(naContext c,naRef me,int argc,naRef * args)185 static naRef f_atan(naContext c, naRef me, int argc, naRef* args)
186 {
187     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
188     if(naIsNil(a))
189         naRuntimeError(c, "non numeric arguments to tan()");
190 
191    a.num = atan(a.num);
192    return VALIDATE(a);
193 }
194 
f_asin(naContext c,naRef me,int argc,naRef * args)195 static naRef f_asin(naContext c, naRef me, int argc, naRef* args)
196 {
197     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
198     if(naIsNil(a))
199         naRuntimeError(c, "non numeric argument to asin()");
200     a.num = asin(a.num);
201     return VALIDATE(a);
202 }
203 
f_acos(naContext c,naRef me,int argc,naRef * args)204 static naRef f_acos(naContext c, naRef me, int argc, naRef* args)
205 {
206     naRef a = naNumValue(argc > 0 ? args[0] : naNil());
207     if(naIsNil(a))
208         naRuntimeError(c, "non numeric argument to acos()");
209     a.num = acos(a.num);
210     return VALIDATE(a);
211 }
212 
213 static naCFuncItem funcs[] = {
214     { "sin", f_sin },
215     { "cos", f_cos },
216     { "exp", f_exp },
217     { "ln", f_ln },
218     { "pow", f_pow },
219     { "sqrt", f_sqrt },
220     { "atan2", f_atan2 },
221     { "atan", f_atan },
222     { "floor", f_floor },
223     { "ceil", f_ceil },
224     { "fmod", f_fmod },
225     { "clamp", f_clamp },
226     { "periodic", f_periodic },
227     { "round", f_round },
228     { "tan", f_tan },
229     { "acos", f_acos },
230     { "asin", f_asin },
231     { 0 }
232 };
233 
234 
235 
naInit_math(naContext c)236 naRef naInit_math(naContext c)
237 {
238     naRef ns = naGenLib(c, funcs);
239     naAddSym(c, ns, "pi", naNum(3.14159265358979323846));
240     naAddSym(c, ns, "e", naNum(2.7182818284590452354));
241     return ns;
242 }
243