add basic quicksort support (#816)

* lib/std/sort: refactor binarysearch namespace to prepare for sorting

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* std/lib/sort: add basic quicksort support

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/hash: use method first parameter inferred type

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

* lib/std/hash: add fnv64a support

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>

---------

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
This commit is contained in:
Pierre Curto
2023-07-04 20:15:03 +02:00
committed by GitHub
parent 6231cc83d9
commit f8a3e4f6f0
10 changed files with 255 additions and 57 deletions

View File

@@ -8,17 +8,17 @@ struct Crc32
uint result;
}
fn void Crc32.init(Crc32* this, uint seed = 0)
fn void Crc32.init(&this, uint seed = 0)
{
this.result = ~seed;
}
fn void Crc32.updatec(Crc32* this, char c)
fn void Crc32.updatec(&this, char c)
{
this.result = (this.result >> 8) ^ CRC32_TABLE[(this.result ^ c) & 0xFF];
}
fn void Crc32.update(Crc32* this, char[] data)
fn void Crc32.update(&this, char[] data)
{
uint result = this.result;
foreach (char x : data)
@@ -28,7 +28,7 @@ fn void Crc32.update(Crc32* this, char[] data)
this.result = result;
}
fn uint Crc32.final(Crc32* this)
fn uint Crc32.final(&this)
{
return ~this.result;
}

View File

@@ -8,17 +8,17 @@ struct Crc64
ulong result;
}
fn void Crc64.init(Crc64* this, uint seed = 0)
fn void Crc64.init(&this, uint seed = 0)
{
this.result = seed;
}
fn void Crc64.updatec(Crc64* this, char c)
fn void Crc64.updatec(&this, char c)
{
this.result = (this.result << 8) ^ CRC64_TABLE[(char)((this.result >> 56) ^ c)];
}
fn void Crc64.update(Crc64* this, char[] data)
fn void Crc64.update(&this, char[] data)
{
ulong result = this.result;
foreach (char x : data)
@@ -28,7 +28,7 @@ fn void Crc64.update(Crc64* this, char[] data)
this.result = result;
}
fn ulong Crc64.final(Crc64* this)
fn ulong Crc64.final(&this)
{
return this.result;
}

View File

@@ -10,12 +10,12 @@ const FNV32A_MUL @private = 0x01000193;
macro void @update(uint &h, char x) @private => h = (h * FNV32A_MUL) ^ x;
fn void Fnv32a.init(Fnv32a* this)
fn void Fnv32a.init(&this)
{
*this = FNV32A_START;
}
fn void Fnv32a.update(Fnv32a* this, char[] data)
fn void Fnv32a.update(&this, char[] data)
{
uint h = (uint)*this;
foreach (char x : data)
@@ -25,7 +25,7 @@ fn void Fnv32a.update(Fnv32a* this, char[] data)
*this = (Fnv32a)h;
}
macro void Fnv32a.update_char(Fnv32a* this, char c)
macro void Fnv32a.update_char(&this, char c)
{
@update(*this, x);
}

41
lib/std/hash/fnv64a.c3 Normal file
View File

@@ -0,0 +1,41 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::fnv64a;
def Fnv64a = distinct ulong;
const FNV64A_START @private = 0xcbf29ce484222325;
const FNV64A_MUL @private = 0x00000100000001b3;
macro void @update(ulong &h, char x) @private => h = (h * FNV64A_MUL) ^ x;
fn void Fnv64a.init(&this)
{
*this = FNV64A_START;
}
fn void Fnv64a.update(&this, char[] data)
{
ulong h = (ulong)*this;
foreach (char x : data)
{
@update(h, x);
}
*this = (Fnv64a)h;
}
macro void Fnv64a.update_char(&this, char c)
{
@update(*this, x);
}
fn ulong encode(char[] data)
{
ulong h = FNV64A_START;
foreach (char x : data)
{
@update(h, x);
}
return h;
}

View File

@@ -1,12 +1,12 @@
module std::sort::binarysearch;
module std::sort;
/**
* Perform a binary search over the sorted array and return the smallest index
* in [0, array.len) where cmp(i) is true and cmp(j) is true for j in [i, array.len).
* @require is_searchable(list) "The list must be indexable and support .len or .len()"
* @require is_comparer(cmp, list) "Expeced a comparison function which compares values"
* @require is_comparer(cmp, list) "Expected a comparison function which compares values"
**/
macro usz cmp_search(list, x, cmp)
macro usz binarysearch_with(list, x, cmp)
{
usz i;
usz len = @len_from_list(list);
@@ -34,7 +34,7 @@ macro usz cmp_search(list, x, cmp)
* @require is_searchable(list) "The list must be indexable and support .len or .len()"
* @checked less(list[0], x) "The values must be comparable"
**/
macro usz search(list, x)
macro usz binarysearch(list, x)
{
usz i;
usz len = @len_from_list(list);
@@ -48,24 +48,4 @@ macro usz search(list, x)
}
}
return i;
}
macro bool is_searchable(list) @local
{
return $checks(list[0]) && ($checks(list.len) || $checks(list.len()));
}
macro usz @len_from_list(&list) @local
{
$if $checks(list.len()):
return list.len();
$else
return list.len;
$endif
}
macro bool is_comparer(cmp, list)
{
return $checks(int i = cmp(list[0], list[0]))
|| $checks(int i = cmp(&list[0], &list[0]));
}
}

66
lib/std/sort/quicksort.c3 Normal file
View File

@@ -0,0 +1,66 @@
module std::sort;
/**
* @require is_searchable(list) "The list must be indexable and support .len or .len()"
**/
macro quicksort(list, $Type)
{
(($Type)(list)).sort(null);
}
/**
* @require is_searchable(list) "The list must be indexable and support .len or .len()"
* @require is_comparer(cmp, list) "Expected a comparison function which compares values"
**/
macro quicksort_with(list, $Type, cmp)
{
(($Type)(list)).sort(cmp);
}
module std::sort::quicksort<Type, Comparer>;
import std::sort;
def Quicksort = distinct Type[];
fn void Quicksort.sort(qs, Comparer cmp)
{
usz len = sort::@len_from_list(qs);
qs.qsort(0, (isz)len - 1, cmp);
}
fn void Quicksort.qsort(Quicksort qs, isz low, isz high, Comparer cmp) @private
{
if (low < high)
{
isz p = qs.partition(low, high, cmp);
qs.qsort(low, p - 1, cmp);
qs.qsort(p + 1, high, cmp);
}
}
fn isz Quicksort.partition(qs, isz low, isz high, Comparer cmp) @inline @private
{
Type pivot = qs[high];
isz i = low - 1;
for (isz j = low; j < high; j++)
{
$if $checks(cmp(qs[0], qs[0])):
int res = cmp(qs[j], pivot);
$else
$if $checks(cmp(&qs[0], &qs[0])):
int res = cmp(&qs[j], &pivot);
$else
int res;
if (greater(qs[j], pivot)) res = 1;
$endif
$endif
if (res <= 0)
{
i++;
@swap(qs[i], qs[j]);
}
}
i++;
@swap(qs[i], qs[high]);
return i;
}

21
lib/std/sort/sort.c3 Normal file
View File

@@ -0,0 +1,21 @@
module std::sort;
macro bool is_searchable(list)
{
return $checks(list[0]) && ($checks(list.len) || $checks(list.len()));
}
macro usz @len_from_list(&list)
{
$if $checks(list.len()):
return list.len();
$else
return list.len;
$endif
}
macro bool is_comparer(cmp, list)
{
return $checks(int i = cmp(list[0], list[0]))
|| $checks(int i = cmp(&list[0], &list[0]));
}

View File

@@ -1,16 +1,16 @@
module binarysearch_test @test;
import std::sort::binarysearch;
module sort_test @test;
import std::sort;
struct SearchTest
struct BinarySearchTest
{
int[] data;
int x;
int index;
}
fn void search()
fn void binarysearch()
{
SearchTest[] tcases = {
BinarySearchTest[] tcases = {
{ {}, 0, 0 },
{ {1, 2, 3}, 1, 0 },
{ {1, 2, 3}, 2, 1 },
@@ -22,28 +22,17 @@ fn void search()
foreach (tc : tcases)
{
usz idx = binarysearch::search(tc.data, tc.x);
usz idx = sort::binarysearch(tc.data, tc.x);
assert(idx == tc.index, "%s: got %d; want %d", tc.data, idx, tc.index);
usz cmp_idx = binarysearch::cmp_search(tc.data, tc.x, &cmp_int);
usz cmp_idx = sort::binarysearch_with(tc.data, tc.x, &sort::cmp_int);
assert(cmp_idx == tc.index, "%s: got %d; want %d", tc.data, cmp_idx, tc.index);
usz cmp_idx2 = binarysearch::cmp_search(tc.data, tc.x, &cmp_int2);
usz cmp_idx2 = sort::binarysearch_with(tc.data, tc.x, &sort::cmp_int2);
assert(cmp_idx2 == tc.index, "%s: got %d; want %d", tc.data, cmp_idx2, tc.index);
usz cmp_idx3 = binarysearch::cmp_search(tc.data, tc.x, fn int(int a, int b) => a - b);
usz cmp_idx3 = sort::binarysearch_with(tc.data, tc.x, fn int(int a, int b) => a - b);
assert(cmp_idx3 == tc.index, "%s: got %d; want %d", tc.data, cmp_idx2, tc.index);
}
}
module binarysearch_test;
fn int cmp_int(void* x, void* y) {
return *(int*)x - *(int*)y;
}
fn int cmp_int2(int x, int y) {
return x - y;
}
}

View File

@@ -0,0 +1,92 @@
module sort_test @test;
import std::sort;
import std::sort::quicksort;
def QSInt = quicksort::Quicksort<int, void*>;
fn void quicksort()
{
int[][] tcases = {
{},
{10, 3},
{3, 2, 1},
{1, 2, 3},
{2, 1, 3},
};
foreach (tc : tcases)
{
sort::quicksort(tc, QSInt);
assert(sort::check_int_sort(tc));
}
}
def Cmp = fn int (void*, void*);
def QSIntCmp = quicksort::Quicksort<int, Cmp>;
fn void quicksort_with()
{
int[][] tcases = {
{},
{10, 3},
{3, 2, 1},
{1, 2, 3},
{2, 1, 3},
};
foreach (tc : tcases)
{
sort::quicksort_with(tc, QSIntCmp, &sort::cmp_int);
assert(sort::check_int_sort(tc));
}
}
def Cmp2 = fn int (int, int);
def QSIntCmp2 = quicksort::Quicksort<int, Cmp2>;
fn void quicksort_with2()
{
int[][] tcases = {
{},
{10, 3},
{3, 2, 1},
{1, 2, 3},
{2, 1, 3},
};
foreach (tc : tcases)
{
sort::quicksort_with(tc, QSIntCmp2, &sort::cmp_int2);
assert(sort::check_int_sort(tc));
}
}
fn void quicksort_with_lambda()
{
int[][] tcases = {
{},
{10, 3},
{3, 2, 1},
{1, 2, 3},
{2, 1, 3},
};
foreach (tc : tcases)
{
sort::quicksort_with(tc, QSIntCmp2, fn int(int a, int b) => a - b);
assert(sort::check_int_sort(tc));
}
}
module std::sort;
fn bool check_int_sort(int[] list)
{
int prev = int.min;
foreach (x : list)
{
if (prev > x) return false;
prev = x;
}
return true;
}

View File

@@ -0,0 +1,9 @@
module std::sort;
fn int cmp_int(void* x, void* y) {
return *(int*)x - *(int*)y;
}
fn int cmp_int2(int x, int y) {
return x - y;
}