Files
Odin/core/slice/sort.odin
T
2022-06-07 14:07:19 +02:00

172 lines
3.8 KiB
Odin

package slice
Ordering :: enum {
Less = -1,
Equal = 0,
Greater = +1,
}
cmp :: proc(a, b: $E) -> Ordering where ORD(E) {
switch {
case a < b:
return .Less
case a > b:
return .Greater
}
return .Equal
}
cmp_proc :: proc($E: typeid) -> (proc(E, E) -> Ordering) where ORD(E) {
return proc(a, b: E) -> Ordering {
switch {
case a < b:
return .Less
case a > b:
return .Greater
}
return .Equal
}
}
// sort sorts a slice
// This sort is not guaranteed to be stable
sort :: proc(data: $T/[]$E) where ORD(E) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
_quick_sort_general(data, 0, n, _max_depth(n), struct{}{}, .Ordered)
}
}
}
// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
// This sort is not guaranteed to be stable
sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
_quick_sort_general(data, 0, n, _max_depth(n), less, .Less)
}
}
}
sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
_quick_sort_general(data, 0, n, _max_depth(n), cmp, .Cmp)
}
}
}
// stable_sort sorts a slice
stable_sort :: proc(data: $T/[]$E) where ORD(E) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
_stable_sort_general(data, struct{}{}, .Ordered)
}
}
}
// stable_sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
stable_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
_stable_sort_general(data, less, .Less)
}
}
}
stable_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
_stable_sort_general(data, cmp, .Cmp)
}
}
}
is_sorted :: proc(array: $T/[]$E) -> bool where ORD(E) {
for i := len(array)-1; i > 0; i -= 1 {
if array[i] < array[i-1] {
return false
}
}
return true
}
is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
for i := len(array)-1; i > 0; i -= 1 {
if less(array[i], array[i-1]) {
return false
}
}
return true
}
is_sorted_by_cmp :: is_sorted_cmp
is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool {
for i := len(array)-1; i > 0; i -= 1 {
if cmp(array[i], array[i-1]) == .Less {
return false
}
}
return true
}
reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
sort_by(data, proc(i, j: E) -> bool {
return j < i
})
}
reverse_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) where ORD(E) {
context._internal = rawptr(less)
sort_by(data, proc(i, j: E) -> bool {
k := (proc(i, j: E) -> bool)(context._internal)
return k(j, i)
})
}
reverse_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) where ORD(E) {
context._internal = rawptr(cmp)
sort_by_cmp(data, proc(i, j: E) -> Ordering {
k := (proc(i, j: E) -> Ordering)(context._internal)
return k(j, i)
})
}
// TODO(bill): Should `sort_by_key` exist or is `sort_by` more than enough?
sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
context._internal = rawptr(key)
sort_by(data, proc(i, j: E) -> bool {
k := (proc(E) -> K)(context._internal)
return k(i) < k(j)
})
}
reverse_sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
context._internal = rawptr(key)
sort_by(data, proc(i, j: E) -> bool {
k := (proc(E) -> K)(context._internal)
return k(j) < k(i)
})
}
is_sorted_by_key :: proc(array: $T/[]$E, key: proc(E) -> $K) -> bool where ORD(K) {
for i := len(array)-1; i > 0; i -= 1 {
if key(array[i]) < key(array[i-1]) {
return false
}
}
return true
}
@(private)
_max_depth :: proc(n: int) -> (depth: int) { // 2*ceil(log2(n+1))
for i := n; i > 0; i >>= 1 {
depth += 1
}
return depth * 2
}