Sort debugging and optimizing

There were bugs in the previous sorting functions. They didn't check the homogeneity of the input list before calling _sort_scalars and _sort_vectors. The bug might result in wrong order and missing list elements in the output.
Besides correcting the bug a recode of all sorting functions result in better performance and a enlargement of their scope. With the new functions, list of vectors of any dimension may be sorted, even with idx given, with the native comparison operators. The scope of indexed sorting is also extended.

The file test_arrays has been extended to check the new funcionality.

New functions:

is_homogeneous - checks if a list has elements of the same type (although not distinguing booleans from numbers) up to a given depth
_sort_vectors - internal function to sort homgeneous lists of vectors using native comparison operators; extends the scope of the previous _sort_vectors# functions with better performance
_lexical_sort - internal function to sort non-homogeneous lists; uses compare_vals
_indexed_sort - internal function to perform indexed sorting of non-homogeneous lists; uses compar_vals

Changed/reviewed functions:
_valid_idx - doesn't requires the input of imin and imax args
sort - explores the internal functions to get better performance and an enlarged scope
sortidx - explores the internal functions to get better performance and an enlarged scope
_sort_general - just for sortings of non-homogeneous lists using compare_vals
_array_dim_recurse - changed for bit better performance

Functions eliminated:
 _sort_vectors1
 _sort_vectors2
 _sort_vectors3
 _sort_vectors4
This commit is contained in:
RonaldoCMP 2020-08-30 12:12:36 +01:00
parent 14b0f264ac
commit bbe4ad1467
2 changed files with 198 additions and 164 deletions

View file

@ -19,6 +19,32 @@
// Section: List Query Operations // Section: List Query Operations
// Function: is_homogeneous()
// Usage:
// is_homogeneous(list,depth)
// Description:
// Returns true when the list have elements of same type up to the depth `depth`.
// Booleans and numbers are not distinguinshed as of distinct types.
// Arguments:
// list - the list to check
// depth - the lowest level the check is done
// Example:
// is_homogeneous( [[1,["a"]], [2,["b"]]] ) // Returns true
// is_homogeneous( [[1,["a"]], [2,[true]]] ) // Returns false
// is_homogeneous( [[1,["a"]], [2,[true]]], 1 ) // Returns true
// is_homogeneous( [[1,["a"]], [2,[true]]], 2 ) // Returns false
// is_homogeneous( [[1,["a"]], [true,["b"]]] ) // Returns true
function is_homogeneous(l, depth) =
!is_list(l) || l==[] ? false :
let( l0=l[0] )
[] == [for(i=[1:len(l)-1]) if( ! _same_type(l[i],l0, depth+1) ) 0 ];
function _same_type(a,b, depth) =
(depth==0) || (a>=b) || (a==b) || (a<=b)
|| ( is_list(a) && is_list(b) && len(a)==len(b)
&& []==[for(i=idx(a)) if( ! _same_type(a[i],b[i],depth-1) )0] );
// Function: select() // Function: select()
// Description: // Description:
// Returns a portion of a list, wrapping around past the beginning, if end<start. // Returns a portion of a list, wrapping around past the beginning, if end<start.
@ -597,6 +623,23 @@ function list_fit(array, length, fill) =
// Section: List Shuffling and Sorting // Section: List Shuffling and Sorting
// returns true for valid index specifications idx in the interval [imin, imax)
// note that idx can't have any value greater or EQUAL to imax
// this allows imax=INF as a bound to numerical lists
function _valid_idx(idx,imin,imax) =
is_undef(idx)
|| ( is_finite(idx)
&& ( is_undef(imin) || idx>=imin )
&& ( is_undef(imax) || idx< imax ) )
|| ( is_list(idx)
&& ( is_undef(imin) || min(idx)>=imin )
&& ( is_undef(imax) || max(idx)< imax ) )
|| ( is_range(idx)
&& ( is_undef(imin) || (idx[1]>0 && idx[0]>=imin ) || (idx[1]<0 && idx[0]<=imax ) )
&& ( is_undef(imax) || (idx[1]>0 && idx[2]<=imax ) || (idx[1]<0 && idx[2]>=imin ) ) );
// Function: shuffle() // Function: shuffle()
// Description: // Description:
// Shuffles the input list into random order. // Shuffles the input list into random order.
@ -611,7 +654,8 @@ function shuffle(list) =
concat(shuffle(left), shuffle(right)); concat(shuffle(left), shuffle(right));
// Sort a vector of scalar values // Sort a vector of scalar values with the native comparison operator
// all elements should have the same type.
function _sort_scalars(arr) = function _sort_scalars(arr) =
len(arr)<=1 ? arr : len(arr)<=1 ? arr :
let( let(
@ -623,105 +667,67 @@ function _sort_scalars(arr) =
concat( _sort_scalars(lesser), equal, _sort_scalars(greater) ); concat( _sort_scalars(lesser), equal, _sort_scalars(greater) );
// Sort a vector of vectors based on the first entry only of each vector // lexical sort of a homogeneous list of vectors
function _sort_vectors1(arr) = // uses native comparison operator
len(arr)<=1 ? arr : function _sort_vectors(arr, _i=0) =
!(len(arr)>0) ? [] : len(arr)<=1 || _i>=len(arr[0]) ? arr :
let( let(
pivot = arr[floor(len(arr)/2)], pivot = arr[floor(len(arr)/2)][_i],
lesser = [ for (y = arr) if (y[0] < pivot[0]) y ], lesser = [ for (entry=arr) if (entry[_i] < pivot ) entry ],
equal = [ for (y = arr) if (y[0] == pivot[0]) y ], equal = [ for (entry=arr) if (entry[_i] == pivot ) entry ],
greater = [ for (y = arr) if (y[0] > pivot[0]) y ] greater = [ for (entry=arr) if (entry[_i] > pivot ) entry ]
) )
concat( _sort_vectors1(lesser), equal, _sort_vectors1(greater) ); concat(
_sort_vectors(lesser, _i ),
_sort_vectors(equal, _i+1 ),
_sort_vectors(greater, _i ) );
// lexical sort of a homogeneous list of vectors by the vector components with indices in idxlist
// Sort a vector of vectors based on the first two entries of each vector // all idxlist indices should be in the range of the vector dimensions
// Lexicographic order, remaining entries of vector ignored // idxlist must be undef or a simple list of numbers
function _sort_vectors2(arr) = // uses native comparison operator
len(arr)<=1 ? arr : function _sort_vectors(arr, idxlist, _i=0) =
!(len(arr)>0) ? [] : len(arr)<=1 || ( is_list(idxlist) && _i>=len(idxlist) ) || _i>=len(arr[0]) ? arr :
let( let(
pivot = arr[floor(len(arr)/2)], k = is_list(idxlist) ? idxlist[_i] : _i,
lesser = [ for (y = arr) if (y[0] < pivot[0] || (y[0]==pivot[0] && y[1]<pivot[1])) y ], pivot = arr[floor(len(arr)/2)][k],
equal = [ for (y = arr) if (y[0] == pivot[0] && y[1]==pivot[1]) y ], lesser = [ for (entry=arr) if (entry[k] < pivot ) entry ],
greater = [ for (y = arr) if (y[0] > pivot[0] || (y[0]==pivot[0] && y[1]>pivot[1])) y ] equal = [ for (entry=arr) if (entry[k] == pivot ) entry ],
) greater = [ for (entry=arr) if (entry[k] > pivot ) entry ]
concat( _sort_vectors2(lesser), equal, _sort_vectors2(greater) ); )
concat(
_sort_vectors(lesser, idxlist, _i ),
_sort_vectors(equal, idxlist, _i+1),
_sort_vectors(greater, idxlist, _i ) );
// sorting using compare_vals(); returns indexed list when `indexed==true`
// Sort a vector of vectors based on the first three entries of each vector function _sort_general(arr, idx=undef, indexed=false) =
// Lexicographic order, remaining entries of vector ignored
function _sort_vectors3(arr) =
len(arr)<=1 ? arr : let(
pivot = arr[floor(len(arr)/2)],
lesser = [ for (y = arr)
if ( y[0] < pivot[0]
|| ( y[0]==pivot[0]
&& ( y[1]<pivot[1]
|| ( y[1]==pivot[1]
&& y[2]<pivot[2] ))))
y ],
equal = [ for (y = arr)
if ( y[0] == pivot[0]
&& y[1]== pivot[1]
&& y[2]==pivot[2] )
y ],
greater = [ for (y = arr)
if ( y[0] > pivot[0]
|| ( y[0]==pivot[0]
&& ( y[1] > pivot[1]
|| ( y[1]==pivot[1]
&& y[2] > pivot[2] ))))
y ]
) concat( _sort_vectors3(lesser), equal, _sort_vectors3(greater) );
// Sort a vector of vectors based on the first four entries of each vector
// Lexicographic order, remaining entries of vector ignored
function _sort_vectors4(arr) =
len(arr)<=1 ? arr : let(
pivot = arr[floor(len(arr)/2)],
lesser = [ for (y = arr)
if ( y[0] < pivot[0]
|| ( y[0]==pivot[0]
&& ( y[1]<pivot[1]
|| ( y[1]==pivot[1]
&& ( y[2]<pivot[2]
|| ( y[2]==pivot[2]
&& y[3]<pivot[3] ))))))
y ],
equal = [ for (y = arr)
if ( y[0] == pivot[0]
&& y[1] == pivot[1]
&& y[2] == pivot[2]
&& y[3] == pivot[3] )
y ],
greater = [ for (y = arr)
if ( y[0] > pivot[0]
|| ( y[0]==pivot[0]
&& ( y[1]>pivot[1]
|| ( y[1]==pivot[1]
&& ( y[2]>pivot[2]
|| ( y[2]==pivot[2]
&& y[3]>pivot[3] ))))))
y ]
) concat( _sort_vectors4(lesser), equal, _sort_vectors4(greater) );
// when idx==undef, returns the sorted array
// otherwise, returns the indices of the sorted array
function _sort_general(arr, idx=undef) =
(len(arr)<=1) ? arr : (len(arr)<=1) ? arr :
is_undef(idx) ! indexed && is_undef(idx)
? _sort_scalar(arr) ? _lexical_sort(arr)
: let( arrind=[for(k=[0:len(arr)-1], ark=[arr[k]]) [ k, [for (i=idx) ark[i]] ] ] ) : let( arrind = _indexed_sort(enumerate(arr,idx)) )
_indexed_sort(arrind); indexed
? arrind
: [for(i=arrind) arr[i]];
// lexical sort using compare_vals()
function _lexical_sort(arr) =
arr==[] ? [] : len(arr)==1? arr :
let( pivot = arr[floor(len(arr)/2)] )
let(
lesser = [ for (entry=arr) if (compare_vals(entry, pivot) <0 ) entry ],
equal = [ for (entry=arr) if (compare_vals(entry, pivot)==0 ) entry ],
greater = [ for (entry=arr) if (compare_vals(entry, pivot) >0 ) entry ]
)
concat(_lexical_sort(lesser), equal, _lexical_sort(greater));
// given a list of pairs, return the first element of each pair of the list sorted by the second element of the pair // given a list of pairs, return the first element of each pair of the list sorted by the second element of the pair
// the sorting is done using compare_vals() // the sorting is done using compare_vals()
function _indexed_sort(arrind) = function _indexed_sort(arrind) =
arrind==[] ? [] : len(arrind)==1? [arrind[0][0]] : arrind==[] ? [] : len(arrind)==1? [arrind[0][0]] :
let( pivot = arrind[floor(len(arrind)/2)][1] ) let( pivot = arrind[floor(len(arrind)/2)][1] )
let( let(
lesser = [ for (entry=arrind) if (compare_vals(entry[1], pivot) <0 ) entry ], lesser = [ for (entry=arrind) if (compare_vals(entry[1], pivot) <0 ) entry ],
@ -731,52 +737,46 @@ function _indexed_sort(arrind) =
concat(_indexed_sort(lesser), equal, _indexed_sort(greater)); concat(_indexed_sort(lesser), equal, _indexed_sort(greater));
// returns true for valid index specifications idx in the interval [imin, imax)
// note that idx can't have any value greater or EQUAL to imax
function _valid_idx(idx,imin,imax) =
is_undef(idx)
|| ( is_finite(idx) && idx>=imin && idx< imax )
|| ( is_list(idx) && min(idx)>=imin && max(idx)< imax )
|| ( valid_range(idx) && idx[0]>=imin && idx[2]< imax );
// Function: sort() // Function: sort()
// Usage: // Usage:
// sort(list, [idx]) // sort(list, [idx])
// Description: // Description:
// Sorts the given list using `compare_vals()`, sorting in lexicographic order, with types ordered according to // Sorts the given list in lexicographic order. If the input is a homogeneous simple list or a homogeneous
// list of vectors (see function is_homogeneous), the sorting method uses the native comparison operator and is faster.
// When sorting non homogeneous list the elements are compared with `compare_vals`, with types ordered according to
// `undef < boolean < number < string < list`. Comparison of lists is recursive. // `undef < boolean < number < string < list`. Comparison of lists is recursive.
// If the list is a list of vectors whose length is from 1 to 4 and the `idx` parameter is not passed, then // When comparing vectors, homogeneous or not, the parameter `idx` may be used to select the components to compare.
// `sort` uses a much more efficient method for comparisons and will run much faster. In this case, all entries // Note that homogeneous lists of vectors may contain mixed types provided that for any two list elements
// in the data are compared using the native comparison operator, so comparisons between types will fail. // list[i] and list[j] satisfies type(list[i][k])==type(list[j][k]) for all k.
// Strings are allowed as any list element and are compared with the native operators although no substring
// comparison is possible.
// Arguments: // Arguments:
// list = The list to sort. // list = The list to sort.
// idx = If given, do the comparison based just on the specified index, range or list of indices. // idx = If given, do the comparison based just on the specified index, range or list of indices.
// Example: // Example:
// l = [45,2,16,37,8,3,9,23,89,12,34]; // // Homogeneous lists
// sorted = sort(l); // Returns [2,3,8,9,12,16,23,34,37,45,89] // l1 = [45,2,16,37,8,3,9,23,89,12,34];
function sort(list, idx=undef) = // sorted1 = sort(l1); // Returns [2,3,8,9,12,16,23,34,37,45,89]
// l2 = [["oat",0], ["cat",1], ["bat",3], ["bat",2], ["fat",3]];
// sorted2 = sort(l2); // Returns: [["bat",2],["bat",3],["cat",1],["fat",3],["oat",0]]
// // Non-homegenous list
// l3 = [[4,0],[7],[3,9],20,[4],[3,1],[8]];
// sorted3 = sort(l3); // Returns: [20,[3,1],[3,9],[4],[4,0],[7],[8]]
function sort(list, idx=undef) =
!is_list(list) || len(list)<=1 ? list : !is_list(list) || len(list)<=1 ? list :
is_def(idx) is_homogeneous(list,1)
? assert( _valid_idx(idx,0,len(list)) , "Invalid indices.") ? let(size = array_dim(list[0]))
let( sarr = _sort_general(list,idx) ) size==0 ? _sort_scalars(list)
[for(i=[0:len(sarr)-1]) list[sarr[i]] ] : len(size)!=1 ? _sort_general(list,idx)
: let(size = array_dim(list)) : is_undef(idx) ? _sort_vectors(list)
len(size)==1 ? _sort_scalars(list) : : assert( _valid_idx(idx) , "Invalid indices.")
len(size)==2 && size[1] <=4 _sort_vectors(list,[for(i=idx) i])
? ( : _sort_general(list,idx);
size[1]==0 ? list :
size[1]==1 ? _sort_vectors1(list) :
size[1]==2 ? _sort_vectors2(list) :
size[1]==3 ? _sort_vectors3(list)
/*size[1]==4*/ : _sort_vectors4(list)
)
: _sort_general(list);
// Function: sortidx() // Function: sortidx()
// Description: // Description:
// Given a list, calculates the sort order of the list, and returns // Given a list, sort it as function `sort()`, and returns
// a list of indexes into the original list in that sorted order. // a list of indexes into the original list in that sorted order.
// If you iterate the returned list in order, and use the list items // If you iterate the returned list in order, and use the list items
// to index into the original list, you will be iterating the original // to index into the original list, you will be iterating the original
@ -795,31 +795,28 @@ function sort(list, idx=undef) =
// idxs1 = sortidx(lst, idx=1); // Returns: [3,0,2,1] // idxs1 = sortidx(lst, idx=1); // Returns: [3,0,2,1]
// idxs2 = sortidx(lst, idx=0); // Returns: [1,2,0,3] // idxs2 = sortidx(lst, idx=0); // Returns: [1,2,0,3]
// idxs3 = sortidx(lst, idx=[1,3]); // Returns: [3,0,2,1] // idxs3 = sortidx(lst, idx=[1,3]); // Returns: [3,0,2,1]
function sortidx(list, idx=undef) = function sortidx(list, idx=undef) =
assert( is_list(list) || is_string(list) , "Invalid input to sort." ) !is_list(list) || len(list)<=1 ? list :
assert( _valid_idx(idx,0,len(list)) , "Invalid indices.") is_homogeneous(list,1)
list==[] ? [] : ? let(
let( size = array_dim(list[0]),
size = array_dim(list), aug = ! (size==0 || len(size)==1) ? 0 // for general sorting
aug = is_undef(idx) && (len(size) == 1 || (len(size) == 2 && size[1]<=4)) : [for(i=[0:len(list)-1]) concat(i,list[i])], // for scalar or vector sorting
? zip(list, list_range(len(list))) lidx = size==0? [1] : // scalar sorting
: 0 len(size)==1
) ? is_undef(idx) ? [for(i=[0:len(list[0])-1]) i+1] // vector sorting
is_undef(idx) && len(size) == 1? subindex(_sort_vectors1(aug),1) : : [for(i=idx) i+1] // vector sorting
is_undef(idx) && len(size) == 2 && size[1] <=4 : 0 // just to signal
? ( )
size[1]==0 ? list_range(len(arr)) : assert( ! ( size==0 && is_def(idx) ),
size[1]==1 ? subindex(_sort_vectors1(aug),1) : "The specification of `idx` is incompatible with scalar sorting." )
size[1]==2 ? subindex(_sort_vectors2(aug),2) : assert( _valid_idx(idx) , "Invalid indices." )
size[1]==3 ? subindex(_sort_vectors3(aug),3) lidx!=0
/*size[1]==4*/ : subindex(_sort_vectors4(aug),4) ? let( lsort = _sort_vectors(aug,lidx) )
) [for(li=lsort) li[0] ]
: // general case : _sort_general(list,idx,indexed=true)
_sort_general(list,idx); : _sort_general(list,idx,indexed=true);
// sort() does not accept strings but sortidx does; isn't inconsistent ?
// Function: unique() // Function: unique()
// Usage: // Usage:
@ -1241,16 +1238,22 @@ function full_flatten(l) = [for(a=l) if(is_list(a)) (each full_flatten(a)) else
// Internal. Not exposed. // Internal. Not exposed.
function _array_dim_recurse(v) = function _array_dim_recurse(v) =
!is_list(v[0]) !is_list(v[0])
? sum( [for(entry=v) is_list(entry) ? 1 : 0] ) == 0 ? [] : [undef] ? len( [for(entry=v) if(!is_list(entry)) 0] ) == 0 ? [] : [undef]
: let( : let(
firstlen = len(v[0]), firstlen = is_list(v[0]) ? len(v[0]): undef,
first = sum( [for(entry = v) len(entry) == firstlen ? 0 : 1] ) == 0 ? firstlen : undef, first = len( [for(entry = v) if(! is_list(entry) || (len(entry) != firstlen)) 0 ] ) == 0 ? firstlen : undef,
leveldown = flatten(v) leveldown = flatten(v)
) )
is_list(leveldown[0]) is_list(leveldown[0])
? concat([first],_array_dim_recurse(leveldown)) ? concat([first],_array_dim_recurse(leveldown))
: [first]; : [first];
function _array_dim_recurse(v) =
let( alen = [for(vi=v) is_list(vi) ? len(vi): -1] )
v==[] || max(alen)==-1 ? [] :
let( add = max(alen)!=min(alen) ? undef : alen[0] )
concat( add, _array_dim_recurse(flatten(v)));
// Function: array_dim() // Function: array_dim()
// Usage: // Usage:
@ -1281,6 +1284,8 @@ function array_dim(v, depth=undef) =
? len(v) ? len(v)
: let( dimlist = _array_dim_recurse(v)) : let( dimlist = _array_dim_recurse(v))
(depth > len(dimlist))? 0 : dimlist[depth-1] ; (depth > len(dimlist))? 0 : dimlist[depth-1] ;
// This function may return undef! // This function may return undef!

View file

@ -3,6 +3,16 @@ include <../std.scad>
// Section: List Query Operations // Section: List Query Operations
module test_is_homogeneous(){
assert(is_homogeneous([[1,["a"]], [2,["b"]]])==true);
assert(is_homogeneous([[1,["a"]], [2,[true]]])==false);
assert(is_homogeneous([[1,["a"]], [2,[true]]],1)==true);
assert(is_homogeneous([[1,["a"]], [2,[true]]],2)==false);
assert(is_homogeneous([[1,["a"]], [true,["b"]]])==true);
}
test_is_homogeneous();
module test_select() { module test_select() {
l = [3,4,5,6,7,8,9]; l = [3,4,5,6,7,8,9];
assert(select(l, 5, 6) == [8,9]); assert(select(l, 5, 6) == [8,9]);
@ -46,7 +56,6 @@ test_in_list();
module test_min_index() { module test_min_index() {
assert(min_index([5,3,9,6,2,7,8,2,1])==8); assert(min_index([5,3,9,6,2,7,8,2,1])==8);
assert(min_index([5,3,9,6,2,7,8,2,7],all=true)==[4,7]); assert(min_index([5,3,9,6,2,7,8,2,7],all=true)==[4,7]);
// assert(min_index([],all=true)==[]);
} }
test_min_index(); test_min_index();
@ -54,7 +63,6 @@ test_min_index();
module test_max_index() { module test_max_index() {
assert(max_index([5,3,9,6,2,7,8,9,1])==2); assert(max_index([5,3,9,6,2,7,8,9,1])==2);
assert(max_index([5,3,9,6,2,7,8,9,7],all=true)==[2,7]); assert(max_index([5,3,9,6,2,7,8,9,7],all=true)==[2,7]);
// assert(max_index([],all=true)==[]);
} }
test_max_index(); test_max_index();
@ -74,6 +82,7 @@ module test_list_decreasing() {
} }
test_list_decreasing(); test_list_decreasing();
// Section: Basic List Generation // Section: Basic List Generation
module test_repeat() { module test_repeat() {
@ -155,7 +164,6 @@ module test_list_remove() {
} }
test_list_remove(); test_list_remove();
module test_list_remove_values() { module test_list_remove_values() {
animals = ["bat", "cat", "rat", "dog", "bat", "rat"]; animals = ["bat", "cat", "rat", "dog", "bat", "rat"];
assert(list_remove_values(animals, "rat") == ["bat","cat","dog","bat","rat"]); assert(list_remove_values(animals, "rat") == ["bat","cat","dog","bat","rat"]);
@ -258,15 +266,24 @@ test_shuffle();
module test_sort() { module test_sort() {
assert(sort([7,3,9,4,3,1,8]) == [1,3,3,4,7,8,9]); assert(sort([7,3,9,4,3,1,8]) == [1,3,3,4,7,8,9]);
assert(sort(["cat", "oat", "sat", "bat", "vat", "rat", "pat", "mat", "fat", "hat", "eat"]) == ["bat", "cat", "eat", "fat", "hat", "mat", "oat", "pat", "rat", "sat", "vat"]); assert(sort([[4,0],[7],[3,9],20,[4],[3,1],[8]]) == [20,[3,1],[3,9],[4],[4,0],[7],[8]]);
assert(sort([[4,0],[7],[3,9],20,[4],[3,1],[8]],idx=1) == [[7],20,[4],[8],[4,0],[3,1],[3,9]]);
assert(sort([[8,6],[3,1],[9,2],[4,3],[3,4],[1,5],[8,0]]) == [[1,5],[3,1],[3,4],[4,3],[8,0],[8,6],[9,2]]);
assert(sort([[8,0],[3,1],[9,2],[4,3],[3,4],[1,5],[8,6]],idx=1) == [[8,0],[3,1],[9,2],[4,3],[3,4],[1,5],[8,6]]);
assert(sort(["cat", "oat", "sat", "bat", "vat", "rat", "pat", "mat", "fat", "hat", "eat"])
== ["bat", "cat", "eat", "fat", "hat", "mat", "oat", "pat", "rat", "sat", "vat"]);
assert(sort(enumerate([[2,3,4],[1,2,3],[2,4,3]]),idx=1)==[[1,[1,2,3]], [0,[2,3,4]], [2,[2,4,3]]]); assert(sort(enumerate([[2,3,4],[1,2,3],[2,4,3]]),idx=1)==[[1,[1,2,3]], [0,[2,3,4]], [2,[2,4,3]]]);
assert(sort([0,"1",[1,0],2,"a",[1]])== [0,2,"1","a",[1],[1,0]]);
assert(sort([["oat",0], ["cat",1], ["bat",3], ["bat",2], ["fat",3]])== [["bat",2],["bat",3],["cat",1],["fat",3],["oat",0]]);
} }
test_sort(); test_sort();
module test_sortidx() { module test_sortidx() {
lst1 = ["d","b","e","c"]; lst1 = ["da","bax","eaw","cav"];
assert(sortidx(lst1) == [1,3,0,2]); assert(sortidx(lst1) == [1,3,0,2]);
lst5 = [3,5,1,7];
assert(sortidx(lst5) == [2,0,1,3]);
lst2 = [ lst2 = [
["foo", 88, [0,0,1], false], ["foo", 88, [0,0,1], false],
["bar", 90, [0,1,0], true], ["bar", 90, [0,1,0], true],
@ -276,11 +293,22 @@ module test_sortidx() {
assert(sortidx(lst2, idx=1) == [3,0,2,1]); assert(sortidx(lst2, idx=1) == [3,0,2,1]);
assert(sortidx(lst2, idx=0) == [1,2,0,3]); assert(sortidx(lst2, idx=0) == [1,2,0,3]);
assert(sortidx(lst2, idx=[1,3]) == [3,0,2,1]); assert(sortidx(lst2, idx=[1,3]) == [3,0,2,1]);
lst3 = [[-4, 0, 0], [0, 0, -4], [0, -4, 0], [-4, 0, 0], [0, -4, 0], [0, 0, 4], [0, 0, -4], [0, 4, 0], [4, 0, 0], [0, 0, 4], [0, 4, 0], [4, 0, 0]]; lst3 = [[-4,0,0],[0,0,-4],[0,-4,0],[-4,0,0],[0,-4,0],[0,0,4],
[0,0,-4],[0,4,0],[4,0,0],[0,0,4],[0,4,0],[4,0,0]];
assert(sortidx(lst3)==[0,3,2,4,1,6,5,9,7,10,8,11]); assert(sortidx(lst3)==[0,3,2,4,1,6,5,9,7,10,8,11]);
assert(sortidx([[4,0],[7],[3,9],20,[4],[3,1],[8]]) == [3,5,2,4,0,1,6]);
assert(sortidx([[4,0],[7],[3,9],20,[4],[3,1],[8]],idx=1) == [1,3,4,6,0,5,2]);
lst4=[0,"1",[1,0],2,"a",[1]];
assert(sortidx(lst4)== [0,3,1,4,5,2]);
assert(sortidx(["cat","oat","sat","bat","vat","rat","pat","mat","fat","hat","eat"])
== [3,0,10,8,9,7,1,6,5,2,4]);
assert(sortidx([["oat",0], ["cat",1], ["bat",3], ["bat",2], ["fat",3]])== [3,2,1,4,0]);
assert(sortidx(["Belfry", "OpenScad", "Library", "Documentation"])==[0,3,2,1]);
assert(sortidx(["x",1,[],0,"abc",true])==[5,3,1,4,0,2]);
} }
test_sortidx(); test_sortidx();
module test_unique() { module test_unique() {
assert(unique([]) == []); assert(unique([]) == []);
assert(unique([8]) == [8]); assert(unique([8]) == [8]);
@ -336,10 +364,8 @@ module test_set_intersection() {
test_set_intersection(); test_set_intersection();
// Arrays // Arrays
module test_add_scalar() { module test_add_scalar() {
assert(add_scalar([1,2,3],3) == [4,5,6]); assert(add_scalar([1,2,3],3) == [4,5,6]);
assert(add_scalar([[1,2,3],[3,4,5]],3) == [[4,5,6],[6,7,8]]); assert(add_scalar([[1,2,3],[3,4,5]],3) == [[4,5,6],[6,7,8]]);
@ -473,6 +499,12 @@ module test_array_dim() {
assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0) == 2); assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0) == 2);
assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2) == 3); assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2) == 3);
assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9]]]) == [2,undef,3]); assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9]]]) == [2,undef,3]);
assert(array_dim([1,2,3,4,5,6,7,8,9]) == [9]);
assert(array_dim([[1],[2],[3],[4],[5],[6],[7],[8],[9]]) == [9,1]);
assert(array_dim([]) == [0]);
assert(array_dim([[]]) == [1,0]);
assert(array_dim([[],[]]) == [2,0]);
assert(array_dim([[],[1]]) == [2,undef]);
} }
test_array_dim(); test_array_dim();
@ -486,7 +518,4 @@ module test_transpose() {
test_transpose(); test_transpose();
cube();
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap