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