omdl  v1.0
OpenSCAD Mechanical Design Library
list_operate.scad
Go to the documentation of this file.
1 //! List data type operations.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2015-2026
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  \details
29 
30  \amu_define group_name (List Operations)
31  \amu_define group_brief (Operations for list data types.)
32 
33  \amu_include (include/amu/doxyg_init_pd_gds_ipg.amu)
34 *******************************************************************************/
35 
36 // auto-tests (add to test results page)
37 /***************************************************************************//**
38  \amu_include (include/amu/validate_log.amu)
39  \amu_include (include/amu/validate_results.amu)
40 *******************************************************************************/
41 
42 // group(s) begin (test summary and includes-required)
43 /***************************************************************************//**
44  \amu_include (include/amu/doxyg_define_in_parent_open.amu)
45  \amu_include (include/amu/validate_summary.amu)
46  \amu_include (include/amu/includes_required.amu)
47 *******************************************************************************/
48 
49 // member-wide reference definitions
50 /***************************************************************************//**
51  \amu_define group_references
52  (
53  )
54 *******************************************************************************/
55 
56 // member-wide documentation and conventions
57 /***************************************************************************//**
58  \addtogroup \amu_eval(${group})
59  \details
60  \anchor \amu_eval(${group})_conventions
61  \par Conventions
62 
63  - The primary list parameter is always \p v.
64  - \p l denotes a \b length or \b count (number of elements to take/skip).
65  - \p u denotes an \b upper bound (exclusive end index) only in limit().
66  This dual usage of \p l is a known inconsistency; see Parameter ยง3.
67  - sort_q() sorts by the natural OpenSCAD comparison of elements.
68  sort_q2() sorts by a sub-element index \p i, enabling sort of
69  structured (tuple) lists.
70  - sum() returns a scalar for a flat numeric list and performs
71  element-wise vector addition for a list of equal-length numeric lists.
72  - merge_s() flattens two lists into one; merge_p() interleaves them.
73 *******************************************************************************/
74 
75 //----------------------------------------------------------------------------//
76 // members
77 //----------------------------------------------------------------------------//
78 
79 //! Convert a list of values to a concatenated string.
80 /***************************************************************************//**
81  \param v <list> A list of values.
82 
83  \returns (1) <string> Constructed by converting each element of the
84  list to a string and concatenating them together.
85  (2) Returns \b undef when the list is not defined.
86 
87  \details
88 
89  \b Example
90  \code{.C}
91  v1=["a", "b", "c", "d"];
92  v2=[1, 2, 3];
93 
94  echo( strl(concat(v1, v2)) );
95  \endcode
96 
97  \b Result
98  \code{.C}
99  ECHO: "abcd123"
100  \endcode
101 *******************************************************************************/
102 function strl
103 (
104  v
105 ) = is_undef(v) ? undef
106  : !is_iterable(v) ? str(v)
107  : is_empty(v) ? empty_str
108  : (len(v) == 1) ? str(first(v))
109  : str(first(v), strl(tailn(v)));
110 
111 //! Convert a list of values to a concatenated HTML-formatted string.
112 /***************************************************************************//**
113  \param v <list> A list of values.
114 
115  \param b <tag-list-list> A list of tag lists.
116  \em Unpaired HTML [tag(s)] to add before the value.
117  \param p <tag-list-list> A list of tag lists.
118  \em Paired HTML [tag(s)] to enclose the value.
119  \param a <tag-list-list> A list of tag lists.
120  \em Unpaired HTML [tag(s)] to add after the value.
121 
122  \param f <attr-list-list> A list of tag attribute lists for \c fs,
123  where <tt>fs=["color","size","face"]</tt> is the font
124  tag to enclose the value. Not all attributes are
125  required, but the order is significant.
126 
127  \param d <boolean> Debug. When \b true angle brackets are replaced
128  with curly brackets to prevent console decoding.
129 
130  \returns (1) <string> Constructed by converting each element of the
131  list to a string with specified HTML markup and
132  concatenating.
133  (2) Returns \b empty_str when the list is empty or is not
134  defined.
135 
136  \details
137 
138  When there are fewer tag lists in \p b, \p p, \p a, or \p f, than
139  there are value elements in \p v, the last specified tag list is
140  used for all subsequent value elements.
141 
142  For a list of the \em paired and \em unpaired HTML tags supported
143  by the console see: [HTML subset].
144 
145  \b Example
146  \code{.C}
147  echo( strl_html(v="bold text", p="b", d=true) );
148  echo( strl_html(v=[1,"x",3], f=[["red",6,"helvetica"],undef,["blue",10,"courier"]], d=true) );
149 
150  v = ["result", "=", "mc", "2"];
151  b = ["hr", undef];
152  p = ["i", undef, ["b", "i"], ["b","sup"]];
153  a = concat(consts(3, u=true), "hr");
154  f = [undef, ["red"], undef, ["blue",4]];
155 
156  echo( strl_html(v=v, b=b, p=p, a=a, f=f, d=true) );
157  \endcode
158 
159  \b Result
160  \code{.C}
161  ECHO: "{b}bold text{/b}"
162  ECHO: "{font color="red" size="6" face="helvetica"}1{/font}x{font color="blue" size="10" face="courier"}3{/font}"
163  ECHO: "{hr}{i}result{/i}{font color="red"}={/font}{b}{i}mc{/i}{/b}{b}{sup}{font color="blue" size="4"}2{/font}{/sup}{/b}{hr}"
164  \endcode
165 
166  [tag(s)]: http://doc.qt.io/qt-5/richtext-html-subset.html
167  [HTML subset]: http://doc.qt.io/qt-5/richtext-html-subset.html
168 *******************************************************************************/
169 function strl_html
170 (
171  v, // value
172  b, // before
173  p, // pair
174  a, // after
175  f, // fs
176  d = false // debug
177 ) = is_undef(v) ? empty_str
178  : is_empty(v) ? empty_str
179  : let
180  ( // brackets or debug
181  bb = (d == true) ? "{" : "<",
182  ba = (d == true) ? "}" : ">",
183 
184  // current element is first
185  cv = is_list(v) ? firstn(v) : v,
186  cb = is_list(b) ? first(b) : b,
187  cp = is_list(p) ? first(p) : p,
188  ca = is_list(a) ? first(a) : a,
189  cf = is_list(f) ? first(f) : f,
190 
191  // close pair lists in reverse order
192  rp = is_list(cp) ? reverse(cp) : cp,
193 
194  // font attributes
195  f0 = exists_e(0,cf) ? str(" color=\"", cf[0], "\"") : empty_str,
196  f1 = exists_e(1,cf) ? str(" size=\"", cf[1], "\"") : empty_str,
197  f2 = exists_e(2,cf) ? str(" face=\"", cf[2], "\"") : empty_str,
198 
199  // before and after
200  fb = is_undef(cf) ? empty_str : str(bb, "font", f0, f1, f2, ba),
201  fa = is_undef(cf) ? empty_str : str(bb, "/font", ba),
202 
203  // current string
204  cs =
205  concat
206  (
207  [for (i=is_string(cb)?[cb]:cb) str(bb, i, ba)],
208  [for (i=is_string(cp)?[cp]:cp) str(bb, i, ba)],
209  fb, cv, fa,
210  [for (i=is_string(rp)?[rp]:rp) str(bb, "/", i, ba)],
211  [for (i=is_string(ca)?[ca]:ca) str(bb, i, ba)]
212  ),
213 
214  // next elements
215  nv = is_list(v) ? tailn(v) : empty_str,
216  nb = is_list(b) ? exists_e(1,b) ? tailn(b) : lastn(b) : b,
217  np = is_list(p) ? exists_e(1,p) ? tailn(p) : lastn(p) : p,
218  na = is_list(a) ? exists_e(1,a) ? tailn(a) : lastn(a) : a,
219  nf = is_list(f) ? exists_e(1,f) ? tailn(f) : lastn(f) : f
220  )
221  strl(concat(cs, strl_html(nv, nb, np, na, nf, d)));
222 
223 //! Create a list of constant or incrementing elements.
224 /***************************************************************************//**
225  \param l <integer> The list length.
226  \param v <value> The element value.
227  \param u <boolean> Element values are \b undef.
228 
229  \returns (1) <list> A list of \p l of constand or incrementing
230  sequential elements.
231  (2) Returns \b empty_lst when \p l is not a positive number.
232 
233  \details
234 
235  When \p v is unspecified and \p u is \b false, each element is
236  assigned the value of its index position.
237 *******************************************************************************/
238 function consts
239 (
240  l,
241  v,
242  u = false
243 ) = !is_number(l) ? empty_lst
244  : (l < 1) ? empty_lst
245  : !is_undef(v) ? [for (i=[0:1:l-1]) v]
246  : (u == false) ? [for (i=[0:1:l-1]) i]
247  : [for (i=[0:1:l-1]) undef];
248 
249 //! Selected element indices of a given list according to a selection scheme.
250 /***************************************************************************//**
251  \param l <list> The list.
252  \param s <index> The index selection.
253  \param rs <number> A random number sequence seed.
254 
255  \returns (1) <list> The selected element indices.
256  (2) Returns \b empty_lst when \p l is not a list or when
257  the selection does not exists.
258 
259  \details
260 
261  This function provides a way to select one or more elements of a
262  list using a convenient shorthand forms for common selection
263  patterns.
264 
265  An index selection may be expressed using any of the following
266  forms:
267 
268  | value / form | description |
269  |:---------------:|:----------------------------------------------|
270  | true | All index positions of the list [0:size-1] |
271  | false | No index positions |
272  | "all" | All index positions of the list [0:size-1] |
273  | "none" | No index positions |
274  | "rands" | Random index selection of the list [0:size-1] |
275  | "even" | The even index of the list [0:size-1] |
276  | "odd" | The odd index of the list [0:size-1] |
277  | integer | The single position given by an <integer> |
278  | range | The range of positions given by a <range> |
279  | integer-list | The list of positions give in <integer-list> |
280 
281  This function resolves an index specification into a concrete list
282  of index positions, converting shorthand or abstract selection
283  patterns into an explicit, iterable list.
284 
285  \b Example
286 
287  \code{.c}
288  // list
289  l1 = [a,b,c,d,e,f]
290 
291  // index sequence
292  index_sel(l1) = [0,1,2,3,4,5]
293  index_sel(l1, "rands") = [0,2,5]
294  \endcode
295 *******************************************************************************/
296 function index_sel
297 (
298  l,
299  s = true,
300  rs
301 ) = !is_list(l) ? empty_lst
302  : (s == true) ? consts(len(l))
303  : (s == false) ? empty_lst
304  : (s == "all") ? consts(len(l))
305  : (s == "none") ? empty_lst
306  : (s == "even") ? [for (i = [0:2:len(l)-1]) i]
307  : (s == "odd") ? [for (i = [1:2:len(l)-1]) i]
308  : (s == "rands") ?
309  let
310  (
311  r = defined_or(rs, first(rands(0, 100, 1))),
312  i = rands(0, 2, len(l), r)
313  )
314  [for (j = [0:len(i)-1]) if (i[j] > 1) j]
315  // any other string
316  : is_string(s) ? empty_lst
317  : is_number(s) ? [s]
318  : is_range(s) ? [for (i=s) i]
319  : all_numbers(s) ? s
320  : empty_lst;
321 
322 //! Round a list of numbers to a fixed number of decimal point digits.
323 /***************************************************************************//**
324  \param v <list> A list of values.
325  \param d <integer> The maximum number of decimals.
326 
327  \returns (1) <list> The list with all numeric values truncated to
328  \p d decimal digits and rounded-up if the following
329  digit is 5 or greater.
330  (2) Returns \b undef when \p v is a non-numeric value.
331 *******************************************************************************/
332 function round_d
333 (
334  v,
335  d = 6
336 ) = is_number(v) ?
337  let(n = pow(10, d))
338  round(v * n) / n
339  : is_list(v) ? [for (i=v) round_d(i, d)]
340  : undef;
341 
342 //! Round a list of numbers to a fixed number of significant figures.
343 /***************************************************************************//**
344  \param v <list> A list of values.
345  \param d <integer> The maximum number of significant figures.
346 
347  \returns (1) <list> The list with all numeric values rounded-up
348  to \p d significant figures.
349  (2) Returns \b undef when \p v is a non-numeric value.
350 
351  \details
352 
353  See [Wikipedia] for more information.
354 
355  [Wikipedia]: https://en.wikipedia.org/wiki/Significant_figures
356 *******************************************************************************/
357 function round_s
358 (
359  v,
360  d = 6
361 ) = (v == 0) ? 0
362  : is_number(v) ?
363  let(n = floor(log(abs(v))) + 1 - d)
364  round(v * pow(10, -n)) * pow(10, n)
365  : is_list(v) ? [for (i=v) round_s(i, d)]
366  : undef;
367 
368 //! Limit a list of numbers between an upper and lower bounds.
369 /***************************************************************************//**
370  \param v <list> A list of values.
371  \param l <number> The minimum value.
372  \param u <number> The maximum value.
373 
374  \returns (1) <list> The list with all numeric values limited to the
375  range <tt>[l : u]</tt>.
376  (2) Returns \b undef when \p v is a non-numeric value or
377  when \p l or \p u is undefined.
378 *******************************************************************************/
379 function limit
380 (
381  v,
382  l,
383  u
384 ) = any_undefined([l, u]) ? undef
385  : is_number(v) ? min(max(v,l),u)
386  : is_list(v) ? [for (i=v) limit(i,l,u)]
387  : undef;
388 
389 //! Compute the sum of a list of numbers.
390 /***************************************************************************//**
391  \param v <number-list | range> A list of numerical values or a range.
392  \param i1 <integer> The list element index at which to begin
393  summation (first when not specified).
394  \param i2 <integer> The list element index at which to end
395  summation (last when not specified).
396 
397  \returns (1) <number | number-list> The sum or list of sums over the
398  index range.
399  (2) Returns \b undef when list is empty, non-numeric, when
400  an index is specified and does not exists in the list
401  \p v, or when \p i1 > \\p i2.
402 
403  \details
404 
405  When the elements of \p v are themselves lists (e.g. 2d or 3d
406  vectors), the result is the element-wise sum across all vectors,
407  equivalent to component-wise vector addition. For example,
408  <tt>sum([[1,2],[3,4],[5,6]])</tt> returns <tt>[9,12]</tt>.
409 *******************************************************************************/
410 function sum
411 (
412  v,
413  i1,
414  i2
415 ) = is_range(v) ? sum([for (i=v) i], i1, i2)
416  : is_number(v) ? v
417  : !is_list(v) ? undef
418  : is_empty(v) ? undef
419  : is_defined(i1) && !is_between(i1, 0, len(v)-1) ? undef
420  : is_defined(i2) && !is_between(i2, 0, len(v)-1) ? undef
421  : all_defined([i1, i2]) && (i1 > i2) ? undef
422  : let
423  (
424  s = defined_or(i1, 0),
425  e = defined_or(i2, len(v)-1)
426  )
427  (s == e) ? v[s]
428  : v[e] + sum(v, s, e-1);
429 
430 //! Compute the mean/average of a list of numbers.
431 /***************************************************************************//**
432  \param v <number-list | range> A list of numerical values or a range.
433 
434  \returns (1) <number | number-list> The sum divided by the number of
435  elements.
436  (2) Returns \b undef when list is empty or is non-numeric.
437 
438  \details
439 
440  See [Wikipedia] for more information.
441 
442  [Wikipedia]: https://en.wikipedia.org/wiki/Mean
443 *******************************************************************************/
444 function mean
445 (
446  v
447 ) = is_range(v) ? mean([for (i=v) i])
448  : is_number(v) ? v
449  : !is_list(v) ? undef
450  : is_empty(v) ? undef
451  : sum(v) / len(v);
452 
453 //! Select specified element from list or return a default.
454 /***************************************************************************//**
455  \param v <list> A list of values.
456  \param i <integer> Element selection index.
457  \param l <bool> Last element is default. When \b false, the first
458  element is the default.
459 
460  \returns (1) <value> The value \p v[i] or the default element of
461  \p v when element \p i does not exists.
462 
463  \details
464 
465  \b Example
466  \code{.C}
467  ov = [ "value1", "value2", "default" ];
468 
469  select_ci( ov ) // "default"
470  select_ci( ov, 4 ) // "default"
471  select_ci( ov, 0 ) // "value1"
472  \endcode
473 *******************************************************************************/
474 function select_ci
475 (
476  v,
477  i,
478  l = true
479 ) = defined_e_or(v, i, (l == true) ? last(v) : first(v));
480 
481 //! Select a specified mapped value from list of key-value pairs or return a default.
482 /***************************************************************************//**
483  \param v <map> A matrix of N key-value pairs [[key, value], ...].
484  \param mv <value> A selection key value.
485  \param l <bool> Last element is default. When \b false, the first
486  element is the default.
487 
488  \returns (1) <value> The value from the map \p v that matches the
489  selection key \p mv.
490  (2) Returns the default value when \p mv does not match any
491  of the map keys \p v.
492 
493  \details
494 
495  \b Example
496  \code{.C}
497  ov = [ [0,"value0"], ["a","value1"], ["b","value2"], ["c","default"] ];
498 
499  select_cm( ov ) // "default"
500  select_cm( ov, "x" ) // "default"
501  select_cm( ov, 0 ) // "value0"
502  select_cm( ov, "b" ) // "value2"
503  \endcode
504 *******************************************************************************/
505 function select_cm
506 (
507  v,
508  mv,
509  l = true
510 ) = // use find() to avoid element is not found warnings
511  // search first element for first match
512  let ( i = first(find(mv, v, 1, 0)) )
513  is_undef(i) ? (l == true) ? second(last(v)) : second(first(v))
514  : second(v[i]);
515 
516 //! Select each element at an index position of a list of iterable values.
517 /***************************************************************************//**
518  \param v <list> A list of iterable values.
519  \param i <integer> Select the element index position.
520  \param f <boolean> Select the first element.
521  \param l <boolean> Select the last element.
522 
523  \returns (1) <list> A list of the selected element from each
524  iterable value of \p v.
525  (2) Returns \b undef when \p v is not iterable or when no
526  index selection is specified.
527  (3) Returns \b empty_lst when \p v is empty.
528 
529  \details
530 
531  When more than one selection criteria is specified, the order of
532  precedence is: \p i, \p f, \p l.
533 *******************************************************************************/
534 function select_e
535 (
536  v,
537  i,
538  f,
539  l
540 ) = !is_iterable(v) ? undef
541  : is_empty(v) ? empty_lst
542  : is_defined(i) ? concat( [first(v)[i]], select_e(tailn(v), i, f, l) )
543  : (f == true) ? concat( [first(first(v))], select_e(tailn(v), i, f, l) )
544  : (l == true) ? concat( [last(first(v))], select_e(tailn(v), i, f, l) )
545  : undef;
546 
547 //! Select n elements from each iterable value of a list.
548 /***************************************************************************//**
549  \param v <list> A list of iterable values.
550  \param f <boolean> Select the first element.
551  \param l <boolean> Select the last element.
552  \param i <integer> Select a numeric element index position.
553  \param n <integer> The element count.
554 
555  \returns (1) <list> A list of the list of \p n selected sequential
556  elements from each iterable value of \p v.
557  (2) Returns \b undef when \p v is not iterable or when no
558  index selection is specified.
559  (3) Returns \b empty_lst when \p v is empty.
560 
561  \details
562 
563  When selecting the \p n elements, only the available elements will
564  be returned when \p n is greater than the number of elements
565  available. When more than one selection criteria is specified, the
566  order of precedence is: \p i, \p f, \p l.
567 *******************************************************************************/
568 function select_en
569 (
570  v,
571  i,
572  f,
573  l,
574  n = 1
575 ) = !is_iterable(v) ? undef
576  : is_empty(v) ? empty_lst
577  : is_defined(i) ?
578  concat
579  (
580  [firstn(select_r(v[0], [i:len(v[0])-1]), n)],
581  select_en(tailn(v), i, f, l, n)
582  )
583  : (f == true) ? concat( [firstn(first(v), n)], select_en(tailn(v), i, f, l, n) )
584  : (l == true) ? concat( [lastn(first(v), n)], select_en(tailn(v), i, f, l, n) )
585  : undef;
586 
587 //! Serially merge the elements of a list.
588 /***************************************************************************//**
589  \param v <list> A list of iterable values.
590  \param r <boolean> Recursively merge elements that are iterable.
591 
592  \returns (1) <list> A list containing the serial-wise element
593  conjunction of each element in \p v.
594  (2) Returns \b undef when \p v is not iterable.
595 
596  \details
597 
598  \b Example
599  \code{.C}
600  l = [[1,2,3],[[[[0]]]], [4,5,6],[[[7,8,[9]]]]];
601 
602  echo( merge_s( l, true ) );
603  \endcode
604 
605  \b Result
606  \code{.C}
607  ECHO: [1, 2, 3, 4, 5, 6, 7, 8, 9]
608  \endcode
609 *******************************************************************************/
610 function merge_s
611 (
612  v,
613  r = false
614 ) = !is_iterable(v) ? undef
615  : is_empty(v) ? empty_lst
616  : ( (r == true) && is_list(first(v)) ) ?
617  concat(merge_s(first(v), r), merge_s(tailn(v), r))
618  : concat(first(v), merge_s(tailn(v), r));
619 
620 //! Parallel-merge the iterable elements of a list.
621 /***************************************************************************//**
622  \param v <list> A list of iterable values.
623  \param j <boolean> Join each merge as a separate list.
624 
625  \returns (1) <list> A list containing the parallel-wise element
626  conjunction of each iterable value in \p v.
627  (2) Returns \b undef when \p v is not iterable.
628 
629  \details
630 
631  The element \c i of each iterable value in \p v are visited and
632  joined to form a new list for every iterable value of \p v. The
633  resulting list length will be limited by the iterable value of \p v
634  with the shortest length.
635 
636  \b Example
637  \code{.C}
638  v1=["a", "b", "c", "d"];
639  v2=[1, 2, 3];
640 
641  echo( merge_p( [v1, v2], true ) );
642  echo( merge_p( [v1, v2], false ) );
643  \endcode
644 
645  \b Result
646  \code{.C}
647  ECHO: [["a", 1], ["b", 2], ["c", 3]]
648  ECHO: ["a", 1, "b", 2, "c", 3]
649  \endcode
650 *******************************************************************************/
651 function merge_p
652 (
653  v,
654  j = true
655 ) = !is_iterable(v) ? undef
656  : is_empty(v) ? empty_lst
657  // each value of 'v' must also be iterable
658  : !all_iterables(v) ? undef
659  // no value of 'v' may be an empty
661  : let
662  (
663  h = [for (i = v) first(i)],
664  t = [for (i = v) tailn(i)]
665  )
666  (j == true) ? concat([h], merge_p(t, j))
667  : concat(h, merge_p(t, j));
668 
669 //! Sort the elements of an iterable value using quick sort.
670 /***************************************************************************//**
671  \param v <iterable> A iterable values.
672  \param i <integer> The sort element index for iterable values of \p v.
673  \param r <boolean> Reverse the sort order.
674 
675  \returns (1) <list> A list with elements sorted in ascending order.
676  (2) Returns \b undef when \p v is not iterable.
677 
678  \details
679 
680  This implementation relies on the comparison operators '<', '>',
681  and '==' which expect the operands to be either two scalar numbers
682  or two strings. Therefore, this function will not correctly sort
683  lists elements if differing data types. Elements with unknown order
684  are placed at the end of the list. When sorting lists of
685  non-iterable values or strings, the \p i must not be specified.
686 
687  See [Wikipedia] for more information.
688 
689  [Wikipedia]: https://en.wikipedia.org/wiki/Quicksort
690 *******************************************************************************/
691 function sort_q
692 (
693  v,
694  i,
695  r = false
696 ) = !is_iterable(v) ? undef
697  : is_empty(v) ? empty_lst
698  : let
699  (
700  mp = v[floor(len(v)/2)], // mid-point index
701  me = is_undef(i) ? mp : mp[i], // mid-point element
702 
703  // place each element of 'v' into bin
704  lt = [for (j = v) let(k = is_undef(i) ? j : j[i]) if (k < me) j],
705  eq = [for (j = v) let(k = is_undef(i) ? j : j[i]) if (k == me) j],
706  gt = [for (j = v) let(k = is_undef(i) ? j : j[i]) if (k > me) j],
707 
708  // un-orderable elements of 'v'
709  uo = [
710  for (j = v) let(k = is_undef(i) ? j : j[i])
711  if ( !( (k < me) || (k == me) || (k > me) ) ) j
712  ],
713 
714  sp = (r == true) ? concat(sort_q(gt, i, r), eq, sort_q(lt, i, r), uo)
715  : concat(sort_q(lt, i, r), eq, sort_q(gt, i, r), uo)
716  )
717  sp;
718 
719 //! Sort the elements of an iterable value using quick sort and compare
720 /***************************************************************************//**
721  \param v <iterable> A iterable values.
722  \param i <integer> The sort element index for iterable values of \p v.
723  \param d <integer> The recursive sort depth.
724  \param r <boolean> Reverse the sort order.
725  \param s <boolean> Order ranges by their enumerated sum.
726 
727  \returns (1) <list> A list with elements sorted in ascending order.
728  (2) Returns \b undef when \p v is not iterable.
729 
730  \details
731 
732  Elements are ordered using the compare() function. See its
733  documentation for a more information. To recursively sort all
734  elements, set \p d to a value greater than, or equal to, the
735  maximum level of hierarchy in \p v. During hierarchial sorts, empty
736  list-levels are reduced.
737 
738  See [Wikipedia] for more information.
739 
740  [Wikipedia]: https://en.wikipedia.org/wiki/Quicksort
741 *******************************************************************************/
742 function sort_q2
743 (
744  v,
745  i,
746  d = 0,
747  r = false,
748  s = true
749 ) = !is_iterable(v) ? undef
751  : let
752  (
753  mp = v[floor(len(v)/2)],
754  me = is_undef(i) ? mp : mp[i],
755 
756  lt =
757  [
758  for (j = v)
759  let(k = is_undef(i) ? j : j[i])
760  if (compare(me, k, s) == -1)
761  ((d > 0) && is_list(k)) ? sort_q2(k, i, d-1, r, s) : j
762  ],
763  eq =
764  [
765  for (j = v)
766  let(k = is_undef(i) ? j : j[i])
767  if (compare(me, k, s) == 0)
768  ((d > 0) && is_list(k)) ? sort_q2(k, i, d-1, r, s) : j
769  ],
770  gt =
771  [
772  for (j = v)
773  let(k = is_undef(i) ? j : j[i])
774  if (compare(me, k, s) == +1)
775  ((d > 0) && is_list(k)) ? sort_q2(k, i, d-1, r, s) : j
776  ],
777  sp = (r == true) ?
778  concat(sort_q2(gt, i, d, r, s), eq, sort_q2(lt, i, d, r, s))
779  : concat(sort_q2(lt, i, d, r, s), eq, sort_q2(gt, i, d, r, s))
780  )
781  (d > 0) ? sort_q2(sp, i, d-1, r, s) : sp;
782 
783 //! @}
784 //! @}
785 
786 //----------------------------------------------------------------------------//
787 // openscad-amu auxiliary scripts
788 //----------------------------------------------------------------------------//
789 
790 /*
791 BEGIN_SCOPE validate;
792  BEGIN_OPENSCAD;
793  include <omdl-base.scad>;
794  include <common/validation.scad>;
795 
796  function fmt( id, td, v1, v2, v3 ) = table_validate_fmt(id, td, v1, v2, v3);
797  function v1(db, id) = table_validate_get_v1(db, id);
798  t = true; f = false; u = undef; skip = validation_skip;
799 
800  tbl_test_values =
801  [
802  fmt("t01", "The undefined value", undef),
803  fmt("t02", "The empty list", empty_lst),
804  fmt("t03", "A long range", [0:0.5:9]),
805  fmt("t04", "string = (A string)", "A string"),
806  fmt("t05", "List-4 fruit", ["orange","apple","grape","banana"]),
807  fmt("t06", "List-7 characters", ["b","a","n","a","n","a","s"]),
808  fmt("t07", "List-1 undefined", [undef]),
809  fmt("t08", "List-2 integers-2", [[1,2],[2,3]]),
810  fmt("t09", "List-4 iterable-2", ["ab",[1,2],[2,3],[4,5]]),
811  fmt("t10", "List-4 iterable-3", [[1,2,3],[4,5,6],[7,8,9],["a","b","c"]]),
812  fmt("t11", "List-15 of integers", [for (i=[0:15]) i])
813  ];
814 
815  tbl_test_answers =
816  [ // function
817  ["strl",
818  undef, // t01
819  empty_str, // t02
820  "[0 : 0.5 : 9]", // t03
821  "A string", // t04
822  "orangeapplegrapebanana", // t05
823  "bananas", // t06
824  "undef", // t07
825  "[1, 2][2, 3]", // t08
826  "ab[1, 2][2, 3][4, 5]", // t09
827  "[1, 2, 3][4, 5, 6][7, 8, 9][\"a\", \"b\", \"c\"]", // t10
828  "0123456789101112131415" // t11
829  ],
830  ["strl_html_B",
831  empty_str, // t01
832  empty_str, // t02
833  empty_str, // t03
834  "<b>A string</b>", // t04
835  "<b>orange</b><b>apple</b><b>grape</b><b>banana</b>",
836  "<b>b</b><b>a</b><b>n</b><b>a</b><b>n</b><b>a</b><b>s</b>",
837  "<b>undef</b>", // t07
838  "<b>[1, 2]</b><b>[2, 3]</b>", // t08
839  "<b>ab</b><b>[1, 2]</b><b>[2, 3]</b><b>[4, 5]</b>", // t09
840  "<b>[1, 2, 3]</b><b>[4, 5, 6]</b><b>[7, 8, 9]</b><b>[\"a\", \"b\", \"c\"]</b>",
841  "<b>0</b><b>1</b><b>2</b><b>3</b><b>4</b><b>5</b><b>6</b><b>7</b><b>8</b><b>9</b><b>10</b><b>11</b><b>12</b><b>13</b><b>14</b><b>15</b>"
842  ],
843  ["consts",
844  empty_lst, // t01
845  empty_lst, // t02
846  empty_lst, // t03
847  empty_lst, // t04
848  empty_lst, // t05
849  empty_lst, // t06
850  empty_lst, // t07
851  empty_lst, // t08
852  empty_lst, // t09
853  empty_lst, // t10
854  empty_lst // t11
855  ],
856  ["index_sel",
857  empty_lst, // t01
858  empty_lst, // t02
859  empty_lst, // t03
860  empty_lst, // t04
861  [0,1,2,3], // t05
862  [0,1,2,3,4,5,6], // t06
863  [0], // t07
864  [0,1], // t08
865  [0,1,2,3], // t09
866  [0,1,2,3], // t10
867  [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] // t11
868  ],
869  ["sum",
870  skip, // t11
871  skip, // t11
872  skip, // t11
873  skip, // t11
874  skip, // t05
875  skip, // t11
876  skip, // t11
877  skip, // t11
878  skip, // t09
879  skip, // t11
880  120 // t11
881  ],
882  ["mean",
883  undef, // t01
884  undef, // t02
885  4.5, // t03
886  undef, // t04
887  undef, // t05
888  undef, // t06
889  undef, // t07
890  [1.5,2.5], // t08
891  undef, // t09
892  [undef,undef,undef], // t10
893  7.5 // t11
894  ],
895  ["select_e_F",
896  undef, // t01
897  empty_lst, // t02
898  undef, // t03
899  ["A"," ","s","t","r","i","n","g"], // t04
900  ["o","a","g","b"], // t05
901  ["b","a","n","a","n","a","s"], // t06
902  [undef], // t07
903  [1,2], // t08
904  ["a",1,2,4], // t09
905  [1,4,7,"a"], // t10
906  skip // t11
907  ],
908  ["select_e_L",
909  undef, // t01
910  empty_lst, // t02
911  undef, // t03
912  ["A"," ","s","t","r","i","n","g"], // t04
913  ["e","e","e","a"], // t05
914  ["b","a","n","a","n","a","s"], // t06
915  [undef], // t07
916  [2,3], // t08
917  ["b",2,3,5], // t09
918  [3,6,9,"c"], // t10
919  skip // t11
920  ],
921  ["select_e_1",
922  undef, // t01
923  empty_lst, // t02
924  undef, // t03
925  skip, // t04
926  ["r","p","r","a"], // t05
927  skip, // t06
928  [undef], // t07
929  [2,3], // t08
930  ["b",2,3,5], // t09
931  [2,5,8,"b"], // t10
932  skip // t11
933  ],
934  ["select_en_F2",
935  undef, // t01
936  empty_lst, // t02
937  undef, // t03
938  [["A"],[" "],["s"],["t"],["r"],["i"],["n"],["g"]], // t04
939  [["o","r"],["a","p"],["g","r"],["b","a"]], // t05
940  [["b"],["a"],["n"],["a"],["n"],["a"],["s"]], // t06
941  [undef], // t07
942  [[1,2],[2,3]], // t08
943  [["a","b"],[1,2],[2,3],[4,5]], // t09
944  [[1,2],[4,5],[7,8],["a","b"]], // t10
945  skip // t11
946  ],
947  ["select_en_L3",
948  undef, // t01
949  empty_lst, // t02
950  undef, // t03
951  [["A"],[" "],["s"],["t"],["r"],["i"],["n"],["g"]], // t04
952  [
953  ["n","g","e"],["p","l","e"],
954  ["a","p","e"],["a","n","a"]
955  ], // t05
956  [["b"],["a"],["n"],["a"],["n"],["a"],["s"]], // t06
957  [undef], // t07
958  [[1,2],[2,3]], // t08
959  [["a","b"],[1,2],[2,3],[4,5]], // t09
960  [[1,2,3],[4,5,6],[7,8,9],["a","b","c"]], // t10
961  skip // t11
962  ],
963  ["select_en_12",
964  undef, // t01
965  empty_lst, // t02
966  undef, // t03
967  skip, // t04 (DEPRECATED WARNING)
968  [["r","a"],["p","p"],["r","a"],["a","n"]], // t05
969  skip, // t06
970  [undef], // t07
971  [[2],[3]], // t08
972  [["b"],[2],[3],[5]], // t09
973  [[2,3],[5,6],[8,9],["b","c"]], // t10
974  skip // t11
975  ],
976  ["merge_s",
977  undef, // t01
978  empty_lst, // t02
979  undef, // t03
980  ["A"," ","s","t","r","i","n","g"], // t04
981  ["orange","apple","grape","banana"], // t05
982  ["b","a","n","a","n","a","s"], // t06
983  [undef], // t07
984  [1,2,2,3], // t08
985  ["ab",1,2,2,3,4,5], // t09
986  [1,2,3,4,5,6,7,8,9,"a","b","c"], // t10
987  [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] // t11
988  ],
989  ["merge_p",
990  undef, // t01
991  empty_lst, // t02
992  undef, // t03
993  [["A"," ","s","t","r","i","n","g"]], // t04
994  [
995  ["o","a","g","b"],["r","p","r","a"],
996  ["a","p","a","n"],["n","l","p","a"],
997  ["g","e","e","n"]
998  ], // t05
999  [["b","a","n","a","n","a","s"]], // t06
1000  undef, // t07
1001  [[1,2],[2,3]], // t08
1002  [["a",1,2,4],["b",2,3,5]], // t09
1003  [[1,4,7,"a"],[2,5,8,"b"],[3,6,9,"c"]], // t10
1004  undef // t11
1005  ],
1006  ["sort_q",
1007  undef, // t01
1008  empty_lst, // t02
1009  undef, // t03
1010  undef, // t04
1011  ["apple","banana","grape","orange"], // t05
1012  ["a","a","a","b","n","n","s"], // t06
1013  [undef], // t07
1014  skip, // t08
1015  skip, // t09
1016  skip, // t10
1017  [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] // t11
1018  ],
1019  ["sort_q_1R",
1020  undef, // t01
1021  empty_lst, // t02
1022  undef, // t03
1023  undef, // t04
1024  ["orange","grape","apple","banana"], // t05
1025  skip, // t06
1026  skip, // t07
1027  [[2,3],[1,2]], // t08
1028  [[4,5],[2,3],[1,2],"ab"], // t09
1029  [[7,8,9],[4,5,6],[1,2,3],["a","b","c"]], // t10
1030  skip // t11
1031  ],
1032  ["sort_q2_1R",
1033  undef, // t01
1034  empty_lst, // t02
1035  undef, // t03
1036  undef, // t04
1037  ["orange","grape","apple","banana"], // t05
1038  skip, // t06
1039  skip, // t07
1040  [[2,3],[1,2]], // t08
1041  ["ab",[4,5],[2,3],[1,2]], // t09
1042  [["a","b","c"],[7,8,9],[4,5,6],[1,2,3]], // t10
1043  skip // t11
1044  ],
1045  ["sort_q2_HR",
1046  undef, // t01
1047  empty_lst, // t02
1048  undef, // t03
1049  undef, // t04
1050  ["orange","grape","banana","apple"], // t05
1051  ["s","n","n","b","a","a","a"], // t06
1052  [undef], // t07
1053  [[3,2],[2,1]], // t08
1054  [[5,4],[3,2],[2,1],"ab"], // t09
1055  [["c","b","a"],[9,8,7],[6,5,4],[3,2,1]], // t10
1056  [15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0] // t11
1057  ]
1058  ];
1059 
1060 
1061  db = table_validate_init( tbl_test_values, tbl_test_answers );
1062 
1063  table_validate_start( db );
1064  test_ids = table_validate_get_ids( db );
1065 
1066  for (id=test_ids) table_validate( db, id, "strl", 1, strl( v1(db,id)) );
1067  for (id=test_ids) table_validate( db, id, "strl_html_B", 1, strl_html( v1(db,id),p="b") );
1068  for (id=test_ids) table_validate( db, id, "consts", 1, consts( v1(db,id)) );
1069  for (id=test_ids) table_validate( db, id, "index_sel", 1, index_sel( v1(db,id)) );
1070  validate_skip( "round_d()" );
1071  validate_skip( "round_s()" );
1072  validate_skip( "limit()" );
1073  validate_skip( "sum()" );
1074  validate_skip( "mean()" );
1075  validate_skip( "select_ci()" );
1076  validate_skip( "select_cm()" );
1077  for (id=test_ids) table_validate( db, id, "select_e_F", 1, select_e( v1(db,id),f=true) );
1078  for (id=test_ids) table_validate( db, id, "select_e_L", 1, select_e( v1(db,id),l=true) );
1079  for (id=test_ids) table_validate( db, id, "select_e_1", 1, select_e( v1(db,id),i=1) );
1080  for (id=test_ids) table_validate( db, id, "select_en_F2", 1, select_en( v1(db,id),f=true,n=2) );
1081  validate_skip( "select_en()" );
1082  for (id=test_ids) table_validate( db, id, "merge_s", 1, merge_s( v1(db,id)) );
1083  for (id=test_ids) table_validate( db, id, "merge_p", 1, merge_p( v1(db,id)) );
1084  validate_skip( "sort_q()" );
1085  validate_skip( "sort_q2()" );
1086 
1087  // end_include
1088  END_OPENSCAD;
1089 
1090  BEGIN_MFSCRIPT;
1091  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
1092  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1093  END_MFSCRIPT;
1094 END_SCOPE;
1095 */
1096 
1097 //----------------------------------------------------------------------------//
1098 // end of file
1099 //----------------------------------------------------------------------------//
empty_str
<string> A string with no characters (the empty string).
Definition: constants.scad:301
empty_lst
<list> A list with no values (the empty list).
Definition: constants.scad:304
function firstn(v, n=1)
Return a list containing the first n elements of an iterable value.
function select_r(v, i)
Select a range of elements from an iterable value.
function find(mv, v, c=1, i, i1=0, i2)
Find the occurrences of a match value in an iterable value.
function defined_e_or(v, i, d)
Returns an element from an iterable if it exists, or a default value if not.
function last(v)
Return the last element of an iterable value.
function exists_e(i, v)
Test if an element exists at a specified index of an iterable value.
function second(v)
Return the second element of an iterable value.
function lastn(v, n=1)
Return a list containing the last n elements of an iterable value.
function first(v)
Return the first element of an iterable value.
function tailn(v, n=1)
Return a list containing all but the first n elements of an iterable value.
function reverse(v)
Reverse the elements of an iterable value.
function all_defined(v)
Test if no element of an iterable value has an undefined value.
function any_undefined(v)
Test if at least one element of an iterable value has an undefined value.
function is_iterable(v)
Test if a value has multiple parts and is iterable.
function all_iterables(v)
Test if all elements of an iterable value are iterable.
function is_empty(v)
Test if an iterable value is empty.
function any_equal(v, cv)
Test if any element of an iterable value equal a comparison value.
function all_numbers(v)
Test if all elements of an iterable value are numbers.
function compare(v1, v2, s=true)
Compare the sort order of any two values.
function round_d(v, d=6)
Round a list of numbers to a fixed number of decimal point digits.
function mean(v)
Compute the mean/average of a list of numbers.
function select_en(v, i, f, l, n=1)
Select n elements from each iterable value of a list.
function select_cm(v, mv, l=true)
Select a specified mapped value from list of key-value pairs or return a default.
function select_ci(v, i, l=true)
Select specified element from list or return a default.
function sort_q2(v, i, d=0, r=false, s=true)
Sort the elements of an iterable value using quick sort and compare.
function index_sel(l, s=true, rs)
Selected element indices of a given list according to a selection scheme.
function sort_q(v, i, r=false)
Sort the elements of an iterable value using quick sort.
function limit(v, l, u)
Limit a list of numbers between an upper and lower bounds.
function consts(l, v, u=false)
Create a list of constant or incrementing elements.
function strl(v)
Convert a list of values to a concatenated string.
function round_s(v, d=6)
Round a list of numbers to a fixed number of significant figures.
function strl_html(v, b, p, a, f, d=false)
Convert a list of values to a concatenated HTML-formatted string.
function merge_s(v, r=false)
Serially merge the elements of a list.
function merge_p(v, j=true)
Parallel-merge the iterable elements of a list.
function sum(v, i1, i2)
Compute the sum of a list of numbers.
function select_e(v, i, f, l)
Select each element at an index position of a list of iterable values.
function defined_or(v, d)
Return given value, if defined, or a secondary value, if primary is not defined.
function is_between(v, l, u)
Test if a numerical value is between an upper and lower bounds.
function is_defined(v)
Test if a value is defined.
function is_number(v)
Test if a value is a number.
function is_range(v)
Test if a value is a range definition.