@@ -8,7 +8,6 @@ using Base.Order
88
99import Base. Sort: sort!
1010import DataStructures: heapify!, percolate_down!
11- import StaticArrays: MVector
1211
1312export HeapSort, TimSort, RadixSort, CombSort, PagedMergeSort, ThreadedPagedMergeSort
1413
786785next_page_A (pages:: Pages ) = Pages (pages. nextA, pages. currentNumber + 1 , pages. nextA + 1 , pages. nextB)
787786next_page_B (pages:: Pages ) = Pages (pages. nextB, pages. currentNumber + 1 , pages. nextA, pages. nextB + 1 )
788787
789- function next_page! (pageLocations, pages, pagesize, lo, a)
788+ Base . @propagate_inbounds function next_page! (pageLocations, pages, pagesize, lo, a)
790789 if a > pages. nextA * pagesize + lo
791790 pages = next_page_A (pages)
792791 else
@@ -796,6 +795,31 @@ function next_page!(pageLocations, pages, pagesize, lo, a)
796795 pages
797796end
798797
798+ Base. @propagate_inbounds function copy_page! (v, source, offset, offset2, pagesize)
799+ for j = 1 : pagesize
800+ v[offset + j] = source[offset2 + j]
801+ end
802+ end
803+
804+ # copy correct data into free page currentPage,
805+ # following a permutation cycle until one page is copied from buf
806+ Base. @propagate_inbounds function copy_pages! (v, buf, pageLocations, currentPage, page_offset, pagesize)
807+ while true
808+ plc = pageLocations[currentPage] # page with data belonging to currentPage
809+ pageLocations[currentPage] = 0
810+ offset = page_offset (currentPage)
811+ if plc > 0 # data for currentPage is in v
812+ offset2 = page_offset (plc)
813+ copy_page! (v, v, offset, offset2, pagesize)
814+ currentPage = plc
815+ else # data for currentPage is in buf
816+ offset2 = (- plc- 1 )* pagesize
817+ copy_page! (v, buf, offset, offset2, pagesize)
818+ return
819+ end
820+ end
821+ end
822+
799823# merge v[lo:m] (A) and v[m+1:hi] (B) using buffer buf in O(sqrt(n)) space
800824function paged_merge! (v:: AbstractVector{T} , lo:: Integer , m:: Integer , hi:: Integer , o:: Ordering , buf:: AbstractVector{T} , pageLocations:: AbstractVector{<:Integer} ) where T
801825 @assert lo < m < hi
@@ -829,7 +853,7 @@ function paged_merge!(v::AbstractVector{T}, lo::Integer, m::Integer, hi::Integer
829853 # #################
830854 # merge the first 3 pages into buf
831855 a,b,k = merge! ((_,_,k) -> k<= 3 pagesize,buf,v,v,o,a,b,1 )
832- # initialize variable for merging into pages
856+ # initialize variables for merging into pages
833857 pageLocations .= 0
834858 pageLocations[1 : 3 ] = - 1 : - 1 : - 3
835859 currentPage = 0
@@ -896,66 +920,51 @@ function paged_merge!(v::AbstractVector{T}, lo::Integer, m::Integer, hi::Integer
896920 # ########################################
897921 nFreePagesB = nPages + 1 - pages. nextB
898922 nFreePagesA = 3 - nFreePagesB - Int (partialPagePresent)
899- freePages = MVector {3,Int} (undef)
900- i = 1
901- for j = 0 : nFreePagesA- 1
902- freePages[i] = pages. nextA + j
903- i += 1
904- end
905- for j = 0 : nFreePagesB- 1
906- freePages[i] = pages. nextB + j
907- i += 1
908- end
909923 if partialPagePresent
910- freePages[i] = pages. current
924+ # nFreePagesA == 0 is impossible:
925+ # the last page in A (partially in B) is always free
926+ if nFreePagesA == 1
927+ freePages = (pages. nextA, pages. nextB, pages. current)
928+ else # nFreePagesA == 2
929+ freePages = (pages. nextA, pages. nextA + 1 , pages. current)
930+ end
931+ else
932+ # nFreePagesA == 0 is impossible:
933+ # next_page!() only uses nextA if there is MORE THAN one page free in A
934+ # -> if there is exactly one page free in A, nextB is used
935+ # nFreePagesA == 3 is impossible:
936+ # B contains at least 3pagesize elements -> at least one free page will exist in B at some point
937+ # next_page!() never uses nextB if there is more than one page free in A
938+ if nFreePagesA == 1
939+ freePages = (pages. nextA, pages. nextB, pages. nextB + 1 )
940+ else # nFreePagesA == 2
941+ freePages = (pages. nextA, pages. nextA + 1 , pages. nextB)
942+ end
911943 end
912- freePagesIndex = 3
913- donePageIndex = 1
914- # use currentPage instead of pages.current because
915- # pages.nextA, pages.nextB and page.currentNumber are no longer needed
916- currentPage = freePages[end ]
917944 # #################
918945 # rearrange pages
919946 # #################
947+ # copy pages belonging to the 3 permutation chains ending with a page in the buffer
948+ for currentPage in freePages
949+ copy_pages! (v, buf, pageLocations, currentPage, page_offset, pagesize)
950+ end
951+ # copy remaining permutation cycles
952+ donePageIndex = 1
920953 while true
921- plc = pageLocations[currentPage] # page with data belonging to currentPage
922- if plc > 0
923- # data for currentPage is in v
924- offset = page_offset (currentPage)
925- offset2 = page_offset (plc)
926- for j = 1 : pagesize
927- v[offset + j] = v[offset2 + j]
928- end
929- pageLocations[currentPage] = 0
930- currentPage = plc
931- else
932- # data for currentPage is in buf
933- offset = page_offset (currentPage)
934- offset2 = (- plc- 1 )* pagesize
935- for j = 1 : pagesize
936- v[offset + j] = buf[offset2 + j]
937- end
938- pageLocations[currentPage] = 0
939- if freePagesIndex > 1
940- # get next free page
941- freePagesIndex -= 1
942- currentPage = freePages[freePagesIndex]
943- else
944- # no free page remains
945- # make sure that all pages are done
946- while pageLocations[donePageIndex] == 0 || pageLocations[donePageIndex] == donePageIndex
947- donePageIndex += 1
948- donePageIndex == nPages && return
949- end
950- # copy misplaced page into buf and continue
951- currentPage = pageLocations[donePageIndex]
952- offset = page_offset (currentPage)
953- for j = 1 : pagesize
954- buf[j] = v[offset + j]
955- end
956- pageLocations[donePageIndex] = - 1
957- end
954+ # linear scan through pageLocations to make sure no cycle is missed
955+ while pageLocations[donePageIndex] == 0 || pageLocations[donePageIndex] == donePageIndex
956+ donePageIndex += 1
957+ donePageIndex == nPages && return
958+ end
959+ # copy misplaced page into buf
960+ # and follow the cycle starting with the newly freed page
961+ currentPage = pageLocations[donePageIndex]
962+ offset = page_offset (currentPage)
963+ for j = 1 : pagesize
964+ buf[j] = v[offset + j]
958965 end
966+ pageLocations[donePageIndex] = - 1
967+ copy_pages! (v, buf, pageLocations, currentPage, page_offset, pagesize)
959968 end
960969 end
961970end
0 commit comments