@@ -8,7 +8,6 @@ using Base.Order
8
8
9
9
import Base. Sort: sort!
10
10
import DataStructures: heapify!, percolate_down!
11
- import StaticArrays: MVector
12
11
13
12
export HeapSort, TimSort, RadixSort, CombSort, PagedMergeSort, ThreadedPagedMergeSort
14
13
786
785
next_page_A (pages:: Pages ) = Pages (pages. nextA, pages. currentNumber + 1 , pages. nextA + 1 , pages. nextB)
787
786
next_page_B (pages:: Pages ) = Pages (pages. nextB, pages. currentNumber + 1 , pages. nextA, pages. nextB + 1 )
788
787
789
- function next_page! (pageLocations, pages, pagesize, lo, a)
788
+ Base . @propagate_inbounds function next_page! (pageLocations, pages, pagesize, lo, a)
790
789
if a > pages. nextA * pagesize + lo
791
790
pages = next_page_A (pages)
792
791
else
@@ -796,6 +795,31 @@ function next_page!(pageLocations, pages, pagesize, lo, a)
796
795
pages
797
796
end
798
797
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
+
799
823
# merge v[lo:m] (A) and v[m+1:hi] (B) using buffer buf in O(sqrt(n)) space
800
824
function paged_merge! (v:: AbstractVector{T} , lo:: Integer , m:: Integer , hi:: Integer , o:: Ordering , buf:: AbstractVector{T} , pageLocations:: AbstractVector{<:Integer} ) where T
801
825
@assert lo < m < hi
@@ -829,7 +853,7 @@ function paged_merge!(v::AbstractVector{T}, lo::Integer, m::Integer, hi::Integer
829
853
# #################
830
854
# merge the first 3 pages into buf
831
855
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
833
857
pageLocations .= 0
834
858
pageLocations[1 : 3 ] = - 1 : - 1 : - 3
835
859
currentPage = 0
@@ -896,66 +920,51 @@ function paged_merge!(v::AbstractVector{T}, lo::Integer, m::Integer, hi::Integer
896
920
# ########################################
897
921
nFreePagesB = nPages + 1 - pages. nextB
898
922
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
909
923
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
911
943
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 ]
917
944
# #################
918
945
# rearrange pages
919
946
# #################
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
920
953
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]
958
965
end
966
+ pageLocations[donePageIndex] = - 1
967
+ copy_pages! (v, buf, pageLocations, currentPage, page_offset, pagesize)
959
968
end
960
969
end
961
970
end
0 commit comments