1 /*
2  *  OpenSCAD (www.openscad.org)
3  *  Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
4  *                          Marius Kintel <marius@kintel.net>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  As a special exception, you have permission to link this program
12  *  with the CGAL library and distribute executables, as long as you
13  *  follow the requirements of the GNU GPL in regard to all of the
14  *  software in the executable aside from CGAL.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  */
26 
27 #include "function.h"
28 #include "expression.h"
29 #include "evalcontext.h"
30 #include "builtin.h"
31 #include "printutils.h"
32 #include "stackcheck.h"
33 #include "exceptions.h"
34 #include "memory.h"
35 #include "UserModule.h"
36 #include "degree_trig.h"
37 
38 #include <cmath>
39 #include <sstream>
40 #include <ctime>
41 #include <limits>
42 #include <algorithm>
43 #include <random>
44 
45 #include"boost-utils.h"
46 /*Unicode support for string lengths and array accesses*/
47 #include <glib.h>
48 // hash double
49 #include "linalg.h"
50 
51 #if defined __WIN32__ || defined _MSC_VER
52 #include <process.h>
53 int process_id = _getpid();
54 #else
55 #include <sys/types.h>
56 #include <unistd.h>
57 int process_id = getpid();
58 #endif
59 
60 std::mt19937 deterministic_rng( std::time(nullptr) + process_id );
61 
print_argCnt_warning(const char * name,const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)62 static void print_argCnt_warning(const char *name, const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx){
63 	LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"%1$s() number of parameters does not match",name);
64 }
65 
print_argConvert_warning(const char * name,const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)66 static void print_argConvert_warning(const char *name, const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx){
67 	LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"%1$s() parameter could not be converted",name);
68 }
69 
builtin_abs(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)70 Value builtin_abs(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
71 {
72 	if (evalctx->numArgs() == 1) {
73 		Value v = evalctx->getArgValue(0);
74 		if (v.type() == Value::Type::NUMBER){
75 			return Value(std::fabs(v.toDouble()));
76 		} else {
77 			print_argConvert_warning("abs", ctx, evalctx);
78 		}
79 	} else {
80 		print_argCnt_warning("abs", ctx, evalctx);
81 	}
82 
83 	return Value::undefined.clone();
84 }
85 
builtin_sign(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)86 Value builtin_sign(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
87 {
88 	if (evalctx->numArgs() == 1) {
89 		Value v = evalctx->getArgValue(0);
90 		if (v.type() == Value::Type::NUMBER) {
91 			double x = v.toDouble();
92 			return Value((x<0) ? -1.0 : ((x>0) ? 1.0 : 0.0));
93 		} else {
94 			print_argConvert_warning("sign", ctx, evalctx);
95 		}
96 	} else {
97 		print_argCnt_warning("sign", ctx, evalctx);
98 	}
99 	return Value::undefined.clone();
100 }
101 
builtin_rands(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)102 Value builtin_rands(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
103 {
104 	size_t n = evalctx->numArgs();
105 	if (n == 3 || n == 4) {
106 		Value v0 = evalctx->getArgValue(0);
107 		if (v0.type() != Value::Type::NUMBER) goto quit;
108 		double min = v0.toDouble();
109 
110 		if (std::isinf(min) || std::isnan(min)){
111 			LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"rands() range min cannot be infinite");
112 			min = -std::numeric_limits<double>::max()/2;
113 			LOG(message_group::Warning,Location::NONE,"","resetting to %1f",min);
114 		}
115 		Value v1 = evalctx->getArgValue(1);
116 		if (v1.type() != Value::Type::NUMBER) goto quit;
117 		double max = v1.toDouble();
118 		if (std::isinf(max)  || std::isnan(max)) {
119 			LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"rands() range max cannot be infinite");
120 			max = std::numeric_limits<double>::max()/2;
121 			LOG(message_group::Warning,Location::NONE,"","resetting to %1f",max);
122 		}
123 		if (max < min) {
124 			double tmp = min; min = max; max = tmp;
125 		}
126 		Value v2 = evalctx->getArgValue(2);
127 		if (v2.type() != Value::Type::NUMBER) goto quit;
128 		double numresultsd = std::abs( v2.toDouble() );
129 		if (std::isinf(numresultsd)  || std::isnan(numresultsd)) {
130 			LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"rands() cannot create an infinite number of results");
131 			LOG(message_group::Warning,Location::NONE,"","resetting number of results to 1");
132 			numresultsd = 1;
133 		}
134 		size_t numresults = boost_numeric_cast<size_t,double>( numresultsd );
135 
136 		if (n > 3) {
137 			Value v3 = evalctx->getArgValue(3);
138 			if (v3.type() != Value::Type::NUMBER) goto quit;
139 			uint32_t seed = static_cast<uint32_t>(hash_floating_point( v3.toDouble() ));
140 			deterministic_rng.seed( seed );
141 		}
142 		VectorType vec;
143 		if (min>=max) { // uniform_real_distribution doesn't allow min == max
144 			for (size_t i=0; i < numresults; ++i)
145 				vec.emplace_back(min);
146 		} else {
147 			std::uniform_real_distribution<> distributor( min, max );
148 			for (size_t i=0; i < numresults; ++i) {
149 				vec.emplace_back(distributor(deterministic_rng));
150 			}
151 		}
152 		return std::move(vec);
153 	} else {
154 		print_argCnt_warning("rands", ctx, evalctx);
155 	}
156 quit:
157 	return Value::undefined.clone();
158 }
159 
builtin_min(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)160 Value builtin_min(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
161 {
162 	// preserve special handling of the first argument
163 	// as a template for vector processing
164 	size_t n = evalctx->numArgs();
165 	if (n >= 1) {
166 		Value v0 = evalctx->getArgValue(0);
167 
168 		if (n == 1 && v0.type() == Value::Type::VECTOR) {
169 			const auto &vec = v0.toVector();
170 			if (!vec.empty()) {
171 				return std::min_element(vec.begin(), vec.end(), Value::cmp_less)->clone();
172 			}
173 		}
174 		if (v0.type() == Value::Type::NUMBER) {
175 			double val = v0.toDouble();
176 			for (size_t i = 1; i < n; ++i) {
177 				Value v = evalctx->getArgValue(i);
178 				// 4/20/14 semantic change per discussion:
179 				// break on any non-number
180 				if (v.type() != Value::Type::NUMBER) goto quit;
181 				double x = v.toDouble();
182 				if (x < val) val = x;
183 			}
184 			return Value(val);
185 		}
186 	} else {
187 		print_argCnt_warning("min", ctx, evalctx);
188 		return Value::undefined.clone();
189 	}
190 quit:
191 	print_argConvert_warning("min", ctx, evalctx);
192 	return Value::undefined.clone();
193 }
194 
builtin_max(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)195 Value builtin_max(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
196 {
197 	// preserve special handling of the first argument
198 	// as a template for vector processing
199 	size_t n = evalctx->numArgs();
200 	if (n >= 1) {
201 		Value v0 = evalctx->getArgValue(0);
202 
203 		if (n == 1 && v0.type() == Value::Type::VECTOR) {
204 			const auto &vec = v0.toVector();
205 			if (!vec.empty()) {
206 				return std::max_element(vec.begin(), vec.end(), Value::cmp_less)->clone();
207 			}
208 		}
209 		if (v0.type() == Value::Type::NUMBER) {
210 			double val = v0.toDouble();
211 			for (size_t i = 1; i < n; ++i) {
212 				Value v = evalctx->getArgValue(i);
213 				// 4/20/14 semantic change per discussion:
214 				// break on any non-number
215 				if (v.type() != Value::Type::NUMBER) goto quit;
216 				double x = v.toDouble();
217 				if (x > val) val = x;
218 			}
219 			return Value(val);
220 		}
221 	} else {
222 		print_argCnt_warning("max", ctx, evalctx);
223 		return Value::undefined.clone();
224 	}
225 quit:
226 	print_argConvert_warning("max", ctx, evalctx);
227 	return Value::undefined.clone();
228 }
229 
builtin_sin(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)230 Value builtin_sin(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
231 {
232 	if (evalctx->numArgs() == 1) {
233 		Value v = evalctx->getArgValue(0);
234 		if (v.type() == Value::Type::NUMBER){
235 			return Value(sin_degrees(v.toDouble()));
236 		} else {
237 			print_argConvert_warning("sin", ctx, evalctx);
238 		}
239 	} else {
240 		print_argCnt_warning("sin", ctx, evalctx);
241 	}
242 	return Value::undefined.clone();
243 }
244 
builtin_cos(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)245 Value builtin_cos(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
246 {
247 	if (evalctx->numArgs() == 1) {
248 		Value v = evalctx->getArgValue(0);
249 		if (v.type() == Value::Type::NUMBER){
250 			return Value(cos_degrees(v.toDouble()));
251 		} else {
252 			print_argConvert_warning("cos", ctx, evalctx);
253 		}
254 	} else {
255 		print_argCnt_warning("cos", ctx, evalctx);
256 	}
257 	return Value::undefined.clone();
258 }
259 
builtin_asin(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)260 Value builtin_asin(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
261 {
262 	if (evalctx->numArgs() == 1) {
263 		Value v = evalctx->getArgValue(0);
264 		if (v.type() == Value::Type::NUMBER){
265 			return Value(asin_degrees(v.toDouble()));
266 		} else {
267 			print_argConvert_warning("asin", ctx, evalctx);
268 		}
269 	} else {
270 		print_argCnt_warning("asin", ctx, evalctx);
271 	}
272 	return Value::undefined.clone();
273 }
274 
builtin_acos(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)275 Value builtin_acos(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
276 {
277 	if (evalctx->numArgs() == 1) {
278 		Value v = evalctx->getArgValue(0);
279 		if (v.type() == Value::Type::NUMBER){
280 			return Value(acos_degrees(v.toDouble()));
281 		} else {
282 			print_argConvert_warning("acos", ctx, evalctx);
283 		}
284 	} else {
285 		print_argCnt_warning("acos", ctx, evalctx);
286 	}
287 	return Value::undefined.clone();
288 }
289 
builtin_tan(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)290 Value builtin_tan(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
291 {
292 	if (evalctx->numArgs() == 1) {
293 		Value v = evalctx->getArgValue(0);
294 		if (v.type() == Value::Type::NUMBER){
295 			return Value(tan_degrees(v.toDouble()));
296 		} else {
297 			print_argConvert_warning("tan", ctx, evalctx);
298 		}
299 	} else {
300 		print_argCnt_warning("tan", ctx, evalctx);
301 	}
302 	return Value::undefined.clone();
303 }
304 
builtin_atan(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)305 Value builtin_atan(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
306 {
307 	if (evalctx->numArgs() == 1) {
308 		Value v = evalctx->getArgValue(0);
309 		if (v.type() == Value::Type::NUMBER){
310 			return Value(atan_degrees(v.toDouble()));
311 		} else {
312 			print_argConvert_warning("atan", ctx, evalctx);
313 		}
314 	} else {
315 		print_argCnt_warning("atan", ctx, evalctx);
316 	}
317 	return Value::undefined.clone();
318 }
319 
builtin_atan2(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)320 Value builtin_atan2(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
321 {
322 	if (evalctx->numArgs() == 2) {
323 		Value v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1);
324 		if (v0.type() == Value::Type::NUMBER && v1.type() == Value::Type::NUMBER){
325 			return Value(atan2_degrees(v0.toDouble(), v1.toDouble()));
326 		} else {
327 			print_argConvert_warning("atan2", ctx, evalctx);
328 		}
329 	} else {
330 		print_argCnt_warning("atan2", ctx, evalctx);
331 	}
332 	return Value::undefined.clone();
333 }
334 
builtin_pow(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)335 Value builtin_pow(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
336 {
337 	if (evalctx->numArgs() == 2) {
338 		Value v0 = evalctx->getArgValue(0), v1 = evalctx->getArgValue(1);
339 		if (v0.type() == Value::Type::NUMBER && v1.type() == Value::Type::NUMBER){
340 			return Value(pow(v0.toDouble(), v1.toDouble()));
341 		} else {
342 			print_argConvert_warning("pow", ctx, evalctx);
343 		}
344 	} else {
345 		print_argCnt_warning("pow", ctx, evalctx);
346 	}
347 	return Value::undefined.clone();
348 }
349 
builtin_round(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)350 Value builtin_round(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
351 {
352 	if (evalctx->numArgs() == 1) {
353 		Value v = evalctx->getArgValue(0);
354 		if (v.type() == Value::Type::NUMBER){
355 			return Value(round(v.toDouble()));
356 		} else {
357 			print_argConvert_warning("round", ctx, evalctx);
358 		}
359 	} else {
360 		print_argCnt_warning("round", ctx, evalctx);
361 	}
362 	return Value::undefined.clone();
363 }
364 
builtin_ceil(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)365 Value builtin_ceil(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
366 {
367 	if (evalctx->numArgs() == 1) {
368 		Value v = evalctx->getArgValue(0);
369 		if (v.type() == Value::Type::NUMBER){
370 			return Value(ceil(v.toDouble()));
371 		} else {
372 			print_argConvert_warning("ceil", ctx, evalctx);
373 		}
374 	} else {
375 		print_argCnt_warning("ceil", ctx, evalctx);
376 	}
377 	return Value::undefined.clone();
378 }
379 
builtin_floor(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)380 Value builtin_floor(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
381 {
382 	if (evalctx->numArgs() == 1) {
383 		Value v = evalctx->getArgValue(0);
384 		if (v.type() == Value::Type::NUMBER){
385 			return Value(floor(v.toDouble()));
386 		} else {
387 			print_argConvert_warning("floor", ctx, evalctx);
388 		}
389 	} else {
390 		print_argCnt_warning("floor", ctx, evalctx);
391 	}
392 	return Value::undefined.clone();
393 }
394 
builtin_sqrt(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)395 Value builtin_sqrt(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
396 {
397 	if (evalctx->numArgs() == 1) {
398 		Value v = evalctx->getArgValue(0);
399 		if (v.type() == Value::Type::NUMBER){
400 			return Value(sqrt(v.toDouble()));
401 		} else {
402 			print_argConvert_warning("sqrt", ctx, evalctx);
403 		}
404 	} else {
405 		print_argCnt_warning("sqrt", ctx, evalctx);
406 	}
407 	return Value::undefined.clone();
408 }
409 
builtin_exp(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)410 Value builtin_exp(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
411 {
412 	if (evalctx->numArgs() == 1) {
413 		Value v = evalctx->getArgValue(0);
414 		if (v.type() == Value::Type::NUMBER){
415 			return Value(exp(v.toDouble()));
416 		} else {
417 			print_argConvert_warning("exp", ctx, evalctx);
418 		}
419 	} else {
420 		print_argCnt_warning("exp", ctx, evalctx);
421 	}
422 	return Value::undefined.clone();
423 }
424 
builtin_length(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)425 Value builtin_length(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
426 {
427 	if (evalctx->numArgs() == 1) {
428 		Value v = evalctx->getArgValue(0);
429 		if (v.type() == Value::Type::VECTOR) return double(v.toVector().size());
430 		if (v.type() == Value::Type::STRING) {
431 			//Unicode glyph count for the length -- rather than the string (num. of bytes) length.
432 			return Value(double( v.toStrUtf8Wrapper().get_utf8_strlen()));
433 		}
434 		print_argConvert_warning("len", ctx, evalctx);
435 	} else {
436 		print_argCnt_warning("len", ctx, evalctx);
437 	}
438 	return Value::undefined.clone();
439 }
440 
builtin_log(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)441 Value builtin_log(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
442 {
443 	size_t n = evalctx->numArgs();
444 	if (n == 1 || n == 2) {
445 		Value v0 = evalctx->getArgValue(0);
446 		if (v0.type() == Value::Type::NUMBER) {
447 			double x = 10.0, y = v0.toDouble();
448 			if (n > 1) {
449 				Value v1 = evalctx->getArgValue(1);
450 				if (v1.type() != Value::Type::NUMBER) goto quit;
451 				x = y; y = v1.toDouble();
452 			}
453 			return Value(log(y) / log(x));
454 		}
455 	} else {
456 		print_argCnt_warning("log", ctx, evalctx);
457 		return Value::undefined.clone();
458 	}
459 quit:
460 	print_argConvert_warning("log", ctx, evalctx);
461 	return Value::undefined.clone();
462 }
463 
builtin_ln(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)464 Value builtin_ln(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
465 {
466 	if (evalctx->numArgs() == 1) {
467 		Value v = evalctx->getArgValue(0);
468 		if (v.type() == Value::Type::NUMBER){
469 			return Value(log(v.toDouble()));
470 		} else {
471 			print_argConvert_warning("ln", ctx, evalctx);
472 		}
473 	} else {
474 		print_argCnt_warning("ln", ctx, evalctx);
475 	}
476 	return Value::undefined.clone();
477 }
478 
builtin_str(const std::shared_ptr<Context>,const std::shared_ptr<EvalContext> evalctx)479 Value builtin_str(const std::shared_ptr<Context>, const std::shared_ptr<EvalContext> evalctx)
480 {
481 	std::ostringstream stream;
482 
483 	for (size_t i = 0; i < evalctx->numArgs(); ++i) {
484 		stream << evalctx->getArgValue(i).toString();
485 	}
486 	return Value(stream.str());
487 }
488 
builtin_chr(const std::shared_ptr<Context>,const std::shared_ptr<EvalContext> evalctx)489 Value builtin_chr(const std::shared_ptr<Context>, const std::shared_ptr<EvalContext> evalctx)
490 {
491 	std::ostringstream stream;
492 
493 	for (size_t i = 0; i < evalctx->numArgs(); ++i) {
494 		Value v = evalctx->getArgValue(i);
495 		stream << v.chrString();
496 	}
497 	return Value(stream.str());
498 }
499 
builtin_ord(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)500 Value builtin_ord(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
501 {
502 	const size_t numArgs = evalctx->numArgs();
503 
504 	if (numArgs == 0) {
505 		return Value::undefined.clone();
506 	} else if (numArgs > 1) {
507 		LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"ord() called with %1$d arguments, only 1 argument expected",numArgs);
508 		return Value::undefined.clone();
509 	}
510 
511 	const Value arg = evalctx->getArgValue(0);
512 	if (arg.type() != Value::Type::STRING) {
513 		LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"ord() argument %1$s is not of type string",arg.toEchoString());
514 		return Value::undefined.clone();
515 	}
516 
517 	const str_utf8_wrapper &arg_str = arg.toStrUtf8Wrapper();
518 	const char *ptr = arg_str.c_str();
519 	if (!g_utf8_validate(ptr, -1, NULL)) {
520 		LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"ord() argument '%1$s' is not a valid utf8 string",arg_str.toString());
521 		return Value::undefined.clone();
522 	}
523 
524 	if (arg_str.get_utf8_strlen() == 0) {
525 		return Value::undefined.clone();
526 	}
527 
528 	const gunichar ch = g_utf8_get_char(ptr);
529 	return Value((double)ch);
530 }
531 
builtin_concat(const std::shared_ptr<Context>,const std::shared_ptr<EvalContext> evalctx)532 Value builtin_concat(const std::shared_ptr<Context>, const std::shared_ptr<EvalContext> evalctx)
533 {
534 	VectorType result;
535 
536 	for (size_t i = 0; i < evalctx->numArgs(); ++i) {
537 		Value val = evalctx->getArgValue(i);
538 		if (val.type() == Value::Type::VECTOR) {
539 			result.emplace_back(EmbeddedVectorType(std::move(val.toVectorNonConst())));
540 		} else {
541 			result.emplace_back(std::move(val));
542 		}
543 	}
544 	return std::move(result);
545 }
546 
builtin_lookup(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)547 Value builtin_lookup(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
548 {
549 	double p, low_p, low_v, high_p, high_v;
550 	if (evalctx->numArgs() != 2){ // Needs two args
551 		print_argCnt_warning("lookup", ctx, evalctx);
552 		return Value::undefined.clone();
553 	}
554 	if(!evalctx->getArgValue(0).getDouble(p) || !std::isfinite(p)){ // First arg must be a number
555 		LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"lookup(%1$s, ...) first argument is not a number",evalctx->getArgValue(0).toEchoString());
556 		return Value::undefined.clone();
557 	}
558 
559 	Value v1 = evalctx->getArgValue(1);
560 	const auto &vec = v1.toVector();
561 
562 	// Second must be a vector of vec2, with valid numbers inside
563 	auto it = vec.begin();
564 	if (vec.empty() || it->toVector().size() < 2 || !it->getVec2(low_p, low_v)) {
565 		return Value::undefined.clone();
566 	}
567 	high_p = low_p;
568 	high_v = low_v;
569 
570 	for (++it; it != vec.end(); ++it) {
571 		double this_p, this_v;
572 		if (it->getVec2(this_p, this_v)) {
573 			if (this_p <= p && (this_p > low_p || low_p > p)) {
574 				low_p = this_p;
575 				low_v = this_v;
576 			}
577 			if (this_p >= p && (this_p < high_p || high_p < p)) {
578 				high_p = this_p;
579 				high_v = this_v;
580 			}
581 		}
582 	}
583 	if (p <= low_p)
584 		return Value(high_v);
585 	if (p >= high_p)
586 		return Value(low_v);
587 	double f = (p-low_p) / (high_p-low_p);
588 	return Value(high_v * f + low_v * (1-f));
589 }
590 
591 /*
592  Pattern:
593 
594   "search" "(" ( match_value | list_of_match_values ) "," vector_of_vectors
595         ("," num_returns_per_match
596           ("," index_col_num )? )?
597         ")";
598   match_value : ( Value::Type::NUMBER | Value::Type::STRING );
599   list_of_values : "[" match_value ("," match_value)* "]";
600   vector_of_vectors : "[" ("[" Value ("," Value)* "]")+ "]";
601   num_returns_per_match : int;
602   index_col_num : int;
603 
604  The search string and searched strings can be unicode strings.
605  Examples:
606   Index values return as list:
607     search("a","abcdabcd");
608         - returns [0]
609     search("Л","Л");  //A unicode string
610         - returns [0]
611     search("��aЛ","a��Л��a��Л��a",0);
612         - returns [[1,3,5,7],[0,4,8],[2,6]]
613     search("a","abcdabcd",0); //Search up to all matches
614         - returns [[0,4]]
615     search("a","abcdabcd",1);
616         - returns [0]
617     search("e","abcdabcd",1);
618         - returns []
619     search("a",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ]);
620         - returns [0,4]
621 
622   Search on different column; return Index values:
623     search(3,[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",3] ], 0, 1);
624         - returns [0,8]
625 
626   Search on list of values:
627     Return all matches per search vector element:
628       search("abc",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 0);
629         - returns [[0,4],[1,5],[2,6]]
630 
631     Return first match per search vector element; special case return vector:
632       search("abc",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 1);
633         - returns [0,1,2]
634 
635     Return first two matches per search vector element; vector of vectors:
636       search("abce",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 2);
637         - returns [[0,4],[1,5],[2,6],[8]]
638 
639 */
640 
search(const str_utf8_wrapper & find,const str_utf8_wrapper & table,unsigned int num_returns_per_match,const Location &)641 static VectorType search(const str_utf8_wrapper &find, const str_utf8_wrapper &table,
642 																unsigned int num_returns_per_match,
643 																const Location &)
644 {
645 	VectorType returnvec;
646 	//Unicode glyph count for the length
647 	size_t findThisSize = find.get_utf8_strlen();
648 	size_t searchTableSize = table.get_utf8_strlen();
649 	for (size_t i = 0; i < findThisSize; ++i) {
650 		unsigned int matchCount = 0;
651 		VectorType resultvec;
652 		const gchar *ptr_ft = g_utf8_offset_to_pointer(find.c_str(), i);
653 		for (size_t j = 0; j < searchTableSize; ++j) {
654 			const gchar *ptr_st = g_utf8_offset_to_pointer(table.c_str(), j);
655 			if (ptr_ft && ptr_st && (g_utf8_get_char(ptr_ft) == g_utf8_get_char(ptr_st)) ) {
656 				matchCount++;
657 				if (num_returns_per_match == 1) {
658 					returnvec.emplace_back(double(j));
659 					break;
660 				} else {
661 					resultvec.emplace_back(double(j));
662 				}
663 				if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) {
664 					break;
665 				}
666 			}
667 		}
668 		if (matchCount == 0) {
669 			gchar utf8_of_cp[6] = ""; //A buffer for a single unicode character to be copied into
670 			if (ptr_ft) g_utf8_strncpy(utf8_of_cp, ptr_ft, 1);
671 		}
672 		if (num_returns_per_match == 0 || num_returns_per_match > 1) {
673 			returnvec.emplace_back(std::move(resultvec));
674 		}
675 	}
676 	return returnvec;
677 }
678 
search(const str_utf8_wrapper & find,const VectorType & table,unsigned int num_returns_per_match,unsigned int index_col_num,const Location & loc,const std::shared_ptr<Context> ctx)679 static VectorType search(const str_utf8_wrapper &find, const VectorType &table,
680 		unsigned int num_returns_per_match, unsigned int index_col_num,
681 		const Location &loc, const std::shared_ptr<Context> ctx)
682 {
683 	VectorType returnvec;
684 	//Unicode glyph count for the length
685 	unsigned int findThisSize =  find.get_utf8_strlen();
686 	unsigned int searchTableSize = table.size();
687 	for (size_t i = 0; i < findThisSize; ++i) {
688 		unsigned int matchCount = 0;
689 		VectorType resultvec;
690 		const gchar *ptr_ft = g_utf8_offset_to_pointer(find.c_str(), i);
691 		for (size_t j = 0; j < searchTableSize; ++j) {
692 			const auto &entryVec = table[j].toVector();
693 			if (entryVec.size() <= index_col_num) {
694 				LOG(message_group::Warning,loc,ctx->documentPath(),"Invalid entry in search vector at index %1$d, required number of values in the entry: %2$d. Invalid entry: %3$s",j,(index_col_num + 1),table[j].toEchoString());
695 				return VectorType();
696 			}
697 			const gchar *ptr_st = g_utf8_offset_to_pointer(entryVec[index_col_num].toString().c_str(), 0);
698 			if (ptr_ft && ptr_st && (g_utf8_get_char(ptr_ft) == g_utf8_get_char(ptr_st)) ) {
699 				matchCount++;
700 				if (num_returns_per_match == 1) {
701 					returnvec.emplace_back(double(j));
702 					break;
703 				} else {
704 					resultvec.emplace_back(double(j));
705 				}
706 				if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) {
707 					break;
708 				}
709 			}
710 		}
711 		if (matchCount == 0) {
712 			gchar utf8_of_cp[6] = ""; //A buffer for a single unicode character to be copied into
713 			if (ptr_ft) g_utf8_strncpy(utf8_of_cp, ptr_ft, 1);
714 			LOG(message_group::Warning,loc,ctx->documentPath(),"search term not found: \"%1$s\"",utf8_of_cp);
715 		}
716 		if (num_returns_per_match == 0 || num_returns_per_match > 1) {
717 			returnvec.emplace_back(std::move(resultvec));
718 		}
719 	}
720 	return returnvec;
721 }
722 
builtin_search(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)723 Value builtin_search(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
724 {
725 	if (evalctx->numArgs() < 2){
726 		print_argCnt_warning("search", ctx, evalctx);
727 		return Value::undefined.clone();
728 	}
729 
730 	Value findThis = evalctx->getArgValue(0);
731 	Value searchTable = evalctx->getArgValue(1);
732 	unsigned int num_returns_per_match = (evalctx->numArgs() > 2) ? (unsigned int)evalctx->getArgValue(2).toDouble() : 1;
733 	unsigned int index_col_num = (evalctx->numArgs() > 3) ? (unsigned int)evalctx->getArgValue(3).toDouble() : 0;
734 
735 	VectorType returnvec;
736 
737 	if (findThis.type() == Value::Type::NUMBER) {
738 		unsigned int matchCount = 0;
739 		size_t j = 0;
740 		for (const auto &search_element : searchTable.toVector()) {
741 			if ((index_col_num == 0 && (findThis == search_element).toBool()) ||
742 			    (index_col_num < search_element.toVector().size() &&
743 			     (findThis == search_element.toVector()[index_col_num]).toBool())) {
744 				returnvec.emplace_back(double(j));
745 				matchCount++;
746 				if (num_returns_per_match != 0 && matchCount >= num_returns_per_match) break;
747 			}
748 			++j;
749 		}
750 	} else if (findThis.type() == Value::Type::STRING) {
751 		if (searchTable.type() == Value::Type::STRING) {
752 			returnvec = search(findThis.toStrUtf8Wrapper(), searchTable.toStrUtf8Wrapper(), num_returns_per_match, evalctx->loc);
753 		}
754 		else {
755 			returnvec = search(findThis.toStrUtf8Wrapper(), searchTable.toVector(), num_returns_per_match, index_col_num, evalctx->loc, ctx);
756 		}
757 	} else if (findThis.type() == Value::Type::VECTOR) {
758 		const auto &findVec = findThis.toVector();
759 		for (size_t i = 0; i < findVec.size(); ++i) {
760 			unsigned int matchCount = 0;
761 			VectorType resultvec;
762 
763 			const auto &find_value = findVec[i];
764 			size_t j = 0;
765 			for (const auto &search_element : searchTable.toVector()) {
766 				if ((index_col_num == 0 && (find_value == search_element).toBool()) ||
767 				    (index_col_num < search_element.toVector().size() &&
768 				     (find_value   == search_element.toVector()[index_col_num]).toBool())) {
769 					matchCount++;
770 					if (num_returns_per_match == 1) {
771 						returnvec.emplace_back(double(j));
772 						break;
773 					} else {
774 						resultvec.emplace_back(double(j));
775 					}
776 					if (num_returns_per_match > 1 && matchCount >= num_returns_per_match) break;
777 				}
778 				++j;
779 			}
780 			if (num_returns_per_match == 1 && matchCount == 0) {
781 				returnvec.emplace_back(std::move(resultvec));
782 			}
783 			if (num_returns_per_match == 0 || num_returns_per_match > 1) {
784 				returnvec.emplace_back(std::move(resultvec));
785 			}
786 		}
787 	} else {
788 		return Value::undefined.clone();
789 	}
790 	return std::move(returnvec);
791 }
792 
793 #define QUOTE(x__) # x__
794 #define QUOTED(x__) QUOTE(x__)
795 
builtin_version(const std::shared_ptr<Context>,const std::shared_ptr<EvalContext>)796 Value builtin_version(const std::shared_ptr<Context>, const std::shared_ptr<EvalContext>)
797 {
798 	VectorType vec;
799 	vec.emplace_back(double(OPENSCAD_YEAR));
800 	vec.emplace_back(double(OPENSCAD_MONTH));
801 #ifdef OPENSCAD_DAY
802 	vec.emplace_back(double(OPENSCAD_DAY));
803 #endif
804 	return std::move(vec);
805 }
806 
builtin_version_num(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)807 Value builtin_version_num(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
808 {
809 	Value val = (evalctx->numArgs() == 0) ? builtin_version(ctx, evalctx) : evalctx->getArgValue(0);
810 	double y, m, d;
811 	if (!val.getVec3(y, m, d, 0)) {
812 		return Value::undefined.clone();
813 	}
814 	return Value(y * 10000 + m * 100 + d);
815 }
816 
builtin_parent_module(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)817 Value builtin_parent_module(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
818 {
819 	int n;
820 	double d;
821 	int s = UserModule::stack_size();
822 	if (evalctx->numArgs() == 0)
823 		d=1; // parent module
824 	else if (evalctx->numArgs() == 1) {
825 		Value v = evalctx->getArgValue(0);
826 		if (v.type() != Value::Type::NUMBER) return Value::undefined.clone();
827 		v.getDouble(d);
828 	} else {
829 		print_argCnt_warning("parent_module", ctx, evalctx);
830 		return Value::undefined.clone();
831 	}
832 	n=trunc(d);
833 	if (n < 0) {
834 		LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"Negative parent module index (%1$d) not allowed",n);
835 		return Value::undefined.clone();
836 	}
837 	if (n >= s) {
838 		LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"Parent module index (%1$d) greater than the number of modules on the stack",n);
839 		return Value::undefined.clone();
840 	}
841 	return Value(UserModule::stack_element(s - 1 - n));
842 }
843 
builtin_norm(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)844 Value builtin_norm(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
845 {
846 	if (evalctx->numArgs() == 1) {
847 		Value val = evalctx->getArgValue(0);
848 		if (val.type() == Value::Type::VECTOR) {
849 			double sum = 0;
850 			for (const auto &v : val.toVector()) {
851 				if (v.type() == Value::Type::NUMBER) {
852 					// sum += pow(v[i].toDouble(),2);
853 					double x = v.toDouble();
854 					sum += x*x;
855 				} else {
856 					LOG(message_group::Warning,evalctx->loc,ctx->documentPath(),"Incorrect arguments to norm()");
857 					return Value::undefined.clone();
858 				}
859 			}
860 			return Value(sqrt(sum));
861 		}
862 	} else {
863 		print_argCnt_warning("norm", ctx, evalctx);
864 	}
865 	return Value::undefined.clone();
866 }
867 
builtin_cross(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)868 Value builtin_cross(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
869 {
870 	auto loc = evalctx->loc;
871 	if (evalctx->numArgs() != 2) {
872 		LOG(message_group::Warning,loc,ctx->documentPath(),"Invalid number of parameters for cross()");
873 		return Value::undefined.clone();
874 	}
875 
876 	Value arg0 = evalctx->getArgValue(0);
877 	Value arg1 = evalctx->getArgValue(1);
878 	if ((arg0.type() != Value::Type::VECTOR) || (arg1.type() != Value::Type::VECTOR)) {
879 		LOG(message_group::Warning,loc,ctx->documentPath(),"Invalid type of parameters for cross()");
880 		return Value::undefined.clone();
881 	}
882 
883 	const auto &v0 = arg0.toVector();
884 	const auto &v1 = arg1.toVector();
885 	if ((v0.size() == 2) && (v1.size() == 2)) {
886 		return Value(v0[0].toDouble() * v1[1].toDouble() - v0[1].toDouble() * v1[0].toDouble());
887 	}
888 
889 	if ((v0.size() != 3) || (v1.size() != 3)) {
890 		LOG(message_group::Warning,loc,ctx->documentPath(),"Invalid vector size of parameter for cross()");
891 		return Value::undefined.clone();
892 	}
893 	for (unsigned int a = 0;a < 3; ++a) {
894 		if ((v0[a].type() != Value::Type::NUMBER) || (v1[a].type() != Value::Type::NUMBER)) {
895 			LOG(message_group::Warning,loc,ctx->documentPath(),"Invalid value in parameter vector for cross()");
896 			return Value::undefined.clone();
897 		}
898 		double d0 = v0[a].toDouble();
899 		double d1 = v1[a].toDouble();
900 		if (std::isnan(d0) || std::isnan(d1)) {
901 			LOG(message_group::Warning,loc,ctx->documentPath(),"Invalid value (NaN) in parameter vector for cross()");
902 			return Value::undefined.clone();
903 		}
904 		if (std::isinf(d0) || std::isinf(d1)) {
905 			LOG(message_group::Warning,loc,ctx->documentPath(),"Invalid value (INF) in parameter vector for cross()");
906 			return Value::undefined.clone();
907 		}
908 	}
909 
910 	double x = v0[1].toDouble() * v1[2].toDouble() - v0[2].toDouble() * v1[1].toDouble();
911 	double y = v0[2].toDouble() * v1[0].toDouble() - v0[0].toDouble() * v1[2].toDouble();
912 	double z = v0[0].toDouble() * v1[1].toDouble() - v0[1].toDouble() * v1[0].toDouble();
913 
914 	return VectorType(x,y,z);
915 }
916 
builtin_is_undef(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)917 Value builtin_is_undef(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
918 {
919 	if (evalctx->numArgs() == 1) {
920 		const auto &arg =evalctx->getArgs()[0];
921 		if (auto lookup = dynamic_pointer_cast<Lookup>(arg->getExpr())) {
922 			return lookup->evaluateSilently(evalctx).isUndefined();
923 		} else {
924 			return evalctx->getArgValue(0).isUndefined();
925 		}
926 	} else {
927 		print_argCnt_warning("is_undef", ctx, evalctx);
928 	}
929 	return Value::undefined.clone();
930 }
931 
builtin_is_list(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)932 Value builtin_is_list(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
933 {
934 	if (evalctx->numArgs() == 1) {
935 		return Value(evalctx->getArgValue(0).isDefinedAs(Value::Type::VECTOR));
936 	} else {
937 		print_argCnt_warning("is_list", ctx, evalctx);
938 	}
939 	return Value::undefined.clone();
940 }
941 
builtin_is_num(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)942 Value builtin_is_num(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
943 {
944 	if (evalctx->numArgs() == 1) {
945 		Value v = evalctx->getArgValue(0);
946 		return Value(v.isDefinedAs(Value::Type::NUMBER) && !std::isnan(v.toDouble()));
947 	} else {
948 		print_argCnt_warning("is_num", ctx, evalctx);
949 	}
950 	return Value::undefined.clone();
951 }
952 
builtin_is_bool(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)953 Value builtin_is_bool(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
954 {
955 	if (evalctx->numArgs() == 1) {
956 		return Value(evalctx->getArgValue(0).isDefinedAs(Value::Type::BOOL));
957 	} else {
958 		print_argCnt_warning("is_bool", ctx, evalctx);
959 	}
960 	return Value::undefined.clone();
961 }
962 
builtin_is_string(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)963 Value builtin_is_string(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
964 {
965 	if (evalctx->numArgs() == 1) {
966 		return Value(evalctx->getArgValue(0).isDefinedAs(Value::Type::STRING));
967 	} else {
968 		print_argCnt_warning("is_string", ctx, evalctx);
969 	}
970 	return Value::undefined.clone();
971 }
972 
builtin_is_function(const std::shared_ptr<Context> ctx,const std::shared_ptr<EvalContext> evalctx)973 Value builtin_is_function(const std::shared_ptr<Context> ctx, const std::shared_ptr<EvalContext> evalctx)
974 {
975 	if (evalctx->numArgs() == 1) {
976 		return Value(evalctx->getArgValue(0).isDefinedAs(Value::Type::FUNCTION));
977 	} else {
978 		print_argCnt_warning("is_function", ctx, evalctx);
979 	}
980 	return Value::undefined.clone();
981 }
982 
register_builtin_functions()983 void register_builtin_functions()
984 {
985 	Builtins::init("abs", new BuiltinFunction(&builtin_abs),
986 				{
987 					"abs(number) -> number",
988 				});
989 
990 	Builtins::init("sign", new BuiltinFunction(&builtin_sign),
991 				{
992 					"sign(number) -> -1, 0 or 1",
993 				});
994 
995 	Builtins::init("rands", new BuiltinFunction(&builtin_rands),
996 				{
997 					"rands(min, max, num_results) -> vector",
998 					"rands(min, max, num_results, seed) -> vector",
999 				});
1000 
1001 	Builtins::init("min", new BuiltinFunction(&builtin_min),
1002 				{
1003 					"min(number, number, ...) -> number",
1004 					"min(vector) -> number",
1005 				});
1006 
1007 	Builtins::init("max", new BuiltinFunction(&builtin_max),
1008 				{
1009 					"max(number, number, ...) -> number",
1010 					"max(vector) -> number",
1011 				});
1012 
1013 	Builtins::init("sin", new BuiltinFunction(&builtin_sin),
1014 				{
1015 					"sin(degrees) -> number",
1016 				});
1017 
1018 	Builtins::init("cos", new BuiltinFunction(&builtin_cos),
1019 				{
1020 					"cos(degrees) -> number",
1021 				});
1022 
1023 	Builtins::init("asin", new BuiltinFunction(&builtin_asin),
1024 				{
1025 					"asin(number) -> degrees",
1026 				});
1027 
1028 	Builtins::init("acos", new BuiltinFunction(&builtin_acos),
1029 				{
1030 					"acos(number) -> degrees",
1031 				});
1032 
1033 	Builtins::init("tan", new BuiltinFunction(&builtin_tan),
1034 				{
1035 					"tan(degrees) -> number",
1036 				});
1037 
1038 	Builtins::init("atan", new BuiltinFunction(&builtin_atan),
1039 				{
1040 					"atan(number) -> degrees",
1041 				});
1042 
1043 	Builtins::init("atan2", new BuiltinFunction(&builtin_atan2),
1044 				{
1045 					"atan2(number, number) -> degrees",
1046 				});
1047 
1048 	Builtins::init("round", new BuiltinFunction(&builtin_round),
1049 				{
1050 					"round(number) -> number",
1051 				});
1052 
1053 	Builtins::init("ceil", new BuiltinFunction(&builtin_ceil),
1054 				{
1055 					"ceil(number) -> number",
1056 				});
1057 
1058 	Builtins::init("floor", new BuiltinFunction(&builtin_floor),
1059 				{
1060 					"floor(number) -> number",
1061 				});
1062 
1063 	Builtins::init("pow", new BuiltinFunction(&builtin_pow),
1064 				{
1065 					"pow(base, exponent) -> number",
1066 				});
1067 
1068 	Builtins::init("sqrt", new BuiltinFunction(&builtin_sqrt),
1069 				{
1070 					"sqrt(number) -> number",
1071 				});
1072 
1073 	Builtins::init("exp", new BuiltinFunction(&builtin_exp),
1074 				{
1075 					"exp(number) -> number",
1076 				});
1077 
1078 	Builtins::init("len", new BuiltinFunction(&builtin_length),
1079 				{
1080 					"len(string) -> number",
1081 					"len(vector) -> number",
1082 				});
1083 
1084 	Builtins::init("log", new BuiltinFunction(&builtin_log),
1085 				{
1086 					"log(number) -> number",
1087 				});
1088 
1089 	Builtins::init("ln", new BuiltinFunction(&builtin_ln),
1090 				{
1091 					"ln(number) -> number",
1092 				});
1093 
1094 	Builtins::init("str", new BuiltinFunction(&builtin_str),
1095 				{
1096 					"str(number or string, ...) -> string",
1097 				});
1098 
1099 	Builtins::init("chr", new BuiltinFunction(&builtin_chr),
1100 				{
1101 					"chr(number) -> string",
1102 					"chr(vector) -> string",
1103 					"chr(range) -> string",
1104 				});
1105 
1106 	Builtins::init("ord", new BuiltinFunction(&builtin_ord),
1107 				{
1108 					"ord(string) -> number",
1109 				});
1110 
1111 	Builtins::init("concat", new BuiltinFunction(&builtin_concat),
1112 				{
1113 					"concat(number or string or vector, ...) -> vector",
1114 				});
1115 
1116 	Builtins::init("lookup", new BuiltinFunction(&builtin_lookup),
1117 				{
1118 					"lookup(key, <key,value> vector) -> value",
1119 				});
1120 
1121 	Builtins::init("search", new BuiltinFunction(&builtin_search),
1122 				{
1123 					"search(string , string or vector [, num_returns_per_match [, index_col_num ] ] ) -> vector",
1124 				});
1125 
1126 	Builtins::init("version", new BuiltinFunction(&builtin_version),
1127 				{
1128 					"version() -> vector",
1129 				});
1130 
1131 	Builtins::init("version_num", new BuiltinFunction(&builtin_version_num),
1132 				{
1133 					"version_num() -> number",
1134 				});
1135 
1136 	Builtins::init("norm", new BuiltinFunction(&builtin_norm),
1137 				{
1138 					"norm(vector) -> number",
1139 				});
1140 
1141 	Builtins::init("cross", new BuiltinFunction(&builtin_cross),
1142 				{
1143 					"cross(vector, vector) -> vector",
1144 				});
1145 
1146 	Builtins::init("parent_module", new BuiltinFunction(&builtin_parent_module),
1147 				{
1148 					"parent_module(number) -> string",
1149 				});
1150 
1151 	Builtins::init("is_undef", new BuiltinFunction(&builtin_is_undef),
1152 				{
1153 					"is_undef(arg) -> boolean",
1154 				});
1155 
1156 	Builtins::init("is_list", new BuiltinFunction(&builtin_is_list),
1157 				{
1158 					"is_list(arg) -> boolean",
1159 				});
1160 
1161 	Builtins::init("is_num", new BuiltinFunction(&builtin_is_num),
1162 				{
1163 					"is_num(arg) -> boolean",
1164 				});
1165 
1166 	Builtins::init("is_bool", new BuiltinFunction(&builtin_is_bool),
1167 				{
1168 					"is_bool(arg) -> boolean",
1169 				});
1170 
1171 	Builtins::init("is_string", new BuiltinFunction(&builtin_is_string),
1172 				{
1173 					"is_string(arg) -> boolean",
1174 				});
1175 
1176 	Builtins::init("is_function", new BuiltinFunction(&builtin_is_function),
1177 				{
1178 					"is_function(arg) -> boolean",
1179 				});
1180 }
1181