Yes - the format (line items - rows - delimited by CR containing fields
- columns - delimited by tabs) is not a natural fit in Pick but is
pretty much everywhere else. Not surprisingly, my benchmarks showed
what you (and I) would have expected - that a Unix !sort was by far and
away the most efficient way to deal with it. I didn't get around to
trying CREATE.INDEX (and BUILD.INDEX on UniData) for the file in the
SSELECT test yet. Maybe someone else will have a go - although I'd be
surprised if that beat the !sort. Here are the results I got:
SORT.BENCHMARK
No. of Sort Initialisation Method 1 Method 2 Method 3
Method 4
Items Depth LOCATEs QuickSort SSELECT
UNIX !sort
1000 1 2 115 153 252
34
1000 2 2 141 165 231
34
1000 3 2 157 170 258
36
--------------------------------------------------------------------------------------------
1000 4 2 187 181 282
36
1000 5 2 207 231 255
39
2000 1 2 414 298 379
45
2000 2 3 508 324 420
43
2000 3 3 568 334 404
49
2000 4 3 683 402 440
50
2000 5 3 747 373 428
58
3000 1 4 903 498 604
60
3000 2 4 1231 553 685
85
3000 3 4 1267 526 611
59
3000 4 4 1499 556 633
65
3000 5 4 1664 577 1089
167
4000 1 6 1554 667 821
79
4000 2 5 1940 722 852
69
4000 3 5 2185 756 851
74
4000 4 6 2615 783 948
79
4000 5 5 2904 804 948
96
5000 1 6 48614 865 1115
117
5000 2 7 2993 946 1106
81
5000 3 6 3447 979 1143
88
5000 4 7 4163 984 1137
93
5000 5 6 4863 1042 1282
109
6000 1 7 3546 1115 1402
115
6000 2 7 4347 1159 1414
93
6000 3 7 4916 1233 1459
98
6000 4 8 6257 1253 1495
111
6000 5 8 6461 1287 1514
135
7000 1 8 4866 1192 1791
133
7000 2 8 6056 1423 1757
110
7000 3 8 6587 1362 1873
155
7000 4 9 8085 1416 1762
120
7000 5 9 9479 1453 1822
144
8000 1 9 6281 1440 2347
151
8000 2 10 7463 1640 2030
113
8000 3 9 8898 1623 2167
123
8000 4 10 10196 1746 2090
132
8000 5 10 11695 1765 2321
156
9000 1 10 8189 1695 2434
166
9000 2 11 9807 1807 2411
126
9000 3 11 10773 1874 2581
137
9000 4 11 19348 2005 2540
146
9000 5 10 14811 1973 2569
175
10000 1 12 9748 1847 2782
263
10000 2 12 11545 2029 2807
134
10000 3 12 13153 2040 2886
146
10000 4 12 16000 2171 2930
154
10000 5 12 18282 2265 3081
190
The very first line here shows that with just 1000 items to sort - by
just a single column - the UNIX !sort was already the most efficient
method. By the time it got to sorting 10,000 items by 5 levels (BY COL1
BY COL2 BY COL3 BY COL4 BY COL5) it was somewhere around 100 times as
quick. QuickSort stood up pretty well in that it turned out to be
quicker than a SSELECT up to the limits of my tests.
Here's the code I used:
Note that it was written and tested on UniData - so expect to have to
make changes if you want to compile and run it on another Pick flavour.
Note also the technique I used to use LOCATEs down to 3 levels below
subvalue level. I welcome suggestions for improvements generally and
this might be one of many areas that can be improved upon. The modified
version of QUICK.SORT allows the dimensioned array to be sorted by any
or all of up to 6 columns (where each element represents a line
containing up to 6 line/column cells). The code used to create the OS
sequential file for the !sort is also specifically UniBasic and would
have to be modified according to MV/Pick flavour.
Oh.. and those of you that like to criticise what they see as bad code
or poor programming practice - don't hold back. Let me have it. We can
all learn and improve, except that if you're one of those that can't
stand the use of EXIT and CONTINUE in loops - I'm sorry to upset you
but I like them. The main point of this thread is so that we can end up
with the most efficient mechanism to sort lists. I realise the type of
lists I'm dealing with here are not the usual Pick structure - in that
I have up to 6 multivalues in a larger number of attributes, rather
than up to 6 attributes each containing a larger number of multivalues
- but I hope to get on to that next.
BP: SORT.BENCHMARK
===================
* This constructs the test array and calls the subroutine to test the
various sort methods:
*
* Construct a dynamic array containing 10,000 lines.
*
* Each line to have 5 multivalues.
* MV 1, representing column 1, will be a signed integer in the range
-9999 to 9999
* Column 2 will be an unsigned integer in the range 0 to 9999
* Column 3 will be a signed number in the range -9999.9999 to 9999.9999
* Column 4 will be an unsigned number in the range 0.1 to 9999.9999
* Column 5 will be a string of between 1 and 5 printable characters
*
POSNEG='-'
UNSORTED.ARRAY=''
DIM UNSORTED.MAT(10000)
MAT UNSORTED.MAT=''
FOR X=1 TO 10000
SIGN=RND(2)+1 ;* 1=negative / 2=positive (no sign)
UNSORTED.MAT(X)<1,1>=POSNEG[SIGN,1]:RND(10000) ;* Signed integer
UNSORTED.MAT(X)<1,2>=POSNEG[SIGN,1]:RND(10000):'.':RND(9999)+1 ;*
Signed numeric
UNSORTED.MAT(X)<1,3>=RND(10000) ;* Unsigned integer
UNSORTED.MAT(X)<1,4>=RND(10000):'.':RND(9999)+1 ;* Unsigned numeric
CHCNT=RND(5)+1 ;* Number of characters (1-5)
FOR Y=1 TO CHCNT
UNSORTED.MAT(X)<1,5>:=CHAR(32+RND(94)) ;* ASCII 32-125 (printable
chars)
NEXT Y
NEXT X
*
SORT.TYPES='AR'
SORT.TYPES<2>='DR'
SORT.TYPES<3>='AR'
SORT.TYPES<4>='DR'
SORT.TYPES<5>='AL'
* Note that some of the above codes will vary according to MV flavour -
* D3 supports ARS & DRS for sorting numerics with signs and/or decimals
* UV supports AN & DN (ditto)
*
CRT 'No. of Sort Initialisation Method 1 Method 2 Method 3
Method 4'
CRT 'Items Depth LOCATEs QuickSort SSELECT
UNIX !sort'
CRT
FOR ARRAY.SIZE=1000 TO 10000 STEP 1000
MATBUILD UNSORTED.ARRAY FROM UNSORTED.MAT,1,ARRAY.SIZE
COLS.TO.SORT.BY=''
SORT.BY=''
*
* Tests to be run for sorting by 1 column...
* then by column 1 by column 2...
* then by column 1 by column 2 by column 3...
etc.
*
FOR NO.OF.COLS.TO.SORT.BY=1 TO 5
COLS.TO.SORT.BY<1,NO.OF.COLS.TO.SORT.BY>=NO.OF.COL S.TO.SORT.BY
SORT.BY<1,NO.OF.COLS.TO.SORT.BY>=SORT.TYPES<NO.OF. COLS.TO.SORT.BY>
CALL TEST.SORT.METHODS(UNSORTED.ARRAY, COLS.TO.SORT.BY, SORT.BY,
SORTED.ARRAY)
NEXT NO.OF.COLS.TO.SORT.BY
NEXT ARRAY.SIZE
STOP
BP: TEST.SORT.METHODS
=====================
SUBROUTINE TEST.SORT.METHODS(UNSORTED.ARRAY, COLS.TO.SORT.BY, SORT.BY,
SORTED.ARRAY)
* UNSORTED.ARRAY is a dynamic array in which each attribute represents
a line and each multivalue a column/line cell
* COLS.TO.SORT.BY is a multivalued list of the columns UNSORTED.ARRAY
is to be sorted by
* - so to sort by column 4 by column 2 by column 3, it would be 4]2]3
* SORT.BY is an associated multivalued list of the type of sort for
each column (eg. AR]AL]AR)
* SORTED.ARRAY is the resultant sorted dynamic array to be returned
CHECKPOINT.0=SYSTEM(12)+1
*
SORTED.ARRAY=''
*
NO.OF.COLS=DCOUNT(COLS.TO.SORT.BY,@VM)
IF NO.OF.COLS>6 THEN
CRT 'This routine can only cope with sorts down to 6 levels. Sorry.'
DEBUG
RETURN
END
NO.OF.TYPES=DCOUNT(SORT.BY,@VM)
IF NO.OF.COLS#NO.OF.TYPES THEN
CRT 'Number of COLS.TO.SORT.BY (':NO.OF.COLS:') differs from Number of
SORT.BY (':NO.OF.TYPES:')'
DEBUG
RETURN
END
*
LINE.COUNT=DCOUNT(UNSORTED.ARRAY,@AM)
DIM UNSORTED.MAT(LINE.COUNT)
MATPARSE UNSORTED.MAT FROM UNSORTED.ARRAY,@AM
*
REFERENCE.ARRAY=''
*
DIM WORK.MAT(6)
MAT WORK.MAT=''
*************************
*
*
EXECUTE 'UDT.OPTIONS 85 ON' CAPTURING CRTOUT
CHECKPOINT.1=SYSTEM(12) ;* Time in milliseconds
CRT LINE.COUNT 'R#6':NO.OF.COLS 'R#8'

CHECKPOINT.1-CHECKPOINT.0+1)
'R#17':
*
* Method 1 : Nested LOCATE...BY...ELSE...INSERT construct.
*
FOR LC=1 TO LINE.COUNT
ARRAY.LINE=UNSORTED.MAT(LC)
FOR COL.NO=1 TO NO.OF.COLS
FIELD.DATA=ARRAY.LINE<1,COLS.TO.SORT.BY<1,COL.NO>>
TYPE.OF.SORT=SORT.BY<1,COL.NO>
IF COL.NO=NO.OF.COLS THEN
MUST.INSERT=1
END ELSE
MUST.INSERT=0
END
NEW.FIELD=0
BEGIN CASE
CASE COL.NO=1
* LOCATE FIELD.DATA IN WORK.MAT(1),1 BY TYPE.OF.SORT SETTING AMPOS
ELSE NEW.FIELD=1
*** UniData version of LOCATE in use - change as reqd
LOCATE FIELD.DATA IN WORK.MAT(1)<1> BY TYPE.OF.SORT SETTING AMPOS
ELSE NEW.FIELD=1
IF MUST.INSERT OR NEW.FIELD THEN
INS FIELD.DATA BEFORE WORK.MAT(1)<AMPOS>
FOR OTHER.COLS=2 TO NO.OF.COLS
INS ARRAY.LINE<1,OTHER.COLS> BEFORE WORK.MAT(OTHER.COLS)<AMPOS>
NEXT OTHER.COLS
INS LC BEFORE REFERENCE.ARRAY<AMPOS>
EXIT
END
CASE COL.NO=2
* LOCATE FIELD.DATA IN WORK.MAT(2)<AMPOS>,1 BY TYPE.OF.SORT SETTING
VMPOS ELSE NEW.FIELD=1
*** UniData version of LOCATE in use - change as reqd
LOCATE FIELD.DATA IN WORK.MAT(2)<AMPOS,1> BY TYPE.OF.SORT SETTING
VMPOS ELSE NEW.FIELD=1
IF MUST.INSERT OR NEW.FIELD THEN
INS FIELD.DATA BEFORE WORK.MAT(2)<AMPOS,VMPOS>
FOR OTHER.COLS=3 TO NO.OF.COLS
INS ARRAY.LINE<1,OTHER.COLS> BEFORE
WORK.MAT(OTHER.COLS)<AMPOS,VMPOS>
NEXT OTHER.COLS
INS LC BEFORE REFERENCE.ARRAY<AMPOS,VMPOS>
EXIT
END
CASE COL.NO=3
* LOCATE FIELD.DATA IN WORK.MAT(3)<AMPOS,VMPOS>,1 BY TYPE.OF.SORT
SETTING SVMPOS ELSE NEW.FIELD=1
*** UniData version of LOCATE in use - change as reqd
LOCATE FIELD.DATA IN WORK.MAT(3)<AMPOS,VMPOS,1> BY TYPE.OF.SORT
SETTING SVMPOS ELSE NEW.FIELD=1
IF MUST.INSERT OR NEW.FIELD THEN
INS FIELD.DATA BEFORE WORK.MAT(3)<AMPOS,VMPOS,SVMPOS>
FOR OTHER.COLS=4 TO NO.OF.COLS
INS ARRAY.LINE<1,OTHER.COLS> BEFORE
WORK.MAT(OTHER.COLS)<AMPOS,VMPOS,SVMPOS>
NEXT OTHER.COLS
INS LC BEFORE REFERENCE.ARRAY<AMPOS,VMPOS,SVMPOS>
EXIT
END
CASE COL.NO=4
UNPACKED=CONVERT(CHAR(251),@AM,WORK.MAT(4)<AMPOS,V MPOS,SVMPOS>)
* LOCATE FIELD.DATA IN UNPACKED,1 BY TYPE.OF.SORT SETTING POS251 ELSE
NEW.FIELD=1
*** UniData version of LOCATE in use - change as reqd
LOCATE FIELD.DATA IN UNPACKED<1> BY TYPE.OF.SORT SETTING POS251 ELSE
NEW.FIELD=1
IF MUST.INSERT OR NEW.FIELD THEN
INS FIELD.DATA BEFORE UNPACKED<POS251>
WORK.MAT(4)<AMPOS,VMPOS,SVMPOS>=CONVERT(@AM,CHAR(2 51),UNPACKED)
FOR OTHER.COLS=5 TO NO.OF.COLS
UNPACKED=CONVERT(CHAR(251),@AM,WORK.MAT(OTHER.COLS )<AMPOS,VMPOS,SVMPOS>)
INS ARRAY.LINE<1,OTHER.COLS> BEFORE UNPACKED<POS251>
WORK.MAT(OTHER.COLS)<AMPOS,VMPOS,SVMPOS>=CONVERT(@ AM,CHAR(251),UNPACKED)
NEXT OTHER.COLS
UNPACKED=CONVERT(CHAR(251),@AM,REFERENCE.ARRAY<AMP OS,VMPOS,SVMPOS>)
INS LC BEFORE UNPACKED<POS251>
REFERENCE.ARRAY<AMPOS,VMPOS,SVMPOS>=CONVERT(@AM,CH AR(251),UNPACKED)
EXIT
END
UNPACKED=CONVERT(CHAR(251):CHAR(250),@AM:@VM,WORK. MAT(5)<AMPOS,VMPOS,SVMPOS>)
* LOCATE FIELD.DATA IN UNPACKED<POS251>,1 BY TYPE.OF.SORT SETTING
POS250 ELSE NEW.FIELD=1
*** UniData version of LOCATE in use - change as reqd
LOCATE FIELD.DATA IN UNPACKED<POS251,1> BY TYPE.OF.SORT SETTING
POS250 ELSE NEW.FIELD=1
IF MUST.INSERT OR NEW.FIELD THEN
INS FIELD.DATA BEFORE UNPACKED<POS251,POS250>
WORK.MAT(5)<AMPOS,VMPOS,SVMPOS>=CONVERT(@AM:@VM,CH AR(251):CHAR(250),UNPACKED)
FOR OTHER.COLS=6 TO NO.OF.COLS
UNPACKED=CONVERT(CHAR(251):CHAR(250),@AM:@VM,WORK. MAT(OTHER.COLS)<AMPOS,VMPOS,SVMPOS>)
INS ARRAY.LINE<1,OTHER.COLS> BEFORE UNPACKED<POS251,POS250>
WORK.MAT(OTHER.COLS)<AMPOS,VMPOS,SVMPOS>=CONVERT(@ AM:@VM,CHAR(251):CHAR(250),UNPACKED)
NEXT OTHER.COLS
UNPACKED=CONVERT(CHAR(251):CHAR(250),@AM:@VM,REFER ENCE.ARRAY<AMPOS,VMPOS,SVMPOS>)
INS LC BEFORE UNPACKED<POS251,POS250>
REFERENCE.ARRAY<AMPOS,VMPOS,SVMPOS>=CONVERT(@AM:@V M,CHAR(251):CHAR(250),UNPACKED)
EXIT
END
CASE COL.NO=6
UNPACKED=CONVERT(CHAR(251):CHAR(250):CHAR(249),@AM :@VM:@SVM,WORK.MAT(6)<AMPOS,VMPOS,SVMPOS>)
* LOCATE FIELD.DATA IN UNPACKED<POS251,POS250>,1 BY TYPE.OF.SORT
SETTING POS249 ELSE NEW.FIELD=1
*** UniData version of LOCATE in use - change as reqd
LOCATE FIELD.DATA IN UNPACKED<POS251,POS250,1> BY TYPE.OF.SORT
SETTING POS249 ELSE NEW.FIELD=1
IF MUST.INSERT OR NEW.FIELD THEN
INS FIELD.DATA BEFORE UNPACKED<POS251,POS250,POS249>
WORK.MAT(6)<AMPOS,VMPOS,SVMPOS>=CONVERT(@AM:@VM:@S VM,CHAR(251):CHAR(250):CHAR(249),UNPACKED)
UNPACKED=CONVERT(CHAR(251):CHAR(250):CHAR(249),@AM :@VM:@SVM,REFERENCE.ARRAY<AMPOS,VMPOS,SVMPOS>)
INS LC BEFORE UNPACKED<POS251,POS250,POS249>
REFERENCE.ARRAY<AMPOS,VMPOS,SVMPOS>=CONVERT(@AM:@V M:@SVM,CHAR(251):CHAR(250):CHAR(249),UNPACKED)
END
END CASE
NEXT COL.NO
NEXT LC
*
*
LOOP
REMOVE REF FROM REFERENCE.ARRAY SETTING MORE.DATA
SORTED.ARRAY<-1>=UNSORTED.MAT(REF)
WHILE MORE.DATA DO REPEAT
*
CRT (SYSTEM(12)-CHECKPOINT.1+1) 'R#11':
SORTED.ARRAY1=SORTED.ARRAY
CHECKPOINT.2=SYSTEM(12)
REFERENCE.ARRAY=''
DIM SORT.MAT(LINE.COUNT)
MAT SORT.MAT=MAT UNSORTED.MAT
*
* Method 2 : QuickSort
*
CALL QUICK.SORT.ASSOC(MAT SORT.MAT, COLS.TO.SORT.BY, 1, LINE.COUNT,
SORT.BY)
MATBUILD SORTED.ARRAY FROM SORT.MAT
CHECKPOINT.3=SYSTEM(12)
*
CRT (CHECKPOINT.3-CHECKPOINT.2+1) 'R#12':
CHECKPOINT.3=SYSTEM(12)
*
MODULO=INT(LEN(UNSORTED.ARRAY)/3000)
IF MODULO>LINE.COUNT THEN MODULO=LINE.COUNT
LOOP WHILE INT(MODULO/2)=MODULO/2 OR INT(MODULO/5)=MODULO/5 DO
MODULO-=1
REPEAT
FNAME='SORTWORK':@USERNO
PERFORM 'CREATE-FILE ':FNAME:' ':MODULO CAPTURING CRTOUT
OPEN 'DICT',FNAME TO FDICT ELSE DEBUG
OPEN FNAME TO FDATA ELSE DEBUG
CLEARFILE FDATA
*
* Method 3 : SSELECT
*
DELIM=''
FOR CH=126 TO 33 STEP -1
IF NOT(INDEX(UNSORTED.ARRAY,CHAR(CH),1)) THEN
DELIM=CHAR(CH)
EXIT
END
NEXT CH
IF DELIM='' THEN DEBUG
FOR X=1 TO LINE.COUNT
WRITE '' ON FDATA,CONVERT(@VM,DELIM,UNSORTED.MAT(X))
NEXT X
D='V'
CMD='SSELECT ':FNAME
FOR COL.NO=1 TO NO.OF.COLS
D<2>="FIELD(@ID,'"

ELIM:"',":COL.NO:")"
D<4>="Column ":COL.NO
D<5>='10':SORT.BY<1,COL.NO>[2,1]
WRITE D ON FDICT,'F':COL.NO
IF SORT.BY<1,COL.NO>[1,1]='A' THEN
CMD:=' BY'
END ELSE
CMD:=' BY-DSND'
END
CMD:=' F':COL.NO
NEXT COL.NO
* Note : this syntax is correct for UniData - check for other MV
flavours
EXECUTE CMD CAPTURING CRTOUT
READLIST SORTED.ARRAY ELSE DEBUG
SORTED.ARRAY=CONVERT(DELIM,@VM,SORTED.ARRAY)
CRT (SYSTEM(12)-CHECKPOINT.3+1) 'R#11':
CHECKPOINT.4=SYSTEM(12)
*
* Method 4 : Unix sort
*
PERFORM 'CREATE.FILE DIR SORTDIR' CAPTURING CRTOUT
OSOPEN 'SORTDIR/UN':FNAME TO FVAR ELSE
OSWRITE '' ON 'SORTDIR/UN':FNAME
OSOPEN 'SORTDIR/UN':FNAME TO FVAR ELSE DEBUG
END
OSBWRITE CONVERT(@VM:@AM,CHAR(9):CHAR(10),UNSORTED.ARRAY) ON FVAR AT 0
OSCLOSE FVAR
CMD='sort -o SORTDIR/':FNAME
FOR COL.NO=1 TO NO.OF.COLS
CMD:=' -k':COLS.TO.SORT.BY<1,COL.NO>:',':COLS.TO.SORT.BY<1 ,COL.NO>
IF SORT.BY<1,COL.NO>[1,1]='D' THEN CMD:='r'
IF SORT.BY<1,COL.NO>[2,1]='R' THEN CMD:='n'
NEXT COL.NO
CMD:=' SORTDIR/UN':FNAME
PCPERFORM CMD CAPTURING CRTOUT
OSOPEN 'SORTDIR/':FNAME TO FVAR ELSE DEBUG
OSBREAD SORTED.ARRAY FROM FVAR AT 0 LENGTH LEN(UNSORTED.ARRAY)
OSCLOSE FVAR
OSDELETE 'SORTDIR/UN':FNAME
OSDELETE 'SORTDIR/':FNAME
SORTED.ARRAY=CONVERT(CHAR(9):CHAR(10),@VM:@AM,SORT ED.ARRAY)
CHECKPOINT.5=SYSTEM(12)
CRT (CHECKPOINT.5-CHECKPOINT.4+1) 'R#13'
RETURN
BP: QUICK.SORT.ASSOC
====================
SUBROUTINE QUICK.SORT.ASSOC(MAT LIST, COLS.TO.SORT.BY, SORT.FROM,
SORT.TO, SORT.BY)
*// quicksort using dim'd array
*// LIST(n) dimensioned list of elements to be sorted
*// COLS.TO.SORT.BY mulivalue nos. within elements to be sorted
*// SORT.FROM sort from this element number - usually 1
*// SORT.TO sort to this element number - usually n
*// SORT.BY 'AR' or 'DR'
DIM LIST(32000)
LOW.POINT = SORT.FROM
HIGH.POINT = SORT.TO
MID.POINT=INT((SORT.FROM+SORT.TO)/2)
MID.VALUES=''
NO.OF.COLS=DCOUNT(COLS.TO.SORT.BY,@VM)
FOR COL.NO=1 TO NO.OF.COLS
MID.VALUES<1,-1>=LIST(MID.POINT)<1,COLS.TO.SORT.BY<1,COL.NO>>
NEXT COL.NO
LOOP
LOOP
IF LOW.POINT >= SORT.TO THEN EXIT
INCREMENT.LOW=0
FOR COL.NO=1 TO NO.OF.COLS
IF MID.VALUES<1,COL.NO>=LIST(LOW.POINT)<1,COL.NO> THEN CONTINUE
IF SORT.BY<1,COL.NO>[1,1]='A' THEN
IF MID.VALUES<1,COL.NO> > LIST(LOW.POINT)<1,COL.NO> THEN
INCREMENT.LOW=1
END
END ELSE
IF MID.VALUES<1,COL.NO> < LIST(LOW.POINT)<1,COL.NO> THEN
INCREMENT.LOW=1
END
END
EXIT
NEXT COL.NO
IF INCREMENT.LOW THEN
LOW.POINT+=1
END ELSE
EXIT
END
REPEAT
LOOP
IF HIGH.POINT <= SORT.FROM THEN EXIT
DECREMENT.HIGH=0
FOR COL.NO=1 TO NO.OF.COLS
IF MID.VALUES<1,COL.NO>=LIST(HIGH.POINT)<1,COL.NO> THEN CONTINUE
IF SORT.BY<1,COL.NO>[1,1]='A' THEN
IF MID.VALUES<1,COL.NO> < LIST(HIGH.POINT)<1,COL.NO> THEN
DECREMENT.HIGH=1
END
END ELSE
IF MID.VALUES<1,COL.NO> > LIST(HIGH.POINT)<1,COL.NO> THEN
DECREMENT.HIGH=1
END
END
EXIT
NEXT COL.NO
IF DECREMENT.HIGH THEN
HIGH.POINT-=1
END ELSE
EXIT
END
REPEAT
IF (LOW.POINT < HIGH.POINT) THEN ;*// swap elements if required
SWAP.VALUE = LIST(LOW.POINT)
LIST(LOW.POINT) = LIST(HIGH.POINT)
LIST(HIGH.POINT) = SWAP.VALUE
END
IF (LOW.POINT <= HIGH.POINT) THEN
LOW.POINT += 1
HIGH.POINT -= 1
END
WHILE (LOW.POINT <= HIGH.POINT) DO REPEAT
*// sort remainder of the elements
IF (SORT.FROM < HIGH.POINT) THEN
CALL QUICK.SORT.ASSOC(MAT LIST, COLS.TO.SORT.BY, SORT.FROM,
HIGH.POINT, SORT.BY)
END
IF (LOW.POINT < SORT.TO) THEN
CALL QUICK.SORT.ASSOC(MAT LIST, COLS.TO.SORT.BY, LOW.POINT, SORT.TO,
SORT.BY)
END
RETURN
END