mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
172
lib/std/sort/countingsort.c3
Normal file
172
lib/std/sort/countingsort.c3
Normal file
@@ -0,0 +1,172 @@
|
||||
module std::sort;
|
||||
import std::sort::is;
|
||||
import std::sort::cs;
|
||||
import std::sort::qs;
|
||||
|
||||
/**
|
||||
* Sort list using the counting sort algorithm.
|
||||
* @require $defined(list[0]) && $defined(list.len) "The list must be indexable and support .len or .len()"
|
||||
* @require $or(@typeid(key) == void*.typeid, @is_keyer(key, list)) "Expected a transformation function which returns a key-like type, think integral types"
|
||||
**/
|
||||
macro countingsort(list, key = null) @builtin
|
||||
{
|
||||
var $Type = $typeof(list);
|
||||
var $KeyFuncType = $typeof(key);
|
||||
usz len = sort::@len_from_list(list);
|
||||
cs::csort(<$Type, $KeyFuncType>)(list, 0, len, key, ~((uint)0));
|
||||
}
|
||||
|
||||
macro insertionsort_indexed(list, start, end, cmp = null) @builtin
|
||||
{
|
||||
var $Type = $typeof(list);
|
||||
var $CmpFuncType = $typeof(cmp);
|
||||
is::isort(<$Type, $CmpFuncType>)(list, (usz)start, (usz)end, cmp);
|
||||
}
|
||||
|
||||
macro quicksort_indexed(list, start, end, cmp = null) @builtin
|
||||
{
|
||||
var $Type = $typeof(list);
|
||||
var $CmpFuncType = $typeof(cmp);
|
||||
qs::qsort(<$Type, $CmpFuncType>)(list, (isz)start, (isz)(end-1), cmp);
|
||||
}
|
||||
|
||||
module std::sort::cs(<Type, Keyer>);
|
||||
|
||||
def Counts = ulong[256] @private;
|
||||
def Ranges = ulong[257] @private;
|
||||
def Indexs = char[256] @private;
|
||||
def ElementType = $typeof(Type{}[0]);
|
||||
//def KeyerReturnTypeid = $typeof(Keyer.returns); //Keyer{}(Keyer.params[0]{})
|
||||
def KeyerReturnType = $evaltype(Keyer.returns.qnameof);
|
||||
def CmpCallback = fn bool(ElementType*, ElementType*);
|
||||
|
||||
fn void csort(Type list, usz low, usz high, Keyer key, uint byte_idx)
|
||||
{
|
||||
var $no_key = Keyer.typeid == void*.typeid;
|
||||
var $key_by_value = $and(!$no_key, Keyer.params[0] == @typeid(list[0]));
|
||||
var $list_has_get_ref = $defined(list.get_ref(0));
|
||||
|
||||
byte_idx = byte_idx >= KeyerReturnType.sizeof ? KeyerReturnType.sizeof-1 : byte_idx;
|
||||
|
||||
if (high <= low) {
|
||||
return;
|
||||
}
|
||||
|
||||
Counts counts;
|
||||
Ranges ranges;
|
||||
Indexs indexs;
|
||||
|
||||
KeyerReturnType mn = ~((KeyerReturnType)0);
|
||||
KeyerReturnType mx = (KeyerReturnType)0;
|
||||
|
||||
char last_key = 0;
|
||||
char keys_ordered = 1;
|
||||
|
||||
for (usz i = 0; i < 256; i++) {
|
||||
counts[i] = 0;
|
||||
}
|
||||
|
||||
for (usz i = low; i < high; i++) {
|
||||
$if $list_has_get_ref:
|
||||
$if $key_by_value:
|
||||
KeyerReturnType k = key(*list.get_ref(i));
|
||||
$else
|
||||
KeyerReturnType k = key(list.get_ref(i));
|
||||
$endif;
|
||||
|
||||
|
||||
$else
|
||||
$if $key_by_value:
|
||||
KeyerReturnType k = key(list[i]);
|
||||
$else
|
||||
KeyerReturnType k = key(&list[i]);
|
||||
$endif;
|
||||
$endif
|
||||
|
||||
char key_byte = (char)((k >> (byte_idx*8)) & 0xff);
|
||||
++counts[key_byte];
|
||||
|
||||
mn = k < mn ? k : mn;
|
||||
mx = k > mx ? k : mx;
|
||||
|
||||
keys_ordered = keys_ordered & (char)(key_byte >= last_key);
|
||||
last_key = key_byte;
|
||||
}
|
||||
|
||||
KeyerReturnType diff = mx-mn;
|
||||
if (diff == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ushort parition_count = 0;
|
||||
usz total = 0;
|
||||
for (usz i = 0; i < 256; i++) {
|
||||
usz count = counts[i];
|
||||
indexs[parition_count] = (char)i;
|
||||
parition_count += (ushort)(count > 0);
|
||||
counts[i] = total;
|
||||
ranges[i] = total;
|
||||
total += count;
|
||||
}
|
||||
ranges[256] = total;
|
||||
|
||||
if (!keys_ordered) {
|
||||
usz sorted_count = 0;
|
||||
//ElementType* first = list.first();
|
||||
do {
|
||||
for (usz x = 0; x < 256; x++) {
|
||||
usz s = counts[x];
|
||||
usz e = ranges[x + 1];
|
||||
|
||||
sorted_count += (e - s);
|
||||
for (; s < e; s++) {
|
||||
$if $list_has_get_ref:
|
||||
$if $key_by_value:
|
||||
KeyerReturnType k = key(*list.get_ref(low+s));
|
||||
$else
|
||||
KeyerReturnType k = key(list.get_ref(low+s));
|
||||
$endif;
|
||||
char k_idx = (char)(k >> (byte_idx*8));
|
||||
usz target_idx = counts[k_idx];
|
||||
|
||||
@swap(list[low+s], list[low+target_idx]);
|
||||
counts[k_idx] += 1;
|
||||
$else
|
||||
$if $key_by_value:
|
||||
KeyerReturnType k = key(list[low+s]);
|
||||
$else
|
||||
KeyerReturnType k = key(&list[low+s]);
|
||||
$endif;
|
||||
char k_idx = (char)(k >> (byte_idx*8));
|
||||
usz target_idx = counts[k_idx];
|
||||
|
||||
@swap(list[low+s], list[low+target_idx]);
|
||||
counts[k_idx] += 1;
|
||||
$endif;
|
||||
}
|
||||
}
|
||||
} while (sorted_count < ranges[256]);
|
||||
}
|
||||
|
||||
if (byte_idx) {
|
||||
for (usz p = 0; p < parition_count; ++p) {
|
||||
usz i = indexs[p];
|
||||
usz start_offset = ranges[i];
|
||||
usz end_offset = ranges[i + 1];
|
||||
|
||||
usz items = end_offset - start_offset;
|
||||
|
||||
if (items <= 32) {
|
||||
insertionsort_indexed(list, low+start_offset, low+end_offset, fn bool (ElementType* lhs, ElementType* rhs) {
|
||||
return Keyer{}(lhs) < Keyer{}(rhs);
|
||||
});
|
||||
} else if (items <= 128) {
|
||||
quicksort_indexed(list, low+start_offset, low+end_offset, fn long (ElementType* lhs, ElementType* rhs) {
|
||||
return (long)(Keyer{}(lhs) - Keyer{}(rhs));
|
||||
});
|
||||
} else {
|
||||
csort(list, low+start_offset, low+end_offset, key, byte_idx-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user