home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / orx8.zip / sort_util.cmd < prev   
OS/2 REXX Batch file  |  1997-07-21  |  23KB  |  515 lines

  1. /* 
  2. program:   sort_util.cmd
  3. type:      Object REXX, REXXSAA 6.0
  4. purpose:   some sort routines on different types of objects, doing NLS-comparisons
  5. version:   1.0
  6. date:      1997-04-11
  7. changed:   ---
  8.  
  9. author:    Rony G. Flatscher
  10.            Rony.Flatscher@wu-wien.ac.at
  11.            (Wirtschaftsuniversitaet Wien, University of Economics and Business
  12.            Administration, Vienna/Austria/Europe)
  13. needs:     ---
  14.  
  15. usage:     call or require 
  16.  
  17. comments:  prepared for the "8th International Rexx Symposium 1997", April 1997, Heidelberg/Germany
  18.  
  19.  
  20. All rights reserved and copyrighted 1995-1997 by the author, no guarantee that
  21. it works without errors, etc.  etc.
  22.  
  23. You are granted the right to use this module under the condition that you don't charge money for this module (as you didn't write
  24. it in the first place) or modules directly derived from this module, that you document the original author (to give appropriate
  25. credit) with the original name of the module and that you make the unaltered, original source-code of this module available on
  26. demand.  If that holds, you may even bundle this module (either in source or compiled form) with commercial software.
  27.  
  28. If you find an error, please send me the description (preferably a *very* short example);
  29. I'll try to fix it and re-release it to the net.
  30.  
  31. */
  32.  
  33.  
  34. :: REQUIRES nls_util.cmd                /* requires NLS-support         */
  35.  
  36.  
  37.  
  38.  
  39. /******************************************************************************/
  40. /*                                                                            */
  41. /* name:    sortStem( stem. )                                                 */
  42. /*                                                                            */
  43. /* purpose: sorts passed in stem in place                                     */
  44. /*                                                                            */
  45. /* returns: ---                                                               */
  46. /*                                                                            */
  47. /* remarks: this is supposedly derived from one of Knuth's algorithms         */
  48. /*                                                                            */
  49. /*          - expected layout of stem (as with some SysFunctions):            */
  50. /*                                                                            */
  51. /*            stem.0 ... contains total number of entries in stem             */
  52. /*            stem.i ... where i > 0 and i <= stem.0                          */
  53. /*                                                                            */
  54. /*            stem-array will get sorted in place (no stem.-copy!)            */
  55. /*                                                                            */
  56. /* created: rgf, 95-09-29; 97-04-15                                           */
  57.  
  58. :: ROUTINE sortStem              PUBLIC
  59.    USE ARG stem. , option
  60.  
  61.    bDesc = ( TRANSLATE( LEFT( option, 1 ) ) = "D" )     /* sort descending ?    */
  62.  
  63.    M = 1                           /* define M for passes           */
  64.    DO WHILE (9 * M + 4) < stem.0
  65.       M = M * 3 + 1
  66.    END
  67.  
  68.    DO WHILE M > 0                  /* sort stem                     */
  69.       K = stem.0 - M
  70.       DO J = 1 TO K
  71.          Q = J
  72.          DO WHILE Q > 0
  73.             L = Q + M
  74.             /* make comparisons case-independent                    */
  75.             IF bDesc THEN       /* descending sort              */        
  76.             DO
  77.                IF NLS_COLLATE( stem.Q )   > NLS_COLLATE( stem.L ) THEN LEAVE
  78.             END
  79.             ELSE 
  80.             DO
  81.                IF NLS_COLLATE( stem.Q ) <<= NLS_COLLATE( stem.L ) THEN LEAVE
  82.             END
  83.  
  84.  
  85.             tmp    = stem.Q        /* switch elements               */
  86.             stem.Q = stem.L
  87.             stem.L = tmp
  88.             Q = Q - M
  89.          END
  90.       END
  91.       M = M % 3
  92.    END
  93.  
  94.    RETURN 
  95. /******************************************************************************/
  96.  
  97.  
  98.  
  99.  
  100.  
  101.  
  102. /******************************************************************************/
  103. /*                                                                            */
  104. /* name:    sort( CollObj [, "Descending" ] )                                 */
  105. /*                                                                            */
  106. /* purpose: creates a *single* array object from a collection object and      */
  107. /*          sorts it by the index-value, which gets put into the single       */
  108. /*          dimensioned array                                                 */
  109. /*                                                                            */
  110. /* returns: returns a *new*, sorted array-object                              */
  111. /*                                                                            */
  112. /* remarks: if object is not of type .array and array will be created via     */
  113. /*          the MAKEARRAY message, else a copy of the passed in array object  */
  114. /*          will be produced; the new .array object gets sorted via           */
  115. /*          sortArray                                                         */
  116. /*                                                                            */
  117. /* needs:   routine sortArray()                                               */
  118. /*                                                                            */
  119. /* created: rgf, 95-09-15; 97-04-15                                           */
  120.  
  121. :: ROUTINE SORT                  PUBLIC
  122.    USE ARG CollObj, option
  123.  
  124.  
  125. /* old, unsafe way
  126.    IF CollObj~class~id <> "Array" THEN
  127. */
  128.  
  129.    IF IsA( CollObj, .array ) THEN             /* safer, new way             */
  130.    DO
  131.       workArray = CollObj ~ copy              /* it's an array, create a copy */
  132.    END
  133.    ELSE
  134.    DO
  135.       /* if a "stem.-array" in hand, assign it to a real array  */
  136.       IF IsA( CollObj, .stem ) & DATATYPE( CollObj[ 0 ], "W" ) THEN   
  137.       DO
  138.          workArray = .array ~ new
  139.          DO i = 1 TO CollObj[ 0 ]
  140.             workArray[ i ] = CollObj[ i ]
  141.          END
  142.       END
  143.  
  144.  
  145.       ELSE IF CollObj ~ HasMethod( "MAKEARRAY" ) THEN   /* use MAKEARRAY      */
  146.       DO
  147.          workArray = CollObj ~ makearray
  148.       END
  149.  
  150.       ELSE                                      /* build array by "hand"      */
  151.       DO
  152.          IF IsA(CollObj, .supplier) THEN        /* take care of a supplier    */
  153.          DO
  154.             i = 1
  155.             workArray = .array ~ new
  156.             DO WHILE CollObj ~ available
  157.                workArray[i] = CollObj ~ index
  158.                CollObj~next
  159.                i = i + 1
  160.             END
  161.          END
  162.          ELSE                           /* use OVER to assemble from keys     */
  163.          DO
  164.             i = 1
  165.             workArray = .array ~ new
  166.             DO item OVER CollObj
  167.                workArray[i] = item
  168.                i = i + 1
  169.             END
  170.          END
  171.       END
  172.    END
  173.  
  174.    RETURN sortArray( workArray, option )        /* do the actual sort   */
  175.  
  176.  
  177.  
  178. /******************************************************************************/
  179.  
  180.  
  181.  
  182. /******************************************************************************/
  183. /*                                                                            */
  184. /* name:    sortArray( array [, "Descending" ] )                              */
  185. /*                                                                            */
  186. /* purpose: sorts passed in single-dimensioned array                          */
  187. /*                                                                            */
  188. /* returns: returns the sorted array                                          */
  189. /*                                                                            */
  190. /* remarks: this is supposedly derived from one of Knuth's algorithms         */
  191. /*                                                                            */
  192. /* created: rgf, 95-09-15; 97-04-15                                           */
  193.  
  194. :: ROUTINE sortArray             PUBLIC
  195.    USE ARG array, option
  196.  
  197.    bDesc = ( TRANSLATE( LEFT( option, 1 ) ) = "D" )     /* sort descending ?    */
  198.  
  199.    M = 1                           /* define M for passes       */
  200.    DO WHILE (9 * M + 4) < array~items
  201.       M = M * 3 + 1
  202.    END
  203.  
  204.    DO WHILE M > 0                  /* sort stem                 */
  205.       K = array~items - M
  206.       DO J = 1 TO K
  207.          Q = J
  208.          DO WHILE Q > 0
  209.             L = Q + M
  210.             /* make comparisons case-independent                */
  211.             IF bDesc THEN       /* descending sort              */        
  212.             DO
  213.                IF NLS_COLLATE( array[Q] )   > NLS_COLLATE( array[L] ) THEN LEAVE
  214.             END
  215.             ELSE 
  216.             DO
  217.                IF NLS_COLLATE( array[Q] ) <<= NLS_COLLATE( array[L] ) THEN LEAVE
  218.             END
  219.  
  220.             tmp      = array[Q]    /* switch elements           */
  221.             array[Q] = array[L]
  222.             array[L] = tmp
  223.             Q = Q - M
  224.          END
  225.       END
  226.       M = M % 3
  227.    END
  228.  
  229.    RETURN array
  230. /******************************************************************************/
  231.  
  232.  
  233.  
  234.  
  235. /******************************************************************************/
  236. /*                                                                            */
  237. /* name:    sortCollection( collection [, [message] [, "Descending" ] ] )     */
  238. /*                                                                            */
  239. /* purpose: sorts objects in "collection" (retrieved via SUPPLIER), returns   */
  240. /*          a sorted, two-dimensional array; the sort-keys are retrieved      */
  241. /*          directly from the stored object                                   */
  242. /*                                                                            */
  243. /*          First the object of the iterated collection gets the "message"    */
  244. /*          sent, the result is stored at subscript #1, the object itself     */
  245. /*          is stored at subscript #2 in the resulting array, then a sort     */
  246. /*          by subscript #1 is undertaken and the array returned.             */
  247. /*                                                                            */
  248. /*          "Message" may be a *plain string* denominating the message name,  */
  249. /*          *a message object* or an *array* with the following layout:       */
  250. /*              message[ 1 ] = name of the message                            */
  251. /*              message[ 2 ] = optional; if given it contains an array        */
  252. /*                             with all the arguments that are needed         */
  253. /*                             for executing the message                      */
  254. /*                                                                            */
  255. /*          If optional "message" is left out, the key (index) is used as     */
  256. /*          is, if it is a string, else Object's "STRING"-message is sent     */
  257. /*          to the key (index) part of the collection.                        */
  258. /*                                                                            */
  259. /* returns: returns 2-dimensional array, sorted after keys stored in          */
  260. /*          subscript #1 and the object being stored in subscript #2          */
  261. /*                                                                            */
  262. /* remarks: the sort routine is supposedly derived from one of Knuth's        */
  263. /*          algorithms                                                        */
  264. /*                                                                            */
  265. /*          if no "message" is given, then in the case of a                   */
  266. /*                                                                            */
  267. /*          - two-dimensional array a copy of it gets directly sorted by      */
  268. /*            subscript # 1                                                   */
  269. /*                                                                            */
  270. /*          - stem, a two-dimensional array is built, where the stem-index    */
  271. /*            gets stored at subscript #1 and the associated object at #2     */
  272. /*                                                                            */
  273. /*          else a STRING-message is sent to the object and the result        */
  274. /*          gets stored at subscript #1 (usually OBJECTNAME)                  */
  275. /*                                                                            */
  276. /* created: rgf, 96-09-09; 97-04-15                                           */
  277.  
  278. :: ROUTINE sortCollection        PUBLIC
  279.    USE ARG collection, msgArg, option 
  280.  
  281.    SIGNAL ON SYNTAX
  282.  
  283.    IF \ VAR( "msgArg" ) THEN    /* no message-argument supplied */
  284.    DO
  285.       MsgObj     = .nil         /* no message-object available  */
  286.    END
  287.    ELSE
  288.    DO           /* create the desired message object to be sent to the index-object */
  289.       IF IsA( msgArg, .message ) THEN   /* a message object ?           */
  290.       DO
  291.          MsgObj = msgArg        /* save message-object                  */
  292.       END
  293.  
  294.                                 /* create message object from array     */
  295.       ELSE IF IsA( msgArg, .array ) THEN      
  296.       DO
  297.          IF msgArg[ 2 ] = .nil THEN   /* method without arguments       */
  298.             MsgObj = .message ~ new( .nil, msgArg[ 1 ] )
  299.          ELSE                   /* array of arguments supplied          */
  300.             MsgObj = .message ~ new( .nil, msgArg[ 1 ], "A", msgArg[ 2 ] )
  301.       END
  302.  
  303.  
  304.       ELSE                      /* create message object from string    */
  305.       DO
  306.          MsgObj = .message ~ new( .nil, msgArg )
  307.       END
  308.    END
  309.  
  310.  
  311.    bDesc = ( TRANSLATE( LEFT( option, 1 ) ) = "D" )     /* sort descending ?    */
  312.       
  313.  
  314.    /* first generate a 2-dimensional array, where the key is stored in [ n, 1 ] and
  315.       the object in [ n, 2 ]                                                    */
  316.    bBuildArray = .true
  317.    IF MsgObj = .nil THEN
  318.    DO
  319.       IF IsA( collection, .array ) THEN         /* already an array in hand ?   */
  320.       DO
  321.          IF collection ~ dimension = 2 THEN     /* [ x, 1 ] key, [ x, 2 ] object*/
  322.          DO
  323.             array = collection ~ copy   /* use a copy of the passed in array    */
  324.             bBuildArray = .false
  325.          END
  326.       END
  327.  
  328.                 /* *any* stem gets sorted by its indices                */
  329.       ELSE IF IsA( collection, .stem ) THEN     /* sort a stem, using index as key */
  330.       DO
  331.          i = 0
  332.          array = .array ~ new
  333.          DO index OVER collection
  334.             i = i + 1
  335.             array[ i, 1 ] = index
  336.             array[ i, 2 ] = collection[ index ] /* get associated object        */
  337.          END
  338.  
  339.          bBuildArray = .false
  340.       END
  341.    END
  342.       
  343.    IF bBuildArray THEN
  344.    DO
  345.       array = .array ~ new                 /* create a new array object            */
  346.       i = 0
  347.       IF IsA( collection, .stem ) THEN     /* .stem is supplied with a message, non-msg
  348.                                               version already handled above        */
  349.       DO
  350.          DO index OVER collection
  351.             i = i + 1                      /* another item to be added to array    */
  352.             object = collection[ index ] 
  353.             array[ i, 1 ] = MsgObj ~ copy ~ send( object ) 
  354.             array[ i, 2 ] = object         /* save object          */
  355.          END
  356.       END
  357.       ELSE
  358.       DO
  359.          tmpSupp = collection ~ SUPPLIER   /* loop over objects in collection      */
  360.          DO WHILE tmpSupp ~ AVAILABLE
  361.             i = i + 1                      /* another item to be added to array    */
  362.  
  363.             object = tmpSupp ~ ITEM
  364.    
  365.             IF MsgObj = .nil THEN          /* if no message-object exists, do default */
  366.                array[ i, 1 ] = object ~ string     /* send the object the STRING-message   */
  367.             ELSE           /* retrieve value to sort by via the message object     */
  368.               array[ i, 1 ] = MsgObj ~ copy ~ send( object ) 
  369.  
  370.             array[ i, 2 ] = object         /* save object          */
  371.               
  372.             tmpSupp ~ NEXT                 /* get next item        */
  373.          END
  374.       END
  375.    END
  376.  
  377.    IF array ~ items = 0 THEN RETURN .array ~ new   /* nothing to sort      */
  378.    total_Items = ( array ~ items / 2 )     /* get # of items to sort       */
  379.  
  380.  
  381.         /* Knuth's algorithm ...                */
  382.    M = 1                           /* define M for passes           */
  383.    DO WHILE (9 * M + 4) < total_Items 
  384.       M = M * 3 + 1
  385.    END
  386.  
  387.    DO WHILE M > 0                  /* sort stem                     */
  388.  
  389.       K = total_Items - M
  390.  
  391.       DO J = 1 TO K
  392.          Q = J
  393.          DO WHILE Q > 0
  394.             L = Q + M
  395.  
  396.             /* make comparisons case-independent                    */
  397.             IF bDesc THEN       /* descending sort              */        
  398.             DO
  399.                IF NLS_COLLATE( array[Q, 1] )   > NLS_COLLATE( array[L, 1] ) THEN LEAVE
  400.             END
  401.             ELSE                /* ascending sort               */
  402.             DO
  403.                IF NLS_COLLATE( array[Q, 1] ) <<= NLS_COLLATE( array[L, 1] ) THEN LEAVE
  404.             END
  405.  
  406.             tmp1        = array[Q, 1]   /* store key                    */
  407.             tmp2        = array[Q, 2]   /* store object                 */
  408.                         
  409.             array[Q, 1] = array[L, 1]   /* exchange key                 */
  410.             array[Q, 2] = array[L, 2]   /* exchange object              */
  411.                         
  412.             array[L, 1] = tmp1          /* exchange key                 */
  413.             array[L, 2] = tmp2          /* exchange object              */
  414.  
  415.             Q = Q - M
  416.          END
  417.       END
  418.       M = M % 3
  419.    END
  420.  
  421.    RETURN array
  422.  
  423. SYNTAX : RAISE PROPAGATE        /* show position in caller      */
  424. /******************************************************************************/
  425.  
  426.  
  427.  
  428. /******************************************************************************/
  429. /*                                                                            */
  430. /* name:    BinFindArray( array, searchKey )                                  */
  431. /*                                                                            */
  432. /* purpose: find a Key in a sorted, 1- or 2-dimensional array, where the key  */
  433. /*          to sort by is stored in subscript # 1                             */
  434. /*                                                                            */
  435. /* returns: return position in array containing the searchKey                 */
  436. /*                                                                            */
  437. /*          - if key was not found, .nil is returned                          */
  438. /*                                                                            */
  439. /*          - if duplicate keys in array, the very first key is searched and  */
  440. /*            returned                                                        */
  441. /*                                                                            */
  442. /* remarks: deals with multiple, sorted key-entries too                       */
  443. /*          array         ... array object                                    */
  444. /*          searchKey     ... key to search for                               */
  445. /*                                                                            */
  446. /*          if key was not found, .nil is returned.                           */
  447. /*                                                                            */
  448. /*          if duplicate keys in array, the very first key is searched and    */
  449. /*          returned.                                                         */
  450. /*                                                                            */
  451. /*                                                                            */
  452. /* created: rgf, 96-09-09                                                     */
  453.  
  454. :: ROUTINE BinFindArray          PUBLIC
  455.    USE ARG array, searchKey, bReturnArray
  456.  
  457.    dimension = array ~ dimension                /* get dimension of array               */
  458.    IF dimension > 2 THEN 
  459.    DO
  460.       SIGNAL ON SYNTAX
  461.       RAISE SYNTAX 40.4 ARRAY ( "BinaryFindInArray", "array-dimension must be <= 2 !" )
  462.    END
  463.  
  464.    total_Items = ( array ~ items / array ~ dimension )  /* determine # of rows          */
  465.  
  466.    bFound = .false
  467.    lowerBound = 1
  468.    upperBound = total_items
  469.    index      = .nil                            /* to contain index to searched key     */
  470.  
  471.    DO WHILE lowerBound <= upperBound
  472.       tmpPosition = ( upperBound + lowerBound ) % 2     /* halve the range              */
  473.  
  474.       IF dimension = 1 THEN arrayKey = array[ tmpPosition    ]  /* 1 dimension          */
  475.                        ELSE arrayKey = array[ tmpPosition, 1 ]  /* 2 dimensions         */
  476.  
  477.       tmpCompare  = NLS_COMPARE( arrayKey, searchKey )
  478.  
  479.       SELECT 
  480.          WHEN tmpCompare =  1 THEN              /* arrayKey > searchKey, search first half      */
  481.               upperBound = tmpPosition - 1
  482.  
  483.          WHEN tmpCompare = -1 THEN              /* arrayKey < searchKey, search second half     */
  484.               lowerBound = tmpPosition + 1
  485.  
  486.          OTHERWISE                              /* strings are identical              */
  487.               DO
  488.                  index  = tmpPosition           /* save found position                */
  489.  
  490.                  IF lowerBound = upperBound | tmpPosition = 1 THEN      /* no more searches possible    */
  491.                     LEAVE
  492.  
  493.                  /* check whether multiple keys in array, move one entry backward     */
  494.                  tmpIndex = tmpPosition - 1
  495.                  IF dimension = 1 THEN tmpArrayKey = array[ tmpIndex    ]  /* 1 dimension       */
  496.                                   ELSE tmpArrayKey = array[ tmpIndex, 1 ]  /* 2 dimensions      */
  497.  
  498.                  IF NLS_COMPARE( tmpArrayKey, searchKey ) <> 0 THEN     /* first entry found    */
  499.                     LEAVE
  500.  
  501.                  upperBound = tmpPosition - 1   /* search in first half               */
  502.               END
  503.       END  
  504.    END
  505.  
  506.    IF bReturnArray = .true THEN /* array-object: [1] index or .nil      */
  507.       RETURN .array ~ of( index, tmpPosition )  /*  [2] position        */  
  508.  
  509.    RETURN index                 /* first position of keyword in array or .nil if not found */
  510.  
  511. SYNTAX : RAISE PROPAGATE        /* show position in caller      */
  512. /******************************************************************************/
  513.  
  514.  
  515.