omdl  v0.6.1
OpenSCAD Mechanical Design Library
datatypes_operate_iterable.scad
Go to the documentation of this file.
1 //! Iterable data type operations.
2 /***************************************************************************//**
3  \file datatypes_operate_iterable.scad
4  \author Roy Allen Sutton
5  \date 2015-2017
6 
7  \copyright
8 
9  This file is part of [omdl] (https://github.com/royasutton/omdl),
10  an OpenSCAD mechanical design library.
11 
12  The \em omdl is free software; you can redistribute it and/or modify
13  it under the terms of the [GNU Lesser General Public License]
14  (http://www.gnu.org/licenses/lgpl.html) as published by the Free
15  Software Foundation; either version 2.1 of the License, or (at
16  your option) any later version.
17 
18  The \em omdl is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  Lesser General Public License for more details.
22 
23  You should have received a copy of the GNU Lesser General Public
24  License along with the \em omdl; if not, write to the Free Software
25  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26  02110-1301, USA; or see <http://www.gnu.org/licenses/>.
27 
28 *******************************************************************************/
29 
30 //----------------------------------------------------------------------------//
31 /***************************************************************************//**
32  \page tv_datatypes_operate_iterable Iterables
33  \li \subpage tv_datatypes_operate_iterable_s
34  \li \subpage tv_datatypes_operate_iterable_r
35 
36  \page tv_datatypes_operate_iterable_s Script
37  \dontinclude datatypes_operate_iterable_validate.scad
38  \skip include
39  \until end-of-tests
40  \page tv_datatypes_operate_iterable_r Results
41  \include datatypes_operate_iterable_validate.log
42 *******************************************************************************/
43 //----------------------------------------------------------------------------//
44 
45 //----------------------------------------------------------------------------//
46 /***************************************************************************//**
47  \addtogroup datatypes_operate
48  @{
49 
50  \defgroup datatypes_operate_iterable Iterables
51  \brief Iterable data type operations.
52 
53  \details
54 
55  See validation \ref tv_datatypes_operate_iterable_r "results".
56  @{
57 *******************************************************************************/
58 //----------------------------------------------------------------------------//
59 
60 //! Return an iterable element when it exists or a default value when it does not.
61 /***************************************************************************//**
62  \param v <value> An iterable value.
63  \param i <integer> An element index.
64  \param d <value> A default value.
65 
66  \returns <value> <tt>v[i]</tt> when it is defined or \p d otherwise.
67 *******************************************************************************/
68 function edefined_or
69 (
70  v,
71  i,
72  d
73 ) = (len(v) > i) ? v[i] : d;
74 
75 //! Find the occurrences of a match value in an iterable value.
76 /***************************************************************************//**
77  \param mv <value> A match value.
78  \param v <value> An iterable value.
79  \param c <integer> A match count.
80  For <tt>(c>=1)</tt>, return the first \p c matches.
81  For <tt>(c<=0)</tt>, return all matches.
82  \param i <integer> The element index to consider for iterable elements.
83  \param i1 <integer> The element index where find begins (default: first).
84  \param i2 <integer> The element index where find ends (default: last).
85 
86  \returns <list> A list of indexes where elements match \p mv.
87  Returns \b empty_lst when no element of \p v matches \p mv
88  or when \p v is not iterable.
89 
90  \details
91 
92  The use-cases for find() and [search()] are summarized in the
93  following tables.
94 
95  \b Find:
96 
97  | mv / v | string | list of scalars | list of iterables |
98  |---------------------|:------:|:-----------------:|:-------------------:|
99  | scalar | | (a) | (b) see note 1 |
100  | string | (c) | | (b) see note 1 |
101  | list of scalars | | | (b) see note 1 |
102  | list of iterables | | | (b) see note 1 |
103 
104  \b Search:
105 
106  | mv / v | string | list of scalars | list of iterables |
107  |---------------------|:------:|:-----------------:|:-------------------:|
108  | scalar | | (a) | (b) |
109  | string | (d) | invalid | (e) see note 2 |
110  | list of scalars | | (f) | (g) |
111  | list of iterables | | | (g) |
112 
113  \b Key:
114 
115  \li (a) Identify each element of \p v that equals \p mv.
116  \li (b) Identify each element of \p v where \p mv equals the element at
117  the specified column index, \p i, of each iterable value in \p v.
118  \li (c) If, and only if, \p mv is a single character, identify each
119  character in \p v that equals \p mv.
120  \li (d) For each character of \p mv, identify where it exists in \p v.
121  \b empty_lst is returned for each character of \p mv absent from \p v.
122  \li (e) For each character of \p mv, identify where it exists in \p v
123  either as a numeric value or as a character at the specified column
124  index, \p i.
125  \b empty_lst is returned for each character of \p mv absent from \p v.
126  \li (f) For each scalar of \p mv, identify where it exists in \p v.
127  \b empty_lst is returned for each scalar of \p mv absent from \p v.
128  \li (g) For each element of \p mv, identify where it equals the element
129  at the specified column index, \p i, of each iterable value in \p v.
130  \b empty_lst is returned for each element of \p mv absent from \p v
131  in the specified column index.
132 
133  \note \b 1: When \p i is specified, that element column is compared.
134  Otherwise, the entire element is compared. Functions find()
135  and [search()] behave differently in this regard.
136  \note \b 2: Invalid use combination when any element of \p v is a
137  string. However, an element that is a list of one or more
138  strings is valid. In which case, only the first character of
139  each string element is considered.
140 
141  [search()]: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Search
142 *******************************************************************************/
143 function find
144 (
145  mv,
146  v,
147  c = 1,
148  i,
149  i1 = 0,
150  i2
151 ) = !is_iterable(v) ? empty_lst
152  : (i1 > i2) ? empty_lst
153  : (i1 > len(v)-1) ? empty_lst
154  : ((not_defined(i) && (v[i1] == mv)) || (v[i1][i] == mv)) ?
155  (
156  (c == 1) ? [i1]
157  : concat(i1, find(mv, v, c-1, i, i1+1, i2))
158  )
159  : find(mv, v, c, i, i1+1, i2);
160 
161 //! Count all occurrences of a match value in an iterable value.
162 /***************************************************************************//**
163  \param mv <value> A match value.
164  \param v <value> An iterable value.
165  \param s <boolean> Use [search] for element matching
166  (assign \b false to use find()).
167  \param i <integer> The element index to consider for iterable elements.
168 
169  \returns <integer> The number of times \p mv occurs in the list.
170 
171  [search]: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Search
172 *******************************************************************************/
173 function count
174 (
175  mv,
176  v,
177  s = true,
178  i
179 ) = (s == false) ?
180  len(find(mv, v, 0, i))
181  : len(smerge(search(mv, v, 0, i)));
182 
183 //! Check for the existence of a match value in an iterable value.
184 /***************************************************************************//**
185  \param mv <value> A match value.
186  \param v <value> An iterable value.
187  \param s <boolean> Use [search] for element matching
188  (assign \b false to use find()).
189  \param i <integer> The element index to consider for iterable elements.
190 
191  \returns <boolean> \b true when \p mv exists in the list and
192  \b false otherwise.
193 
194  [search]: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Search
195 *******************************************************************************/
196 function exists
197 (
198  mv,
199  v,
200  s = true,
201  i
202 ) = (s == false) ?
203  (find(mv, v, 1, i) != empty_lst)
204  : (strip(search(mv, v, 1, i)) != empty_lst);
205 
206 //! Return the first element of an iterable value.
207 /***************************************************************************//**
208  \param v <value> An iterable value.
209 
210  \returns <value> The first element of \p v.
211  Returns \b undef when \p v is not defined, is not iterable,
212  or is empty.
213 
214  \details
215 
216  \note Value may also be a range.
217 *******************************************************************************/
218 function first( v ) = v[0];
219 
220 //! Return the second element of an iterable value.
221 /***************************************************************************//**
222  \param v <value> An iterable value.
223 
224  \returns <value> The second element of \p v.
225  Returns \b undef when \p v is not defined, is not iterable,
226  or is empty.
227 
228  \details
229 
230  \note Value may also be a range.
231 *******************************************************************************/
232 function second( v ) = v[1];
233 
234 //! Return the third element of an iterable value.
235 /***************************************************************************//**
236  \param v <value> An iterable value.
237 
238  \returns <value> The second element of \p v.
239  Returns \b undef when \p v is not defined, is not iterable,
240  or is empty.
241 
242  \details
243 
244  \note Value may also be a range.
245 *******************************************************************************/
246 function third( v ) = v[2];
247 
248 //! Return the last element of an iterable value.
249 /***************************************************************************//**
250  \param v <value> An iterable value.
251 
252  \returns <value> The last element of \p v.
253  Returns \b undef when \p v is not defined, is not iterable,
254  or is empty.
255 *******************************************************************************/
256 function last( v ) = v[len(v)-1];
257 
258 //! Return a list containing the first n elements of an iterable value.
259 /***************************************************************************//**
260  \param v <value> An iterable value.
261  \param n <integer> The element count.
262 
263  \returns <list> A list containing the first \p n elements of \p v.
264  Returns \b undef when \p v is not defined, is not iterable,
265  or is empty.
266 *******************************************************************************/
267 function nfirst
268 (
269  v,
270  n = 1
271 ) = not_defined(v) ? undef
272  : !is_iterable(v) ? undef
273  : is_empty(v) ? undef
274  : n < 1 ? empty_lst
275  : [for (i = [0 : min(n-1, len(v)-1)]) v[i]];
276 
277 //! Return a list containing the last n elements of an iterable value.
278 /***************************************************************************//**
279  \param v <value> An iterable value.
280  \param n <integer> The element count.
281 
282  \returns <list> A list containing the last \p n elements of \p v.
283  Returns \b undef when \p v is not defined, is not iterable,
284  or is empty.
285 *******************************************************************************/
286 function nlast
287 (
288  v,
289  n = 1
290 ) = not_defined(v) ? undef
291  : !is_iterable(v) ? undef
292  : is_empty(v) ? undef
293  : n < 1 ? empty_lst
294  : [for (i = [max(0, len(v)-n) : len(v)-1]) v[i]];
295 
296 //! Return a list containing all but the last n elements of an iterable value.
297 /***************************************************************************//**
298  \param v <value> An iterable value.
299  \param n <integer> The element count.
300 
301  \returns <list> A list containing all but the last \p n elements of \p v.
302  Returns \b empty_lst when \p v contains fewer than \p n elements.
303  Returns \b undef when \p v is not defined, is not iterable,
304  or is empty.
305 *******************************************************************************/
306 function nhead
307 (
308  v,
309  n = 1
310 ) = not_defined(v) ? undef
311  : !is_iterable(v) ? undef
312  : is_empty(v) ? undef
313  : (n >= len(v)) ? empty_lst
314  : [for (i = [0 : min(len(v)-1, len(v)-1-n)]) v[i]];
315 
316 //! Return a list containing all but the first n elements of an iterable value.
317 /***************************************************************************//**
318  \param v <value> An iterable value.
319  \param n <integer> The element count.
320 
321  \returns <list> A list containing all but the first n elements of \p v.
322  Returns \b empty_lst when \p v contains fewer than \p n elements.
323  Returns \b undef when \p v is not defined, is not iterable,
324  or is empty.
325 *******************************************************************************/
326 function ntail
327 (
328  v,
329  n = 1
330 ) = not_defined(v) ? undef
331  : !is_iterable(v) ? undef
332  : is_empty(v) ? undef
333  : (n >= len(v)) ? empty_lst
334  : [for (i = [max(0, n) : len(v)-1]) v[i]];
335 
336 //! Reverse the elements of an iterable value.
337 /***************************************************************************//**
338  \param v <value> An iterable value.
339 
340  \returns <list> A list containing the elements of \p v in reversed order.
341  Returns \b empty_lst when \p v is empty.
342  Returns \b undef when \p v is not defined or is not iterable.
343 *******************************************************************************/
344 function reverse
345 (
346  v
347 ) = not_defined(v) ? undef
348  : !is_iterable(v) ? undef
349  : is_empty(v) ? empty_lst
350  : [for (i = [len(v)-1 : -1 : 0]) v[i]];
351 
352 //! Select a range of elements from an iterable value.
353 /***************************************************************************//**
354  \param v <value> An iterable value.
355  \param i <range|list|integer> The index selection.
356 
357  \returns <list> A list containing the identified element(s).
358  Returns \b undef when \p i does not map to an element of \p v.
359  Returns \b empty_lst when \p v is empty.
360  Returns \b undef when \p v is not defined or is not iterable.
361 *******************************************************************************/
362 function rselect
363 (
364  v,
365  i
366 ) = !all_defined([v, i]) ? undef
367  : !is_iterable(v) ? undef
368  : is_empty(v) ? empty_lst
369  : is_number(i) && ((i<0) || (i>(len(v)-1))) ? undef
370  : is_list(i) && ((min([for (y=i) y])<0) || (max([for (y=i) y])>(len(v)-1))) ? undef
371  : is_range(i) && ((min([for (y=i) y])<0) || (max([for (y=i) y])>(len(v)-1))) ? undef
372  : let
373  (
374  s = is_number(i) ? [i] : i
375  )
376  [for (j = [for (k=s) k]) v[j]];
377 
378 //! Return a list of all n-element sequential-subsets of an iterable value.
379 /***************************************************************************//**
380  \param v <value> An iterable value.
381  \param n <integer> The number of elements for each subset.
382  \param s <integer> The iteration step size.
383  \param w <boolean> Use wrap-at-end circular subset selection.
384 
385  \returns <list-list> A list of all n-element sequential-subsets of
386  \p v skipping \p s elements between each subset selection.
387  Returns \b empty_lst when \p v is empty, is not defined or is
388  not iterable.
389 
390  \details
391 
392  \b Example
393  \code{.C}
394  v = [1, 2, 3, 4];
395 
396  nssequence( v, 3, 1, false ); // [ [1,2,3], [2,3,4] ]
397  nssequence( v, 3, 1, true ); // [ [1,2,3], [2,3,4], [3,4,1], [4,1,2] ]
398  \endcode
399 *******************************************************************************/
400 function nssequence
401 (
402  v,
403  n = 1,
404  s = 1,
405  w = false
406 ) =
407 [
408  for (i=[0 : s : (len(v)-((w == true) ? 1 : n)) ])
409  [
410  for (j=[i : (i+n-1)])
411  v[j % len(v)]
412  ]
413 ];
414 
415 //! Append a value to each element of an iterable value.
416 /***************************************************************************//**
417  \param nv <value> A new value to append.
418  \param v <value> An iterable value.
419 
420  \param r <boolean> Reduce list element value before appending.
421  \param j <boolean> Join each appendage as a separate list.
422 
423  \param l <boolean> Append new value to last element.
424 
425  \returns <list> A list with \p nv appended to each element of \p v.
426  Returns \b undef when \p v is not defined or is not iterable.
427 
428  \details
429 
430  \b Example
431  \code{.C}
432  v1=[["a"], ["b"], ["c"], ["d"]];
433  v2=[1, 2, 3];
434 
435  echo( eappend( v2, v1 ) );
436  echo( eappend( v2, v1, r=false ) );
437  echo( eappend( v2, v1, j=false, l=false ) );
438  \endcode
439 
440  \b Result
441  \code{.C}
442  ECHO: [["a", 1, 2, 3], ["b", 1, 2, 3], ["c", 1, 2, 3], ["d", 1, 2, 3]]
443  ECHO: [[["a"], 1, 2, 3], [["b"], 1, 2, 3], [["c"], 1, 2, 3], [["d"], 1, 2, 3]]
444  ECHO: ["a", 1, 2, 3, "b", 1, 2, 3, "c", 1, 2, 3, "d"]
445  \endcode
446 
447  \note Appending with reduction causes \p nv to be appended to the
448  \e elements of each iterable value. Otherwise, \p nv is
449  appended to the iterable value itself.
450 *******************************************************************************/
451 function eappend
452 (
453  nv,
454  v,
455  r = true,
456  j = true,
457  l = true
458 ) = not_defined(v) ? undef
459  : !is_iterable(v) ? undef
460  : is_empty(v) ? ((j == true) ? [concat(nv)] : concat(nv))
461  : let
462  (
463  ce = (r == true) ? first(v) : nfirst(v)
464  )
465  (len(v) == 1) ?
466  (
467  (j == true) ? (l == true) ? [concat(ce, nv)] : [ce]
468  : (l == true) ? concat(ce, nv) : ce
469  )
470  : (j == true) ? concat([concat(ce, nv)], eappend(nv, ntail(v), r, j, l))
471  : concat(concat(ce, nv), eappend(nv, ntail(v), r, j, l));
472 
473 //! Insert a new value into an iterable value.
474 /***************************************************************************//**
475  \param nv <value> A new value to insert.
476  \param v <value> An iterable value.
477 
478  \param i <integer> An insert position index.
479 
480  \param mv <list|string|value> Match value candidates.
481  \param mi <integer> A match index.
482 
483  \param s <boolean> Use [search] for element matching
484  (assign \b false to use find()).
485  \param si <integer> The element column index when matching.
486 
487  \returns <list> A list with \p nv inserted into \p v at the
488  specified position.
489  Returns \b undef when no value of \p mv exists in \p v.
490  Returns \b undef when <tt>(mi + 1)</tt> exceeds the matched
491  element count.
492  Returns \b undef when \p i does not map to an element of \p v.
493  Returns \b undef when \p v is not defined or is not iterable.
494 
495  \details
496 
497  The insert position can be specified by an index, an element match
498  value, or list of potential match values (when using [search]). When
499  multiple matches exists, \p mi indicates the insert position. When
500  more than one insert position criteria is specified, the order of
501  precedence is: \p mv, \p i.
502 
503  [search]: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Search
504 *******************************************************************************/
505 function insert
506 (
507  nv,
508  v,
509  i = 0,
510  mv,
511  mi = 0,
512  s = true,
513  si
514 ) = not_defined(v) ? undef
515  : !is_iterable(v) ? undef
516  : is_empty(v) ?
517  (
518  ( is_defined(mv) && any_equal(mv, empty_lst)) ||
519  (not_defined(mv) && (i == 0))
520  ) ? concat(nv) : undef
521  : ((i<0) || (i>len(v))) ? undef
522  : let
523  (
524  p = is_defined(mv) ?
525  (
526  (s == false) ?
527  find(mv, v, 0, si)[mi]
528  : smerge(search(mv, v, 0, si), false)[mi]
529  )
530  : is_number(i) ? i
531  : undef,
532  h = (p>0) ? [for (j = [0 : p-1]) v[j]] : empty_lst,
533  t = (p>len(v)-1) ? empty_lst : [for (j = [p : len(v)-1]) v[j]]
534  )
535  all_equal([h, t], empty_lst) ? undef : concat(h, nv, t);
536 
537 //! Delete elements from an iterable value.
538 /***************************************************************************//**
539  \param v <value> An iterable value.
540 
541  \param i <range|list|integer> Deletion Indexes.
542 
543  \param mv <list|string|value> Match value candidates.
544  \param mc <integer> A match count.
545  For <tt>(mc>=1)</tt>, remove the first \p mc matches.
546  For <tt>(mc<=0)</tt>, remove all matches.
547 
548  \param s <boolean> Use [search] for element matching
549  (assign \b false to use find()).
550  \param si <integer> The element column index when matching.
551 
552  \returns <list> A list with all specified elements removed.
553  Returns \b undef when \p i does not map to an element of \p v.
554  Returns \b undef when \p v is not defined or is not iterable.
555 
556  \details
557 
558  The elements to delete can be specified by an index position, a
559  list of index positions, an index range, an element match value, or
560  a list of element match values (when using [search]). When \p mv is
561  a list of match values, all values of \p mv that exists in \p v are
562  candidates for deletion. For each matching candidate, \p mc
563  indicates the quantity to remove. When more than one deletion
564  criteria is specified, the order of precedence is: \p mv, \p i.
565 
566  [search]: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Other_Language_Features#Search
567 *******************************************************************************/
568 function delete
569 (
570  v,
571  i,
572  mv,
573  mc = 1,
574  s = true,
575  si
576 ) = not_defined(v) ? undef
577  : !is_iterable(v) ? undef
578  : is_empty(v) ? empty_lst
579  : is_number(i) && ((i<0) || (i>(len(v)-1))) ? undef
580  : is_list(i) && ((min([for (y=i) y])<0) || (max([for (y=i) y])>(len(v)-1))) ? undef
581  : is_range(i) && ((min([for (y=i) y])<0) || (max([for (y=i) y])>(len(v)-1))) ? undef
582  : let
583  (
584  p = is_defined(mv) ?
585  (
586  (s == false) ?
587  find(mv, v, mc, si)
588  : smerge(search(mv, v, mc, si), false)
589  )
590  : is_number(i) ? [i]
591  : is_list(i) ? i
592  : is_range(i) ? [for (y=i) y]
593  : undef
594  )
595  [
596  for (j = [0 : len(v)-1])
597  if (is_empty(find(j, p))) v[j]
598  ];
599 
600 //! Strip all matching values from an iterable value.
601 /***************************************************************************//**
602  \param v <value> An iterable value.
603  \param mv <value> The match value.
604 
605  \returns <list> A list with all elements equal to \p mv removed.
606  Returns \b undef when \p v is not defined or is not iterable.
607 *******************************************************************************/
608 function strip
609 (
610  v,
611  mv = empty_lst
612 ) = not_defined(v) ? undef
613  : !is_iterable(v) ? undef
614  : is_empty(v) ? empty_lst
615  : (first(v) == mv) ? concat(strip(ntail(v), mv))
616  : concat(nfirst(v), strip(ntail(v), mv));
617 
618 //! Return the unique elements of an iterable value.
619 /***************************************************************************//**
620  \param v <value> An iterable value.
621 
622  \returns <list> A list of unique elements with order preserved.
623  Returns \b undef when \p v is not defined or is not iterable.
624 *******************************************************************************/
625 function unique
626 (
627  v
628 ) = not_defined(v) ? undef
629  : !is_iterable(v) ? undef
630  : is_empty(v) ? empty_lst
631  : (len(v) < 1) ? v
632  // use exact element matching via s=false for find().
633  : exists(last(v), nhead(v), s=false) ? unique(nhead(v))
634  : concat(unique(nhead(v)), nlast(v));
635 
636 //! @}
637 //! @}
638 
639 //----------------------------------------------------------------------------//
640 // openscad-amu auxiliary scripts
641 //----------------------------------------------------------------------------//
642 
643 /*
644 BEGIN_SCOPE validate;
645  BEGIN_OPENSCAD;
646  include <console.scad>;
647  include <datatypes/datatypes-base.scad>;
648  include <datatypes/datatypes_table.scad>;
649  include <validation.scad>;
650 
651  show_passing = true; // show passing tests
652  show_skipped = true; // show skipped tests
653 
654  echo( str("OpenSCAD Version ", version()) );
655 
656  // test-values columns
657  test_c =
658  [
659  ["id", "identifier"],
660  ["td", "description"],
661  ["tv", "test value"]
662  ];
663 
664  // test-values rows
665  test_r =
666  [
667  ["t01", "The undefined value", undef],
668  ["t02", "The empty list", empty_lst],
669  ["t03", "A range", [0:0.5:9]],
670  ["t04", "A string", "A string"],
671  ["t05", "Test list 01", ["orange","apple","grape","banana"]],
672  ["t06", "Test list 02", ["b","a","n","a","n","a","s"]],
673  ["t07", "Test list 03", [undef]],
674  ["t08", "Test list 04", [[1,2],[2,3]]],
675  ["t09", "Test list 05", ["ab",[1,2],[2,3],[4,5]]],
676  ["t10", "Test list 06", [[1,2,3],[4,5,6],[7,8,9],["a","b","c"]]],
677  ["t11", "Vector of integers 0 to 15", [for (i=[0:15]) i]]
678  ];
679 
680  test_ids = get_table_ridl( test_r );
681 
682  // expected columns: ("id" + one column for each test)
683  good_c = pmerge([concat("id", test_ids), concat("identifier", test_ids)]);
684 
685  // expected rows: ("golden" test results), use 's' to skip test
686  skip = -1; // skip test
687 
688  good_r =
689  [ // function
690  ["edefined_or_DE3",
691  "default", // t01
692  "default", // t02
693  "default", // t03
694  "t", // t04
695  "banana", // t05
696  "a", // t06
697  "default", // t07
698  "default", // t08
699  [4,5], // t09
700  ["a","b","c"], // t10
701  3 // t11
702  ],
703  ["find_12",
704  empty_lst, // t01
705  empty_lst, // t02
706  empty_lst, // t03
707  empty_lst, // t04
708  empty_lst, // t05
709  empty_lst, // t06
710  empty_lst, // t07
711  [0], // t08
712  [1], // t09
713  empty_lst, // t10
714  empty_lst // t11
715  ],
716  ["count_S1",
717  0, // t01
718  0, // t02
719  0, // t03
720  0, // t04
721  0, // t05
722  0, // t06
723  0, // t07
724  1, // t08
725  1, // t09
726  1, // t10
727  1 // t11
728  ],
729  ["exists_S1",
730  false, // t01
731  false, // t02
732  false, // t03
733  false, // t04
734  false, // t05
735  false, // t06
736  false, // t07
737  true, // t08
738  true, // t09
739  true, // t10
740  true // t11
741  ],
742  ["first",
743  undef, // t01
744  undef, // t02
745  0, // t03
746  "A", // t04
747  "orange", // t05
748  "b", // t06
749  undef, // t07
750  [1,2], // t08
751  "ab", // t09
752  [1,2,3], // t10
753  0 // t11
754  ],
755  ["second",
756  undef, // t01
757  undef, // t02
758  0.5, // t03
759  " ", // t04
760  "apple", // t05
761  "a", // t06
762  undef, // t07
763  [2,3], // t08
764  [1,2], // t09
765  [4,5,6], // t10
766  1 // t11
767  ],
768  ["third",
769  undef, // t01
770  undef, // t02
771  9, // t03
772  "s", // t04
773  "grape", // t05
774  "n", // t06
775  undef, // t07
776  undef, // t08
777  [2,3], // t09
778  [7,8,9], // t10
779  2 // t11
780  ],
781  ["last",
782  undef, // t01
783  undef, // t02
784  undef, // t03
785  "g", // t04
786  "banana", // t05
787  "s", // t06
788  undef, // t07
789  [2,3], // t08
790  [4,5], // t09
791  ["a","b","c"], // t10
792  15 // t11
793  ],
794  ["nfirst_1",
795  undef, // t01
796  undef, // t02
797  undef, // t03
798  ["A"], // t04
799  ["orange"], // t05
800  ["b"], // t06
801  [undef], // t07
802  [[1,2]], // t08
803  ["ab"], // t09
804  [[1,2,3]], // t10
805  [0] // t11
806  ],
807  ["nlast_1",
808  undef, // t01
809  undef, // t02
810  undef, // t03
811  ["g"], // t04
812  ["banana"], // t05
813  ["s"], // t06
814  [undef], // t07
815  [[2,3]], // t08
816  [[4,5]], // t09
817  [["a","b","c"]], // t10
818  [15] // t11
819  ],
820  ["nhead_1",
821  undef, // t01
822  undef, // t02
823  undef, // t03
824  ["A"," ","s","t","r","i","n"], // t04
825  ["orange","apple","grape"], // t05
826  ["b","a","n","a","n","a"], // t06
827  empty_lst, // t07
828  [[1,2]], // t08
829  ["ab",[1,2],[2,3]], // t09
830  [[1,2,3],[4,5,6],[7,8,9]], // t10
831  [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14] // t11
832  ],
833  ["ntail_1",
834  undef, // t01
835  undef, // t02
836  undef, // t03
837  [" ","s","t","r","i","n","g"], // t04
838  ["apple","grape","banana"], // t05
839  ["a","n","a","n","a","s"], // t06
840  empty_lst, // t07
841  [[2,3]], // t08
842  [[1,2],[2,3],[4,5]], // t09
843  [[4,5,6],[7,8,9],["a","b","c"]], // t10
844  [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] // t11
845  ],
846  ["reverse",
847  undef, // t01
848  empty_lst, // t02
849  undef, // t03
850  ["g","n","i","r","t","s"," ","A"], // t04
851  ["banana","grape","apple","orange"], // t05
852  ["s","a","n","a","n","a","b"], // t06
853  [undef], // t07
854  [[2,3],[1,2]], // t08
855  [[4,5],[2,3],[1,2],"ab"], // t09
856  [["a","b","c"],[7,8,9],[4,5,6],[1,2,3]], // t10
857  [15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0] // t11
858  ],
859  ["rselect_02",
860  undef, // t01
861  empty_lst, // t02
862  undef, // t03
863  ["A"," ","s"], // t04
864  ["orange","apple","grape"], // t05
865  ["b","a","n"], // t06
866  undef, // t07
867  undef, // t08
868  ["ab",[1,2],[2,3]], // t09
869  [[1,2,3],[4,5,6],[7,8,9]], // t10
870  [0,1,2] // t11
871  ],
872  ["nssequence_31",
873  empty_lst, // t01
874  empty_lst, // t02
875  empty_lst, // t03
876  [
877  ["A"," ","s"],[" ","s","t"],["s","t","r"],
878  ["t","r","i"],["r","i","n"],["i","n","g"]
879  ], // t04
880  [
881  ["orange","apple","grape"],
882  ["apple","grape","banana"]
883  ], // t05
884  [
885  ["b","a","n"],["a","n","a"],["n","a","n"],
886  ["a","n","a"],["n","a","s"]
887  ], // t06
888  empty_lst, // t07
889  empty_lst, // t08
890  [["ab",[1,2],[2,3]],[[1,2],[2,3],[4,5]]], // t09
891  [
892  [[1,2,3],[4,5,6],[7,8,9]],
893  [[4,5,6],[7,8,9],["a","b","c"]]
894  ], // t10
895  [
896  [0,1,2],[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7],
897  [6,7,8],[7,8,9],[8,9,10],[9,10,11],[10,11,12],
898  [11,12,13],[12,13,14],[13,14,15]
899  ] // t11
900  ],
901  ["eappend_T0",
902  undef, // t01
903  [[0]], // t02
904  undef, // t03
905  [
906  ["A",0],[" ",0],["s",0],["t",0],
907  ["r",0],["i",0],["n",0],["g",0]
908  ], // t04
909  [
910  ["orange",0],["apple",0],
911  ["grape",0],["banana",0]
912  ], // t05
913  [
914  ["b",0],["a",0],["n",0],["a",0],
915  ["n",0],["a",0],["s",0]
916  ], // t06
917  [[undef,0]], // t07
918  [[1,2,0],[2,3,0]], // t08
919  [["ab",0],[1,2,0],[2,3,0],[4,5,0]], // t09
920  [[1,2,3,0],[4,5,6,0],[7,8,9,0],["a","b","c",0]], // t10
921  [
922  [0,0],[1,0],[2,0],[3,0],[4,0],[5,0],
923  [6,0],[7,0],[8,0],[9,0],[10,0],[11,0],
924  [12,0],[13,0],[14,0],[15,0]
925  ] // t11
926  ],
927  ["insert_T0",
928  undef, // t01
929  undef, // t02
930  undef, // t03
931  undef, // t04
932  ["orange",0,"apple","grape","banana"], // t05
933  ["b","a","n","a","n","a",0,"s"], // t06
934  undef, // t07
935  [[1,2],0,[2,3]], // t08
936  ["ab",[1,2],0,[2,3],[4,5]], // t09
937  undef, // t10
938  [0,1,2,3,4,0,5,6,7,8,9,10,11,12,13,14,15] // t11
939  ],
940  ["delete_T0",
941  undef, // t01
942  empty_lst, // t02
943  undef, // t03
944  ["A"," ","s","t","r","i","n","g"], // t04
945  ["orange","grape","banana"], // t05
946  ["b","a","n","a","n","a"], // t06
947  [undef], // t07
948  [[1,2]], // t08
949  ["ab",[1,2],[4,5]], // t09
950  [[1,2,3],[4,5,6],[7,8,9],["a","b","c"]], // t10
951  [0,1,2,3,4,6,7,8,9,10,11,12,13,14,15] // t11
952  ],
953  ["strip",
954  undef, // t01
955  empty_lst, // t02
956  undef, // t03
957  ["A"," ","s","t","r","i","n","g"], // t04
958  ["orange","apple","grape","banana"], // t05
959  ["b","a","n","a","n","a","s"], // t06
960  [undef], // t07
961  [[1,2],[2,3]], // t08
962  ["ab",[1,2],[2,3],[4,5]], // t09
963  [[1,2,3],[4,5,6],[7,8,9],["a","b","c"]], // t10
964  [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] // t11
965  ],
966  ["unique",
967  undef, // t01
968  empty_lst, // t02
969  undef, // t03
970  ["A"," ","s","t","r","i","n","g"], // t04
971  ["orange","apple","grape","banana"], // t05
972  ["b","a","n","s"], // t06
973  [undef], // t07
974  [[1,2],[2,3]], // t08
975  ["ab",[1,2],[2,3],[4,5]], // t09
976  [[1,2,3],[4,5,6],[7,8,9],["a","b","c"]], // t10
977  [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] // t11
978  ]
979  ];
980 
981  // sanity-test tables
982  table_check( test_r, test_c, false );
983  table_check( good_r, good_c, false );
984 
985  // validate helper function and module
986  function get_value( vid ) = get_table_v(test_r, test_c, vid, "tv");
987  module run_test( fname, fresult, vid )
988  {
989  value_text = get_table_v(test_r, test_c, vid, "td");
990  pass_value = get_table_v(good_r, good_c, fname, vid);
991 
992  test_pass = validate( cv=fresult, t="equals", ev=pass_value, pf=true );
993  test_text = validate( str(fname, "(", get_value(vid), ")=", pass_value), fresult, "equals", pass_value );
994 
995  if ( pass_value != skip )
996  {
997  if ( !test_pass )
998  log_warn( str(vid, "(", value_text, ") ", test_text) );
999  else if ( show_passing )
1000  log_info( str(vid, " ", test_text) );
1001  }
1002  else if ( show_skipped )
1003  log_info( str(vid, " *skip*: '", fname, "(", value_text, ")'") );
1004  }
1005 
1006  // Indirect function calls would be very useful here!!!
1007  for (vid=test_ids) run_test( "edefined_or_DE3", edefined_or(get_value(vid),3,"default"), vid );
1008  for (vid=test_ids) run_test( "find_12", find([1,2],get_value(vid)), vid );
1009  for (vid=test_ids) run_test( "count_S1", count(1,get_value(vid),true), vid );
1010  for (vid=test_ids) run_test( "exists_S1", exists(1,get_value(vid),true), vid );
1011  for (vid=test_ids) run_test( "first", first(get_value(vid)), vid );
1012  for (vid=test_ids) run_test( "second", second(get_value(vid)), vid );
1013  for (vid=test_ids) run_test( "third", third(get_value(vid)), vid );
1014  for (vid=test_ids) run_test( "last", last(get_value(vid)), vid );
1015  for (vid=test_ids) run_test( "nfirst_1", nfirst(get_value(vid),n=1), vid );
1016  for (vid=test_ids) run_test( "nlast_1", nlast(get_value(vid),n=1), vid );
1017  for (vid=test_ids) run_test( "nhead_1", nhead(get_value(vid),n=1), vid );
1018  for (vid=test_ids) run_test( "ntail_1", ntail(get_value(vid),n=1), vid );
1019  for (vid=test_ids) run_test( "reverse", reverse(get_value(vid)), vid );
1020  for (vid=test_ids) run_test( "rselect_02", rselect(get_value(vid),i=[0:2]), vid );
1021  for (vid=test_ids) run_test( "nssequence_31", nssequence(get_value(vid),n=3,s=1), vid );
1022  for (vid=test_ids) run_test( "eappend_T0", eappend(0,get_value(vid)), vid );
1023  for (vid=test_ids) run_test( "insert_T0", insert(0,get_value(vid),mv=["x","r","apple","s",[2,3],5]), vid );
1024  for (vid=test_ids) run_test( "delete_T0", delete(get_value(vid),mv=["x","r","apple","s",[2,3],5]), vid );
1025  for (vid=test_ids) run_test( "strip", strip(get_value(vid)), vid );
1026  for (vid=test_ids) run_test( "unique", unique(get_value(vid)), vid );
1027 
1028  // end-of-tests
1029  END_OPENSCAD;
1030 
1031  BEGIN_MFSCRIPT;
1032  include --path "${INCLUDE_PATH}" {config_base,config_csg}.mfs;
1033  include --path "${INCLUDE_PATH}" script_std.mfs;
1034  END_MFSCRIPT;
1035 END_SCOPE;
1036 */
1037 
1038 //----------------------------------------------------------------------------//
1039 // end of file
1040 //----------------------------------------------------------------------------//
function count(mv, v, s=true, i)
Count all occurrences of a match value in an iterable value.
function nssequence(v, n=1, s=1, w=false)
Return a list of all n-element sequential-subsets of an iterable value.
function is_empty(v)
Test if an iterable value is empty.
function nfirst(v, n=1)
Return a list containing the first n elements of an iterable value.
function second(v)
Return the second element of an iterable value.
function unique(v)
Return the unique elements of an iterable value.
function all_defined(v)
Test if no element of a list of values is undefined.
function edefined_or(v, i, d)
Return an iterable element when it exists or a default value when it does not.
function all_equal(v, cv)
Test if a list of values equal a comparison value.
function is_number(v)
Test if a value is a number.
function smerge(v, r=false)
Serial-merge lists of iterable values.
empty_lst
A list with no values (the empty list).
Definition: constants.scad:80
function not_defined(v)
Test if a value is not defined.
function exists(mv, v, s=true, i)
Check for the existence of a match value in an iterable value.
function nlast(v, n=1)
Return a list containing the last n elements of an iterable value.
function insert(nv, v, i=0, mv, mi=0, s=true, si)
Insert a new value into an iterable value.
function rselect(v, i)
Select a range of elements from an iterable value.
function strip(v, mv=empty_lst)
Strip all matching values from an iterable value.
function is_defined(v)
Test if a value is defined.
function is_range(v)
Test if a value is a range definition.
function is_list(v)
Test if a value is an iterable list of values.
function first(v)
Return the first element of an iterable value.
function third(v)
Return the third element of an iterable value.
function last(v)
Return the last element of an iterable value.
function nhead(v, n=1)
Return a list containing all but the last n elements of an iterable value.
function eappend(nv, v, r=true, j=true, l=true)
Append a value to each element of an iterable value.
function any_equal(v, cv)
Test if any element of a list of values equal a comparison value.
function reverse(v)
Reverse the elements of an iterable value.
function is_iterable(v)
Test if a value has multiple parts and is iterable.
function ntail(v, n=1)
Return a list containing all but the first n elements of an iterable value.
function find(mv, v, c=1, i, i1=0, i2)
Find the occurrences of a match value in an iterable value.