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