omdl  v0.9.5
OpenSCAD Mechanical Design Library
list_compare.scad
Go to the documentation of this file.
1 //! List data type comparision.
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 Comparision)
31  \amu_define group_brief (List comparision operations.)
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 //! Test to see if two numerical vectors are sufficiently equal.
62 /***************************************************************************//**
63  \param v1 <vector-Nd> A vector 1 in an 'N' dimensional space.
64  \param v2 <vector-Nd> A vector 2 in an 'N' dimensional space.
65  \param p <number> The numerical precision.
66 
67  \returns (1) <boolean> \b true when the distance between \p v1 and
68  \p v2 is less than \p d and \b false otherwise.
69  (2) Returns \b false when either \p v1 or \p v2 are not
70  numerical vectors of the same length.
71 
72  \details
73 
74  Can also compare two scalar numbers. To compare general lists of
75  non-numerical values see almost_eq().
76 *******************************************************************************/
77 function almost_eq_nv
78 (
79  v1,
80  v2,
81  p = 6
82 ) = !all_numbers(concat(v1, v2)) ? false // must be all numbers
83  : all_scalars(concat([v1], [v2])) ? // compare if scalars?
84  ( (sqrt((v1-v2)*(v1-v2)) * pow(10, p)) < 1 ) // -> compare as scalars
85  : !all_lists(concat([v1], [v2])) ? false // requires all lists or false
86  : (len(v1) != len(v2)) ? false // size must be equal
87  : ( (sqrt((v1-v2)*(v1-v2)) * pow(10, p)) < 1 ); // -> compare as vectors
88 
89 //! Test if all elements of two iterable values are sufficiently equal.
90 /***************************************************************************//**
91  \param v1 <iterable> An iterable data type value 1.
92  \param v2 <iterable> An iterable data type value 2.
93  \param p <number> The precision for numerical comparisons.
94 
95  \returns (1) <boolean> \b true when all elements of each iterable
96  value are \em sufficiently equal and \b false
97  otherwise.
98 
99  \details
100 
101  The iterable values can be of mixed data types. All numerical
102  comparisons are performed using the specified precision. All
103  non-numeric comparisons test for equality. When both \p v1 and \p
104  v2 are both numerical vectors, the function almost_eq_nv()
105  provides a more efficient test.
106 *******************************************************************************/
107 function almost_eq
108 (
109  v1,
110  v2,
111  p = 6
112 ) = all_numbers(concat(v1, v2)) ? almost_eq_nv(v1, v2, p) // all numerical
113  : all_scalars(concat([v1], [v2])) ? (v1 == v2) // all single values
114  : (is_string(v1) || is_string(v2)) ? (v1 == v2) // either is a string
115  : !all_iterables(concat([v1], [v2])) ? false // false if either not iterable
116  : !almost_eq(first(v1), first(v2), p) ? false // compare first elements
117  : almost_eq(tailn(v1), tailn(v2), p); // compare remaining elements
118 
119 //! Compare the sort order of any two values.
120 /***************************************************************************//**
121  \param v1 <value> The values 1.
122  \param v2 <value> The values 2.
123  \param s <boolean> Order ranges by their enumerated sum.
124 
125  \returns (1) <integer> An integer value.
126 
127  \details
128 
129  Return values (rv) table.
130 
131  rv | order of values
132  :-------:|:-------------------
133  \b -1 | <tt>(v2 < v1)</tt>
134  \b 0 | <tt>(v2 == v1)</tt>
135  \b +1 | <tt>(v2 > v1)</tt>
136 
137  The following table summarizes how data type values are ordered.
138 
139  order | type | \p s | intra-type ordering
140  :-----:|:--------:|:---------:|:--------------------------------------
141  1 | \b undef | | (singular)
142  2 | number | | numerical comparison
143  3 | boolean | | \b false < \b true
144  4 | string | | lexical comparison
145  5 | list | | compare (1) lengths, then (2) element-wise
146  6 | range | \b true | compare sum of range elements
147  6 | range | \b false | compare (1) lengths, then (2) element-wise
148 
149  When comparing two lists of equal length, the comparison continues
150  with successive elements until an ordering can be determined. Two
151  lists are equal when all elements have been compared and no
152  ordering has been determined.
153 
154  \warning The performance of element-wise comparisons degrades
155  exponentially with list size and the sum of a range may
156  exceeded the intermediate variable storage capacity for
157  long ranges.
158 *******************************************************************************/
159 function compare
160 (
161  v1,
162  v2,
163  s = true
164 ) = let( v2_nd = is_undef(v2) )
165  is_undef(v1) ?
166  (
167  v2_nd ? 0
168  : 1 // others are greater
169  )
170  // v1 a number
171  : let( v2_in = is_number(v2) )
172  is_number(v1) ?
173  (
174  v2_nd ? -1
175  : v2_in ?
176  (
177  (v1 > v2) ? -1 // compare numbers
178  : (v2 > v1) ? +1
179  : 0
180  )
181  : 1 // others are greater
182  )
183  // v1 a boolean
184  : let( v2_ib = is_bool(v2) )
185  is_bool(v1) ?
186  (
187  (v2_nd || v2_in) ? -1
188  : v2_ib ?
189  (
190  ((v1 == true) && (v2 == false)) ? -1 // defined: true > false
191  : ((v1 == false) && (v2 == true)) ? +1
192  : 0
193  )
194  : 1 // others are greater
195  )
196  // v1 a string
197  : let( v2_is = is_string(v2) )
198  is_string(v1) ?
199  (
200  (v2_nd || v2_in || v2_ib) ? -1
201  : v2_is ?
202  (
203  (v1 > v2) ? -1 // compare strings
204  : (v2 > v1) ? +1
205  : 0
206  )
207  : 1 // others are greater
208  )
209  // v1 a list
210  : let( v2_il = is_list(v2) )
211  is_list(v1) ?
212  (
213  (v2_nd || v2_in || v2_ib || v2_is) ? -1
214  : v2_il ?
215  (
216  let
217  (
218  l1 = len(merge_s(v1, true)), // get total element count
219  l2 = len(merge_s(v2, true))
220  )
221  (l1 > l2) ? -1 // longest list is greater
222  : (l2 > l1) ? +1
223  : ((l1 == 0) && (l2 == 0)) ? 0 // reached end, are equal
224  : let
225  (
226  cf = compare(first(v1), first(v2), s) // compare first elements
227  )
228  (cf != 0) ? cf // not equal, ordering determined
229  : compare(tailn(v1), tailn(v2), s) // equal, check remaining
230  )
231  : 1 // others are greater
232  )
233  // v1 a range.
234  : is_range(v2) ?
235  (
236  (v1 == v2) ? 0 // equal range definitions
237  // compare range sums
238  : (s == true) ?
239  (
240  let
241  (
242  rs1 = sum(v1), // compute range sums
243  rs2 = sum(v2)
244  )
245  (rs1 > rs2) ? -1 // greatest sum is greater
246  : (rs2 > rs1) ? +1
247  : 0 // sums equal
248  )
249  // compare range lists
250  : (
251  let
252  (
253  rv1 = [for (i=v1) i], // convert to lists
254  rv2 = [for (i=v2) i],
255  rl1 = len(rv1), // get lengths
256  rl2 = len(rv2)
257  )
258  (rl1 > rl2) ? -1 // longest range is greater
259  : (rl2 > rl1) ? +1
260  : compare(rv1, rv2, s) // equal so compare as lists
261  )
262  )
263  // v2 not a range so v1 > v2
264  : -1;
265 
266 //! @}
267 //! @}
268 
269 //----------------------------------------------------------------------------//
270 // openscad-amu auxiliary scripts
271 //----------------------------------------------------------------------------//
272 
273 /*
274 BEGIN_SCOPE validate;
275  BEGIN_OPENSCAD;
276  include <omdl-base.scad>;
277  include <common/validation.scad>;
278 
279  function fmt( id, td, v1, v2, v3 ) = table_validate_fmt(id, td, v1, v2, v3);
280  function v1(db, id) = table_validate_get_v1(db, id);
281  function v2(db, id) = table_validate_get_v2(db, id);
282  t = true; f = false; u = undef; s = validation_skip;
283 
284  tbl_test_values =
285  [
286  fmt("t01", "The undefined value",
287  undef,
288  undef
289  ),
290  fmt("t02", "Integers",
291  2121,
292  2100
293  ),
294  fmt("t03", "Equal srings",
295  "This is a test",
296  "This is a test"
297  ),
298  fmt("t04", "Non-equal srings",
299  "This is test v1",
300  "This is test v2"
301  ),
302  fmt("t05", "Non-equal srings 2",
303  "v1 this is test v1",
304  "v2 this is test v2"
305  ),
306  fmt("t06", "Empty strings",
307  empty_str,
308  empty_str
309  ),
310  fmt("t07", "Empty lists",
311  empty_lst,
312  empty_lst
313  ),
314  fmt("t08", "Non-equal short ranges",
315  [0:9],
316  [-1:9]
317  ),
318  fmt("t09", "Equal ranges",
319  [0:9],
320  [0:9]
321  ),
322  fmt("t10", "Long and short ranges",
323  [0:0.5:9],
324  [0:9]
325  ),
326  fmt("t11", "Lists with num-3 + undef",
327  [1, 2, 3, undef],
328  [undef, 1, 2, 3]
329  ),
330  fmt("t12", "3D vector and scalar",
331  [21, 32, 35],
332  19
333  ),
334  fmt("t13", "Unequal 3D vectors",
335  [1, 2, 3],
336  [3, 2, 1]
337  ),
338  fmt("t14", "equal 5D vectors",
339  [10, 12, 33, 98, 100],
340  [10, 12, 33, 98, 100]
341  ),
342  fmt("t15", "close 5D vectors",
343  [09.999, 11.999, 32.999, 97.999, 99.999],
344  [10.001, 12.001, 33.001, 98.001, 100.001]
345  ),
346  fmt("t16", "Equal tuples list-3",
347  [[1,2,3], [4,5,6], [7,8,9]],
348  [[1,2,3], [4,5,6], [7,8,9]]
349  ),
350  fmt("t17", "Equal mixed-tuples list",
351  [[1,2,3], [4,5,6], [7,8,9], ["a", "b", "c"]],
352  [[1,2,3], [4,5,6], [7,8,9], ["a", "b", "c"]]
353  ),
354  fmt("t18", "List-4, all undef",
355  [undef, undef, undef, undef],
356  [undef, undef, undef, undef]
357  ),
358  fmt("t19", "List of equal num lists-1",
359  [[1], [2], [3], [4], [5]],
360  [[1], [2], [3], [4], [5]]
361  ),
362  fmt("t20", "List-4 of close number pairs",
363  [[1,2], [3,4], [5,6], [7,8]],
364  [[1.001,2.001], [3.001,4.001], [5.001,6.001], [7.001,8.001]]
365  )
366  ];
367 
368  tbl_test_answers =
369  [ // function 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20
370  ["almost_eq_nv_p4", f, f, f, f, f, f, f, f, f, f, f, f, f, t, f, f, f, f, f, f],
371  ["almost_eq_nv_p2", f, f, f, f, f, f, f, f, f, f, f, f, f, t, t, f, f, f, f, f],
372  ["almost_eq_p2", t, f, t, f, f, t, t, f, t, f, f, f, f, t, t, t, t, t, t, t],
373  ["compare", 0,-1, 0,+1,+1, 0, 0,-1, 0,-1,-1,-1,+1, 0,+1, 0, 0, 0, 0,+1],
374  ];
375 
376  db = table_validate_init( tbl_test_values, tbl_test_answers );
377 
378  table_validate_start( db );
379  test_ids = table_validate_get_ids( db );
380 
381  for (id=test_ids) table_validate( db, id, "almost_eq_nv_p4", 2, almost_eq_nv( v1(db,id), v2(db,id), 4) );
382  for (id=test_ids) table_validate( db, id, "almost_eq_nv_p2", 2, almost_eq_nv( v1(db,id), v2(db,id), 2) );
383  for (id=test_ids) table_validate( db, id, "almost_eq_p2", 2, almost_eq( v1(db,id), v2(db,id), 2) );
384  for (id=test_ids) table_validate( db, id, "compare", 2, compare( v1(db,id), v2(db,id) ) );
385 
386  // end_include
387  END_OPENSCAD;
388 
389  BEGIN_MFSCRIPT;
390  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
391  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
392  END_MFSCRIPT;
393 END_SCOPE;
394 */
395 
396 //----------------------------------------------------------------------------//
397 // end of file
398 //----------------------------------------------------------------------------//
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 all_numbers(v, c=0)
Test if all elements of an iterable value are numbers.
function all_scalars(v)
Test if all elements of an iterable value are scalar values.
function all_lists(v, c=0)
Test if all elements of an iterable value are lists.
function all_iterables(v)
Test if all elements of an iterable value are iterable.
function almost_eq(v1, v2, p=6)
Test if all elements of two iterable values are sufficiently equal.
function almost_eq_nv(v1, v2, p=6)
Test to see if two numerical vectors are sufficiently equal.
function compare(v1, v2, s=true)
Compare the sort order of any two values.
function merge_s(v, r=false)
Serially merge the elements of a list.
function sum(v, i1, i2)
Compute the sum of a list of numbers.
function is_number(v)
Test if a value is a number.
function is_range(v)
Test if a value is a range definition.