Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Sorting #761

Merged
merged 5 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macOS-latest, macOS-13, macOS-12]
os: [macOS-latest, macOS-15, macOS-13]

steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 4 additions & 0 deletions ops/checkTests.du
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const ignored = {
'read.txt',
'read2.txt',
],
'lists': [
'sorted.txt',
'unsorted.txt',
],
'strings': [
'test',
],
Expand Down
105 changes: 17 additions & 88 deletions src/vm/datatypes/lists/lists.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,91 +309,6 @@ static Value copyListDeep(DictuVM *vm, int argCount, Value *args) {
return OBJ_VAL(list);
}


int compareString(ObjString *operandOne, ObjString *operandTwo) {
return strcmp(operandOne->chars, operandTwo->chars);
}

static int partitionStringList(ObjList *arr, int start, int end) {
int pivot_index = (int) floor(start + end) / 2;

ObjString *pivot = AS_STRING(arr->values.values[pivot_index]);

int i = start - 1;
int j = end + 1;

for (;;) {
do {
i = i + 1;
} while (compareString(AS_STRING(arr->values.values[i]), pivot) < 0);

do {
j = j - 1;
} while (compareString(AS_STRING(arr->values.values[j]), pivot) > 0);

if (i >= j) {
return j;
}

// Swap arr[i] with arr[j]
Value temp = arr->values.values[i];

arr->values.values[i] = arr->values.values[j];
arr->values.values[j] = temp;
}
}


static int partitionNumberList(ObjList *arr, int start, int end) {
int pivot_index = (int) floor(start + end) / 2;

double pivot = AS_NUMBER(arr->values.values[pivot_index]);

int i = start - 1;
int j = end + 1;

for (;;) {
do {
i = i + 1;
} while (AS_NUMBER(arr->values.values[i]) < pivot);

do {
j = j - 1;
} while (AS_NUMBER(arr->values.values[j]) > pivot);

if (i >= j) {
return j;
}

// Swap arr[i] with arr[j]
Value temp = arr->values.values[i];

arr->values.values[i] = arr->values.values[j];
arr->values.values[j] = temp;
}
}

// Implementation of Quick Sort using the Hoare
// Partition scheme.
// Best Case O(n log n)
// Worst Case O(n^2) (If the list is already sorted.)
static void quickSort(ObjList *arr, int start, int end, int (*partition)(ObjList *, int, int)) {
while (start < end) {
int part = partition(arr, start, end);

// Recurse for the smaller halve.
if (part - start < end - part) {
quickSort(arr, start, part, partition);

start = start + 1;
} else {
quickSort(arr, part + 1, end, partition);

end = end - 1;
}
}
}

bool isNumberList(ObjList *list) {
for (int i = 0; i < list->values.count; i++) {
if (!IS_NUMBER(list->values.values[i])) {
Expand All @@ -412,6 +327,20 @@ bool isStringList(ObjList *list) {
return true;
}

int compareNumbers(const void* a, const void* b) {
Value aVal = *(Value *)a;
Value bVal = *(Value *)b;

return AS_NUMBER(aVal) - AS_NUMBER(bVal);
}

int compareString(const void* a, const void* b) {
Value aVal = *(Value *)a;
Value bVal = *(Value *)b;

return utf8cmp(AS_CSTRING(aVal), AS_CSTRING(bVal));
}

static Value sortList(DictuVM *vm, int argCount, Value *args) {
if (argCount != 0) {
runtimeError(vm, "sort() takes no arguments (%d given)", argCount);
Expand All @@ -421,17 +350,17 @@ static Value sortList(DictuVM *vm, int argCount, Value *args) {
ObjList *list = AS_LIST(args[0]);

if (isNumberList(list)) {
quickSort(list, 0, list->values.count - 1, &partitionNumberList);
qsort(list->values.values, list->values.count, sizeof(*list->values.values), compareNumbers);
return NIL_VAL;
}

if (isStringList(list)) {
quickSort(list, 0, list->values.count - 1, &partitionStringList);
qsort(list->values.values, list->values.count, sizeof(*list->values.values), compareString);
return NIL_VAL;
}

runtimeError(vm,
"sort() takes lists with either numbers or string (other types and heterotypic lists are not supported)");
"sort() takes lists with either numbers or string (other types and heterogeneous lists are not supported)");
return EMPTY_VAL;
}

Expand Down
1 change: 1 addition & 0 deletions src/vm/datatypes/lists/lists.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "../copy.h"
#include "../../util.h"
#include "../../utf8.h"

void declareListMethods(DictuVM *vm);

Expand Down
40 changes: 40 additions & 0 deletions tests/lists/sort.du
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,46 @@ class TestListSort < UnitTest {
z.sort();
this.assertEquals(z, ["abc", "abc", "cde", "cde"]);
}

testStringLargeSort() {
var contents;
with("tests/lists/unsorted.txt", "r") {
contents = file.read();
}

const listContents = contents.split("\n");
listContents.sort();

with("tests/lists/sorted.txt", "r") {
var line;
var index = 0;
// When you reach the end of the file, nil is returned
while((line = file.readLine()) != nil) {
this.assertEquals(listContents[index], line);
index += 1;
}
}
}

testNumberLargeSort() {
var contents;
with("tests/lists/unsorted.txt", "r") {
contents = file.read();
}

const listContents = contents.split("\n");
listContents.sort();

with("tests/lists/sorted.txt", "r") {
var line;
var index = 0;
// When you reach the end of the file, nil is returned
while((line = file.readLine()) != nil) {
this.assertEquals(listContents[index].toNumber().unwrap(), line.toNumber().unwrap());
index += 1;
}
}
}
}

TestListSort().run();
Loading
Loading