vmsort.scm 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. ;;; The sort package -- stable vector merge & merge sort -*- Scheme -*-
  2. ;;; Copyright (c) 1998 by Olin Shivers.
  3. ;;; This code is open-source; see the end of the file for porting and
  4. ;;; more copyright information.
  5. ;;; Olin Shivers 10/98.
  6. ;;; Exports:
  7. ;;; (vector-merge < v1 v2 [start1 end1 start2 end2]) -> vector
  8. ;;; (vector-merge! < v v1 v2 [start0 start1 end1 start2 end2]) -> unspecific
  9. ;;;
  10. ;;; (vector-merge-sort < v [start end temp]) -> vector
  11. ;;; (vector-merge-sort! < v [start end temp]) -> unspecific
  12. ;;; Merge
  13. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  14. ;;; (vector-merge < v1 v2 [start1 end1 start2 end2]) -> vector
  15. ;;; (vector-merge! < v v1 v2 [start start1 end1 start2 end2]) -> unspecific
  16. ;;;
  17. ;;; Stable vector merge -- V1's elements come out ahead of equal V2 elements.
  18. (define (vector-merge < v1 v2 . maybe-starts+ends)
  19. (call-with-values
  20. (lambda () (vectors-start+end-2 v1 v2 maybe-starts+ends))
  21. (lambda (start1 end1 start2 end2)
  22. (let ((ans (make-vector (+ (- end1 start1) (- end2 start2)))))
  23. (%vector-merge! < ans v1 v2 0 start1 end1 start2 end2)
  24. ans))))
  25. (define (vector-merge! < v v1 v2 . maybe-starts+ends)
  26. (call-with-values
  27. (lambda ()
  28. (if (pair? maybe-starts+ends)
  29. (values (car maybe-starts+ends)
  30. (cdr maybe-starts+ends))
  31. (values 0
  32. '())))
  33. (lambda (start rest)
  34. (call-with-values
  35. (lambda () (vectors-start+end-2 v1 v2 rest))
  36. (lambda (start1 end1 start2 end2)
  37. (%vector-merge! < v v1 v2 start start1 end1 start2 end2))))))
  38. ;;; This routine is not exported. The code is tightly bummed.
  39. ;;;
  40. ;;; If these preconditions hold, the routine can be bummed to run with
  41. ;;; unsafe vector-indexing and fixnum arithmetic ops:
  42. ;;; - V V1 V2 are vectors.
  43. ;;; - START START1 END1 START2 END2 are fixnums.
  44. ;;; - (<= 0 START END0 (vector-length V),
  45. ;;; where end0 = start + (end1 - start1) + (end2 - start2)
  46. ;;; - (<= 0 START1 END1 (vector-length V1))
  47. ;;; - (<= 0 START2 END2 (vector-length V2))
  48. ;;; If you put these error checks in the two client procedures above, you can
  49. ;;; safely convert this procedure to use unsafe ops -- which is why it isn't
  50. ;;; exported. This will provide *huge* speedup.
  51. (define (%vector-merge! elt< v v1 v2 start start1 end1 start2 end2)
  52. (letrec ((vblit (lambda (fromv j i end) ; Blit FROMV[J,END) to V[I,?].
  53. (let lp ((j j) (i i))
  54. (vector-set! v i (vector-ref fromv j))
  55. (let ((j (+ j 1)))
  56. (if (< j end) (lp j (+ i 1))))))))
  57. (cond ((<= end1 start1) (if (< start2 end2) (vblit v2 start2 start end2)))
  58. ((<= end2 start2) (vblit v1 start1 start end1))
  59. ;; Invariants: I is next index of V to write; X = V1[J]; Y = V2[K].
  60. (else (let lp ((i start)
  61. (j start1) (x (vector-ref v1 start1))
  62. (k start2) (y (vector-ref v2 start2)))
  63. (let ((i1 (+ i 1))) ; "i+1" is a complex number in R4RS!
  64. (if (elt< y x)
  65. (let ((k (+ k 1)))
  66. (vector-set! v i y)
  67. (if (< k end2)
  68. (lp i1 j x k (vector-ref v2 k))
  69. (vblit v1 j i1 end1)))
  70. (let ((j (+ j 1)))
  71. (vector-set! v i x)
  72. (if (< j end1)
  73. (lp i1 j (vector-ref v1 j) k y)
  74. (vblit v2 k i1 end2))))))))))
  75. ;;; (vector-merge-sort < v [start end temp]) -> vector
  76. ;;; (vector-merge-sort! < v [start end temp]) -> unspecific
  77. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  78. ;;; Stable natural vector merge sort
  79. (define (vector-merge-sort! < v . maybe-args)
  80. (call-with-values
  81. (lambda () (vector-start+end v maybe-args))
  82. (lambda (start end)
  83. (let ((temp (if (and (pair? maybe-args) ; kludge
  84. (pair? (cdr maybe-args))
  85. (pair? (cddr maybe-args)))
  86. (caddr maybe-args)
  87. (vector-copy v))))
  88. (%vector-merge-sort! < v start end temp)))))
  89. (define (vector-merge-sort < v . maybe-args)
  90. (let ((ans (vector-copy v)))
  91. (apply vector-merge-sort! < ans maybe-args)
  92. ans))
  93. ;;; %VECTOR-MERGE-SORT! is not exported.
  94. ;;; Preconditions:
  95. ;;; V TEMP vectors
  96. ;;; START END fixnums
  97. ;;; START END legal indices for V and TEMP
  98. ;;; If these preconditions are ensured by the cover functions, you
  99. ;;; can safely change this code to use unsafe fixnum arithmetic and vector
  100. ;;; indexing ops, for *huge* speedup.
  101. ;;; This merge sort is "opportunistic" -- the leaves of the merge tree are
  102. ;;; contiguous runs of already sorted elements in the vector. In the best
  103. ;;; case -- an already sorted vector -- it runs in linear time. Worst case
  104. ;;; is still O(n lg n) time.
  105. (define (%vector-merge-sort! elt< v0 l r temp0)
  106. (define (xor a b) (not (eq? a b)))
  107. ;; Merge v1[l,l+len1) and v2[l+len1,l+len1+len2) into target[l,l+len1+len2)
  108. ;; Merge left-to-right, so that TEMP may be either V1 or V2
  109. ;; (that this is OK takes a little bit of thought).
  110. ;; V2=TARGET? is true if V2 and TARGET are the same, which allows
  111. ;; merge to punt the final blit half of the time.
  112. (define (merge target v1 v2 l len1 len2 v2=target?)
  113. (letrec ((vblit (lambda (fromv j i end) ; Blit FROMV[J,END) to TARGET[I,?]
  114. (let lp ((j j) (i i)) ; J < END. The final copy.
  115. (vector-set! target i (vector-ref fromv j))
  116. (let ((j (+ j 1)))
  117. (if (< j end) (lp j (+ i 1))))))))
  118. (let* ((r1 (+ l len1))
  119. (r2 (+ r1 len2)))
  120. ; Invariants:
  121. (let lp ((n l) ; N is next index of
  122. (j l) (x (vector-ref v1 l)) ; TARGET to write.
  123. (k r1) (y (vector-ref v2 r1))) ; X = V1[J]
  124. (let ((n+1 (+ n 1))) ; Y = V2[K]
  125. (if (elt< y x)
  126. (let ((k (+ k 1)))
  127. (vector-set! target n y)
  128. (if (< k r2)
  129. (lp n+1 j x k (vector-ref v2 k))
  130. (vblit v1 j n+1 r1)))
  131. (let ((j (+ j 1)))
  132. (vector-set! target n x)
  133. (if (< j r1)
  134. (lp n+1 j (vector-ref v1 j) k y)
  135. (if (not v2=target?) (vblit v2 k n+1 r2))))))))))
  136. ;; Might hack GETRUN so that if the run is short it pads it out to length
  137. ;; 10 with insert sort...
  138. ;; Precondition: l < r.
  139. (define (getrun v l r)
  140. (let lp ((i (+ l 1)) (x (vector-ref v l)))
  141. (if (>= i r)
  142. (- i l)
  143. (let ((y (vector-ref v i)))
  144. (if (elt< y x)
  145. (- i l)
  146. (lp (+ i 1) y))))))
  147. ;; RECUR: Sort V0[L,L+LEN) for some LEN where 0 < WANT <= LEN <= (R-L).
  148. ;; That is, sort *at least* WANT elements in V0 starting at index L.
  149. ;; May put the result into either V0[L,L+LEN) or TEMP0[L,L+LEN).
  150. ;; Must not alter either vector outside this range.
  151. ;; Return:
  152. ;; - LEN -- the number of values we sorted
  153. ;; - ANSVEC -- the vector holding the value
  154. ;; - ANS=V0? -- tells if ANSVEC is V0 or TEMP
  155. ;;
  156. ;; LP: V[L,L+PFXLEN) holds a sorted prefix of V0.
  157. ;; TEMP = if V = V0 then TEMP0 else V0. (I.e., TEMP is the other vec.)
  158. ;; PFXLEN2 is a power of 2 <= PFXLEN.
  159. ;; Solve RECUR's problem.
  160. (if (< l r) ; Don't try to sort an empty range.
  161. (call-with-values
  162. (lambda ()
  163. (let recur ((l l) (want (- r l)))
  164. (let ((len (- r l)))
  165. (let lp ((pfxlen (getrun v0 l r)) (pfxlen2 1)
  166. (v v0) (temp temp0)
  167. (v=v0? #t))
  168. (if (or (>= pfxlen want) (= pfxlen len))
  169. (values pfxlen v v=v0?)
  170. (let ((pfxlen2 (let lp ((j pfxlen2))
  171. (let ((j*2 (+ j j)))
  172. (if (<= j pfxlen) (lp j*2) j))))
  173. (tail-len (- len pfxlen)))
  174. ;; PFXLEN2 is now the largest power of 2 <= PFXLEN.
  175. ;; (Just think of it as being roughly PFXLEN.)
  176. (call-with-values
  177. (lambda ()
  178. (recur (+ pfxlen l) pfxlen2))
  179. (lambda (nr-len nr-vec nrvec=v0?)
  180. (merge temp v nr-vec l pfxlen nr-len
  181. (xor nrvec=v0? v=v0?))
  182. (lp (+ pfxlen nr-len) (+ pfxlen2 pfxlen2)
  183. temp v (not v=v0?))))))))))
  184. (lambda (ignored-len ignored-ansvec ansvec=v0?)
  185. (if (not ansvec=v0?) (vector-portion-copy! v0 temp0 l r))))))
  186. ;;; Copyright
  187. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  188. ;;; This code is
  189. ;;; Copyright (c) 1998 by Olin Shivers.
  190. ;;; The terms are: You may do as you please with this code, as long as
  191. ;;; you do not delete this notice or hold me responsible for any outcome
  192. ;;; related to its use.
  193. ;;;
  194. ;;; Blah blah blah. Don't you think source files should contain more lines
  195. ;;; of code than copyright notice?
  196. ;;; Code tuning & porting
  197. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  198. ;;; This code is *tightly* bummed as far as I can go in portable Scheme.
  199. ;;;
  200. ;;; The two internal primitives that do the real work can be converted to
  201. ;;; use unsafe vector-indexing and fixnum-specific arithmetic ops *if* you
  202. ;;; alter the four small cover functions to enforce the invariants. This should
  203. ;;; provide *big* speedups. In fact, all the code bumming I've done pretty
  204. ;;; much disappears in the noise unless you have a good compiler and also
  205. ;;; can dump the vector-index checks and generic arithmetic -- so I've really
  206. ;;; just set things up for you to exploit.
  207. ;;;
  208. ;;; The optional-arg parsing, defaulting, and error checking is done with a
  209. ;;; portable R4RS macro. But if your Scheme has a faster mechanism (e.g.,
  210. ;;; Chez), you should definitely port over to it. Note that argument defaulting
  211. ;;; and error-checking are interleaved -- you don't have to error-check
  212. ;;; defaulted START/END args to see if they are fixnums that are legal vector
  213. ;;; indices for the corresponding vector, etc.