From bbe4ad1467633d0da4bd3fe60af96592af709ba9 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Sun, 30 Aug 2020 12:12:36 +0100 Subject: [PATCH] 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 --- arrays.scad | 311 +++++++++++++++++++++-------------------- tests/test_arrays.scad | 51 +++++-- 2 files changed, 198 insertions(+), 164 deletions(-) diff --git a/arrays.scad b/arrays.scad index a6d43d1..d012c94 100644 --- a/arrays.scad +++ b/arrays.scad @@ -19,6 +19,32 @@ // 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() // Description: // Returns a portion of a list, wrapping around past the beginning, if end=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() // Description: // Shuffles the input list into random order. @@ -611,7 +654,8 @@ function shuffle(list) = 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) = len(arr)<=1 ? arr : let( @@ -623,105 +667,67 @@ function _sort_scalars(arr) = concat( _sort_scalars(lesser), equal, _sort_scalars(greater) ); -// Sort a vector of vectors based on the first entry only of each vector -function _sort_vectors1(arr) = - len(arr)<=1 ? arr : - !(len(arr)>0) ? [] : +// lexical sort of a homogeneous list of vectors +// uses native comparison operator +function _sort_vectors(arr, _i=0) = + len(arr)<=1 || _i>=len(arr[0]) ? arr : let( - pivot = arr[floor(len(arr)/2)], - lesser = [ for (y = arr) if (y[0] < pivot[0]) y ], - equal = [ for (y = arr) if (y[0] == pivot[0]) y ], - greater = [ for (y = arr) if (y[0] > pivot[0]) y ] - ) - concat( _sort_vectors1(lesser), equal, _sort_vectors1(greater) ); + pivot = arr[floor(len(arr)/2)][_i], + lesser = [ for (entry=arr) if (entry[_i] < pivot ) entry ], + equal = [ for (entry=arr) if (entry[_i] == pivot ) entry ], + greater = [ for (entry=arr) if (entry[_i] > pivot ) entry ] + ) + concat( + _sort_vectors(lesser, _i ), + _sort_vectors(equal, _i+1 ), + _sort_vectors(greater, _i ) ); + - -// Sort a vector of vectors based on the first two entries of each vector -// Lexicographic order, remaining entries of vector ignored -function _sort_vectors2(arr) = - len(arr)<=1 ? arr : - !(len(arr)>0) ? [] : +// lexical sort of a homogeneous list of vectors by the vector components with indices in idxlist +// all idxlist indices should be in the range of the vector dimensions +// idxlist must be undef or a simple list of numbers +// uses native comparison operator +function _sort_vectors(arr, idxlist, _i=0) = + len(arr)<=1 || ( is_list(idxlist) && _i>=len(idxlist) ) || _i>=len(arr[0]) ? arr : let( - pivot = arr[floor(len(arr)/2)], - lesser = [ for (y = arr) if (y[0] < pivot[0] || (y[0]==pivot[0] && y[1] pivot[0] || (y[0]==pivot[0] && y[1]>pivot[1])) y ] - ) - concat( _sort_vectors2(lesser), equal, _sort_vectors2(greater) ); + k = is_list(idxlist) ? idxlist[_i] : _i, + pivot = arr[floor(len(arr)/2)][k], + lesser = [ for (entry=arr) if (entry[k] < pivot ) entry ], + equal = [ for (entry=arr) if (entry[k] == pivot ) entry ], + greater = [ for (entry=arr) if (entry[k] > pivot ) entry ] + ) + concat( + _sort_vectors(lesser, idxlist, _i ), + _sort_vectors(equal, idxlist, _i+1), + _sort_vectors(greater, idxlist, _i ) ); + - -// Sort a vector of vectors based on the first three entries of each vector -// 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[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[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) = +// sorting using compare_vals(); returns indexed list when `indexed==true` +function _sort_general(arr, idx=undef, indexed=false) = (len(arr)<=1) ? arr : - is_undef(idx) - ? _sort_scalar(arr) - : let( arrind=[for(k=[0:len(arr)-1], ark=[arr[k]]) [ k, [for (i=idx) ark[i]] ] ] ) - _indexed_sort(arrind); + ! indexed && is_undef(idx) + ? _lexical_sort(arr) + : let( arrind = _indexed_sort(enumerate(arr,idx)) ) + 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 // the sorting is done using compare_vals() 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( 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)); -// 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() // Usage: // sort(list, [idx]) // 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. -// If the list is a list of vectors whose length is from 1 to 4 and the `idx` parameter is not passed, then -// `sort` uses a much more efficient method for comparisons and will run much faster. In this case, all entries -// in the data are compared using the native comparison operator, so comparisons between types will fail. +// When comparing vectors, homogeneous or not, the parameter `idx` may be used to select the components to compare. +// Note that homogeneous lists of vectors may contain mixed types provided that for any two list elements +// 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: // list = The list to sort. // idx = If given, do the comparison based just on the specified index, range or list of indices. -// Example: -// l = [45,2,16,37,8,3,9,23,89,12,34]; -// sorted = sort(l); // Returns [2,3,8,9,12,16,23,34,37,45,89] -function sort(list, idx=undef) = +// Example: +// // Homogeneous lists +// l1 = [45,2,16,37,8,3,9,23,89,12,34]; +// 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_def(idx) - ? assert( _valid_idx(idx,0,len(list)) , "Invalid indices.") - let( sarr = _sort_general(list,idx) ) - [for(i=[0:len(sarr)-1]) list[sarr[i]] ] - : let(size = array_dim(list)) - len(size)==1 ? _sort_scalars(list) : - len(size)==2 && size[1] <=4 - ? ( - 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); - + is_homogeneous(list,1) + ? let(size = array_dim(list[0])) + size==0 ? _sort_scalars(list) + : len(size)!=1 ? _sort_general(list,idx) + : is_undef(idx) ? _sort_vectors(list) + : assert( _valid_idx(idx) , "Invalid indices.") + _sort_vectors(list,[for(i=idx) i]) + : _sort_general(list,idx); + // Function: sortidx() // 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. // 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 @@ -795,31 +795,28 @@ function sort(list, idx=undef) = // idxs1 = sortidx(lst, idx=1); // Returns: [3,0,2,1] // idxs2 = sortidx(lst, idx=0); // Returns: [1,2,0,3] // idxs3 = sortidx(lst, idx=[1,3]); // Returns: [3,0,2,1] -function sortidx(list, idx=undef) = - assert( is_list(list) || is_string(list) , "Invalid input to sort." ) - assert( _valid_idx(idx,0,len(list)) , "Invalid indices.") - list==[] ? [] : - let( - size = array_dim(list), - aug = is_undef(idx) && (len(size) == 1 || (len(size) == 2 && size[1]<=4)) - ? zip(list, list_range(len(list))) - : 0 - ) - is_undef(idx) && len(size) == 1? subindex(_sort_vectors1(aug),1) : - is_undef(idx) && len(size) == 2 && size[1] <=4 - ? ( - size[1]==0 ? list_range(len(arr)) : - size[1]==1 ? subindex(_sort_vectors1(aug),1) : - size[1]==2 ? subindex(_sort_vectors2(aug),2) : - size[1]==3 ? subindex(_sort_vectors3(aug),3) - /*size[1]==4*/ : subindex(_sort_vectors4(aug),4) - ) - : // general case - _sort_general(list,idx); - - -// sort() does not accept strings but sortidx does; isn't inconsistent ? - +function sortidx(list, idx=undef) = + !is_list(list) || len(list)<=1 ? list : + is_homogeneous(list,1) + ? let( + size = array_dim(list[0]), + aug = ! (size==0 || len(size)==1) ? 0 // for general sorting + : [for(i=[0:len(list)-1]) concat(i,list[i])], // for scalar or vector sorting + lidx = size==0? [1] : // scalar sorting + len(size)==1 + ? is_undef(idx) ? [for(i=[0:len(list[0])-1]) i+1] // vector sorting + : [for(i=idx) i+1] // vector sorting + : 0 // just to signal + ) + assert( ! ( size==0 && is_def(idx) ), + "The specification of `idx` is incompatible with scalar sorting." ) + assert( _valid_idx(idx) , "Invalid indices." ) + lidx!=0 + ? let( lsort = _sort_vectors(aug,lidx) ) + [for(li=lsort) li[0] ] + : _sort_general(list,idx,indexed=true) + : _sort_general(list,idx,indexed=true); + // Function: unique() // Usage: @@ -1241,16 +1238,22 @@ function full_flatten(l) = [for(a=l) if(is_list(a)) (each full_flatten(a)) else // Internal. Not exposed. function _array_dim_recurse(v) = !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( - firstlen = len(v[0]), - first = sum( [for(entry = v) len(entry) == firstlen ? 0 : 1] ) == 0 ? firstlen : undef, + firstlen = is_list(v[0]) ? len(v[0]): undef, + first = len( [for(entry = v) if(! is_list(entry) || (len(entry) != firstlen)) 0 ] ) == 0 ? firstlen : undef, leveldown = flatten(v) ) is_list(leveldown[0]) ? concat([first],_array_dim_recurse(leveldown)) : [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() // Usage: @@ -1281,6 +1284,8 @@ function array_dim(v, depth=undef) = ? len(v) : let( dimlist = _array_dim_recurse(v)) (depth > len(dimlist))? 0 : dimlist[depth-1] ; + + // This function may return undef! diff --git a/tests/test_arrays.scad b/tests/test_arrays.scad index 8df23a8..45cd2cd 100644 --- a/tests/test_arrays.scad +++ b/tests/test_arrays.scad @@ -3,6 +3,16 @@ include <../std.scad> // 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() { l = [3,4,5,6,7,8,9]; assert(select(l, 5, 6) == [8,9]); @@ -46,7 +56,6 @@ test_in_list(); 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,7],all=true)==[4,7]); -// assert(min_index([],all=true)==[]); } test_min_index(); @@ -54,7 +63,6 @@ test_min_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,7],all=true)==[2,7]); -// assert(max_index([],all=true)==[]); } test_max_index(); @@ -74,6 +82,7 @@ module test_list_decreasing() { } test_list_decreasing(); + // Section: Basic List Generation module test_repeat() { @@ -155,7 +164,6 @@ module test_list_remove() { } test_list_remove(); - module test_list_remove_values() { animals = ["bat", "cat", "rat", "dog", "bat", "rat"]; assert(list_remove_values(animals, "rat") == ["bat","cat","dog","bat","rat"]); @@ -258,15 +266,24 @@ test_shuffle(); module test_sort() { 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([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(); module test_sortidx() { - lst1 = ["d","b","e","c"]; + lst1 = ["da","bax","eaw","cav"]; assert(sortidx(lst1) == [1,3,0,2]); + lst5 = [3,5,1,7]; + assert(sortidx(lst5) == [2,0,1,3]); lst2 = [ ["foo", 88, [0,0,1], false], ["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=0) == [1,2,0,3]); 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([[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(); + module test_unique() { assert(unique([]) == []); assert(unique([8]) == [8]); @@ -336,10 +364,8 @@ module test_set_intersection() { test_set_intersection(); - // Arrays - module test_add_scalar() { 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]]); @@ -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]]], 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]) == [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(); @@ -486,7 +518,4 @@ module test_transpose() { test_transpose(); -cube(); - - // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap