OpenLibm/slatec/spsort.f
Viral B. Shah c977aa998f Add Makefile.extras to build libopenlibm-extras.
Replace amos with slatec
2012-12-31 16:37:05 -05:00

268 lines
7.8 KiB
Fortran

*DECK SPSORT
SUBROUTINE SPSORT (X, N, IPERM, KFLAG, IER)
C***BEGIN PROLOGUE SPSORT
C***PURPOSE Return the permutation vector generated by sorting a given
C array and, optionally, rearrange the elements of the array.
C The array may be sorted in increasing or decreasing order.
C A slightly modified quicksort algorithm is used.
C***LIBRARY SLATEC
C***CATEGORY N6A1B, N6A2B
C***TYPE SINGLE PRECISION (SPSORT-S, DPSORT-D, IPSORT-I, HPSORT-H)
C***KEYWORDS NUMBER SORTING, PASSIVE SORTING, SINGLETON QUICKSORT, SORT
C***AUTHOR Jones, R. E., (SNLA)
C Rhoads, G. S., (NBS)
C Wisniewski, J. A., (SNLA)
C***DESCRIPTION
C
C SPSORT returns the permutation vector IPERM generated by sorting
C the array X and, optionally, rearranges the values in X. X may
C be sorted in increasing or decreasing order. A slightly modified
C quicksort algorithm is used.
C
C IPERM is such that X(IPERM(I)) is the Ith value in the rearrangement
C of X. IPERM may be applied to another array by calling IPPERM,
C SPPERM, DPPERM or HPPERM.
C
C The main difference between SPSORT and its active sorting equivalent
C SSORT is that the data are referenced indirectly rather than
C directly. Therefore, SPSORT should require approximately twice as
C long to execute as SSORT. However, SPSORT is more general.
C
C Description of Parameters
C X - input/output -- real array of values to be sorted.
C If ABS(KFLAG) = 2, then the values in X will be
C rearranged on output; otherwise, they are unchanged.
C N - input -- number of values in array X to be sorted.
C IPERM - output -- permutation array such that IPERM(I) is the
C index of the value in the original order of the
C X array that is in the Ith location in the sorted
C order.
C KFLAG - input -- control parameter:
C = 2 means return the permutation vector resulting from
C sorting X in increasing order and sort X also.
C = 1 means return the permutation vector resulting from
C sorting X in increasing order and do not sort X.
C = -1 means return the permutation vector resulting from
C sorting X in decreasing order and do not sort X.
C = -2 means return the permutation vector resulting from
C sorting X in decreasing order and sort X also.
C IER - output -- error indicator:
C = 0 if no error,
C = 1 if N is zero or negative,
C = 2 if KFLAG is not 2, 1, -1, or -2.
C***REFERENCES R. C. Singleton, Algorithm 347, An efficient algorithm
C for sorting with minimal storage, Communications of
C the ACM, 12, 3 (1969), pp. 185-187.
C***ROUTINES CALLED XERMSG
C***REVISION HISTORY (YYMMDD)
C 761101 DATE WRITTEN
C 761118 Modified by John A. Wisniewski to use the Singleton
C quicksort algorithm.
C 870423 Modified by Gregory S. Rhoads for passive sorting with the
C option for the rearrangement of the original data.
C 890620 Algorithm for rearranging the data vector corrected by R.
C Boisvert.
C 890622 Prologue upgraded to Version 4.0 style by D. Lozier.
C 891128 Error when KFLAG.LT.0 and N=1 corrected by R. Boisvert.
C 920507 Modified by M. McClain to revise prologue text.
C 920818 Declarations section rebuilt and code restructured to use
C IF-THEN-ELSE-ENDIF. (SMR, WRB)
C***END PROLOGUE SPSORT
C .. Scalar Arguments ..
INTEGER IER, KFLAG, N
C .. Array Arguments ..
REAL X(*)
INTEGER IPERM(*)
C .. Local Scalars ..
REAL R, TEMP
INTEGER I, IJ, INDX, INDX0, ISTRT, J, K, KK, L, LM, LMT, M, NN
C .. Local Arrays ..
INTEGER IL(21), IU(21)
C .. External Subroutines ..
EXTERNAL XERMSG
C .. Intrinsic Functions ..
INTRINSIC ABS, INT
C***FIRST EXECUTABLE STATEMENT SPSORT
IER = 0
NN = N
IF (NN .LT. 1) THEN
IER = 1
CALL XERMSG ('SLATEC', 'SPSORT',
+ 'The number of values to be sorted, N, is not positive.',
+ IER, 1)
RETURN
ENDIF
KK = ABS(KFLAG)
IF (KK.NE.1 .AND. KK.NE.2) THEN
IER = 2
CALL XERMSG ('SLATEC', 'SPSORT',
+ 'The sort control parameter, KFLAG, is not 2, 1, -1, or -2.',
+ IER, 1)
RETURN
ENDIF
C
C Initialize permutation vector
C
DO 10 I=1,NN
IPERM(I) = I
10 CONTINUE
C
C Return if only one value is to be sorted
C
IF (NN .EQ. 1) RETURN
C
C Alter array X to get decreasing order if needed
C
IF (KFLAG .LE. -1) THEN
DO 20 I=1,NN
X(I) = -X(I)
20 CONTINUE
ENDIF
C
C Sort X only
C
M = 1
I = 1
J = NN
R = .375E0
C
30 IF (I .EQ. J) GO TO 80
IF (R .LE. 0.5898437E0) THEN
R = R+3.90625E-2
ELSE
R = R-0.21875E0
ENDIF
C
40 K = I
C
C Select a central element of the array and save it in location L
C
IJ = I + INT((J-I)*R)
LM = IPERM(IJ)
C
C If first element of array is greater than LM, interchange with LM
C
IF (X(IPERM(I)) .GT. X(LM)) THEN
IPERM(IJ) = IPERM(I)
IPERM(I) = LM
LM = IPERM(IJ)
ENDIF
L = J
C
C If last element of array is less than LM, interchange with LM
C
IF (X(IPERM(J)) .LT. X(LM)) THEN
IPERM(IJ) = IPERM(J)
IPERM(J) = LM
LM = IPERM(IJ)
C
C If first element of array is greater than LM, interchange
C with LM
C
IF (X(IPERM(I)) .GT. X(LM)) THEN
IPERM(IJ) = IPERM(I)
IPERM(I) = LM
LM = IPERM(IJ)
ENDIF
ENDIF
GO TO 60
50 LMT = IPERM(L)
IPERM(L) = IPERM(K)
IPERM(K) = LMT
C
C Find an element in the second half of the array which is smaller
C than LM
C
60 L = L-1
IF (X(IPERM(L)) .GT. X(LM)) GO TO 60
C
C Find an element in the first half of the array which is greater
C than LM
C
70 K = K+1
IF (X(IPERM(K)) .LT. X(LM)) GO TO 70
C
C Interchange these elements
C
IF (K .LE. L) GO TO 50
C
C Save upper and lower subscripts of the array yet to be sorted
C
IF (L-I .GT. J-K) THEN
IL(M) = I
IU(M) = L
I = K
M = M+1
ELSE
IL(M) = K
IU(M) = J
J = L
M = M+1
ENDIF
GO TO 90
C
C Begin again on another portion of the unsorted array
C
80 M = M-1
IF (M .EQ. 0) GO TO 120
I = IL(M)
J = IU(M)
C
90 IF (J-I .GE. 1) GO TO 40
IF (I .EQ. 1) GO TO 30
I = I-1
C
100 I = I+1
IF (I .EQ. J) GO TO 80
LM = IPERM(I+1)
IF (X(IPERM(I)) .LE. X(LM)) GO TO 100
K = I
C
110 IPERM(K+1) = IPERM(K)
K = K-1
C
IF (X(LM) .LT. X(IPERM(K))) GO TO 110
IPERM(K+1) = LM
GO TO 100
C
C Clean up
C
120 IF (KFLAG .LE. -1) THEN
DO 130 I=1,NN
X(I) = -X(I)
130 CONTINUE
ENDIF
C
C Rearrange the values of X if desired
C
IF (KK .EQ. 2) THEN
C
C Use the IPERM vector as a flag.
C If IPERM(I) < 0, then the I-th value is in correct location
C
DO 150 ISTRT=1,NN
IF (IPERM(ISTRT) .GE. 0) THEN
INDX = ISTRT
INDX0 = INDX
TEMP = X(ISTRT)
140 IF (IPERM(INDX) .GT. 0) THEN
X(INDX) = X(IPERM(INDX))
INDX0 = INDX
IPERM(INDX) = -IPERM(INDX)
INDX = ABS(IPERM(INDX))
GO TO 140
ENDIF
X(INDX0) = TEMP
ENDIF
150 CONTINUE
C
C Revert the signs of the IPERM values
C
DO 160 I=1,NN
IPERM(I) = -IPERM(I)
160 CONTINUE
C
ENDIF
C
RETURN
END