diff --git a/core/slice/sort_private.odin b/core/slice/sort_private.odin index efa9c596b..35886c8c4 100644 --- a/core/slice/sort_private.odin +++ b/core/slice/sort_private.odin @@ -13,7 +13,7 @@ Sort_Kind :: enum { Cmp, } -_stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check { +_stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) { less :: #force_inline proc(a, b: E, call: P) -> bool { when KIND == .Ordered { return a < b @@ -25,14 +25,102 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O #panic("unhandled Sort_Kind") } } - - // insertion sort - // TODO(bill): use a different algorithm as insertion sort is O(n^2) - n := len(data) - for i in 1.. 0 && less(data[j], data[j-1], call); j -= 1 { - swap(data, j, j-1) + + merge_rotate(data, call) + + insertion_sort :: proc(data: T, call: P){ + for i in 1.. 0 && less(temp, data[j - 1], call); j -= 1 { + data[j] = data[j - 1] + } + data[j] = temp + } + } + + merge_rotate :: proc(data: T, call: P){ + if len(data) <= 64 { + insertion_sort(data, call) + return + } + + mid := len(data) / 2 + merge_rotate(data[:mid], call) + merge_rotate(data[mid:], call) + + merge(data, mid, len(data) - mid, call) + } + + bin_search_left :: proc(data:T, value: E, call: P) -> int{ + from := 0 + len := len(data) + + for len > 0 { + half := len / 2 + mid := from + half + + if less(data[mid], value, call){ + from = mid + 1 + len -= half + 1 + } else { + len = half + } } + return from + } + + bin_search_right :: proc(data: T, value: E, call: P) -> int { + from := 0 + len := len(data) + + for len > 0 { + half := len / 2 + mid := from + half + + if less(value, data[mid], call){ + len = half + } else { + from = mid + 1 + len -= half + 1 + } + } + return from + } + + merge :: proc(data: T, left, right: int, call: P) { + if left == 0 || right == 0 { + return + } + + if left + right == 2 { + if less(data[1],data[0], call) { + data[1], data[0] = data[0], data[1] + } + return + } + + first_cut, second_cut : int + left2, right2 : int + if left > right { + left2 = left / 2 + first_cut = left2 + + second_cut = left + bin_search_left(data[left:], data[first_cut], call) + right2 = second_cut - left + } else { + right2 = right / 2 + second_cut = left + right2 + + first_cut = bin_search_right(data[:left], data[second_cut], call) + left2 = first_cut + } + + slice.rotate_left(data[first_cut:second_cut], left - first_cut) + new_mid := first_cut + right2 + + merge(data[:new_mid], left2, right2, call) + merge(data[new_mid:], left-left2, right-right2, call) } }