Commit Diff


commit - da735c49230ce27cc18449d4d5d7f3bcfecaa7de
commit + ef56551877144c333eb315c393d159d2414ae0f3
blob - e152b15b776fb2c8c69f76664a019ec0248d7a59
blob + 6c63e57c408b7f62baa2bed580e9fa6b81c19e00
--- include/lflinkedlist.h
+++ include/lflinkedlist.h
@@ -34,7 +34,17 @@ int ll_remove_next(List *list, ListNode *node, void **
 
 int ll_remove_prev(List *list, ListNode *node, void **data);
 
+ListNode *ll_find(List *list, const void *data);
+
+void ll_reverse(List *list);
+
+void ll_sort(List *list, int (*cmp)(const void *a, const void *b));
+
+void **ll_to_array(List *list);
+
 /* Provides ListNode *node for the iteration loop */
 #define LL_ITER(list) for(ListNode *node = (list)->head; node != NULL; node = node->next)
 
+#define LL_ITER_REV(list) for(ListNode *node = (list)->tail; node != NULL; node = node->prev)
+
 #endif
blob - 53ec9d9a88a03fb3090e822ddcc9959b4fef0560
blob + 3efce6794774833d7c2752b2fe570b9e354da7dd
--- src/linkedlist.c
+++ src/linkedlist.c
@@ -120,3 +120,110 @@ int ll_remove_prev(List *list, ListNode *node, void **
     return ll_remove(list, node->prev, data);
 }
 
+ListNode *ll_find(List *list, const void *data) {
+    if (list->match == NULL) {
+        return NULL;
+    }
+    for (ListNode *node = list->head; node != NULL; node = node->next) {
+        if (list->match(data, node->data)) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+void ll_reverse(List *list) {
+    ListNode *cur = list->head;
+    ListNode *tmp;
+    while (cur != NULL) {
+        tmp = cur->next;
+        cur->next = cur->prev;
+        cur->prev = tmp;
+        cur = tmp;
+    }
+    tmp = list->head;
+    list->head = list->tail;
+    list->tail = tmp;
+}
+
+static ListNode *ll_merge(ListNode *a, ListNode *b,
+                           int (*cmp)(const void *, const void *)) {
+    ListNode dummy;
+    ListNode *tail = &dummy;
+    dummy.next = NULL;
+
+    while (a != NULL && b != NULL) {
+        if (cmp(a->data, b->data) <= 0) {
+            tail->next = a;
+            a->prev = tail;
+            a = a->next;
+        } else {
+            tail->next = b;
+            b->prev = tail;
+            b = b->next;
+        }
+        tail = tail->next;
+    }
+
+    tail->next = (a != NULL) ? a : b;
+    if (tail->next != NULL) {
+        tail->next->prev = tail;
+    }
+
+    dummy.next->prev = NULL;
+    return dummy.next;
+}
+
+static ListNode *ll_mergesort(ListNode *head,
+                               int (*cmp)(const void *, const void *)) {
+    if (head == NULL || head->next == NULL) {
+        return head;
+    }
+
+    /* split in half with slow/fast pointers */
+    ListNode *slow = head;
+    ListNode *fast = head->next;
+    while (fast != NULL && fast->next != NULL) {
+        slow = slow->next;
+        fast = fast->next->next;
+    }
+    ListNode *second = slow->next;
+    slow->next = NULL;
+    if (second != NULL) {
+        second->prev = NULL;
+    }
+
+    ListNode *a = ll_mergesort(head, cmp);
+    ListNode *b = ll_mergesort(second, cmp);
+    return ll_merge(a, b, cmp);
+}
+
+void ll_sort(List *list, int (*cmp)(const void *a, const void *b)) {
+    if (list->size < 2) {
+        return;
+    }
+    list->head = ll_mergesort(list->head, cmp);
+
+    /* fix tail pointer */
+    ListNode *node = list->head;
+    while (node->next != NULL) {
+        node = node->next;
+    }
+    list->tail = node;
+}
+
+void **ll_to_array(List *list) {
+    if (list->size == 0) {
+        return NULL;
+    }
+    void **arr = malloc(list->size * sizeof(void *));
+    if (arr == NULL) {
+        return NULL;
+    }
+    size_t i = 0;
+    for (ListNode *node = list->head; node != NULL; node = node->next) {
+        arr[i++] = node->data;
+    }
+    return arr;
+}
+
blob - 9e68fc54b2da44997bbd2dcc4d980f68c25aaef4
blob + e9ea33f070449ed8609fba04178972b065dba6a4
--- src/set.c
+++ src/set.c
@@ -18,14 +18,7 @@ int set_insert(Set *set, const void *data) {
 }
 
 int set_remove(Set *set, void **data) {
-    ListNode *node = NULL;
-
-    for (node = set->head; node != NULL; node = node->next) {
-        if (set->match(*data, node->data)) {
-            break;
-        }
-    }
-
+    ListNode *node = ll_find(set, *data);
     if (node == NULL) {
         return -1;
     }
@@ -96,12 +89,7 @@ int set_difference(Set *setd, const Set *a, const Set 
 }
 
 int set_is_member(const Set *set, const void *data) {
-    for (ListNode *node = set->head; node != NULL; node = node->next) {
-        if (set->match(data, node->data)) {
-            return 1;
-        }
-    }
-    return 0;
+    return ll_find((List *)set, data) != NULL;
 }
 
 int set_is_subset(const Set *a, const Set *b) {
blob - 02121eab523233f0ce728df2fc6931aba6901619
blob + ff52d5816b27025a8199993784ea05baa32931ad
--- tests/test_linkedlist.c
+++ tests/test_linkedlist.c
@@ -3,7 +3,18 @@
 #include "lftest.h"
 #include "lflinkedlist.h"
 
+static int match_int(const void *a, const void *b) {
+    return *(const int *)a == *(const int *)b;
+}
+
+static int cmp_int(const void *a, const void *b) {
+    int x = *(const int *)a;
+    int y = *(const int *)b;
+    return (x > y) - (x < y);
+}
+
 int main() {
+    /* basic insert/remove */
     List *list = malloc(sizeof(List));
     ll_init(list, NULL);
 
@@ -25,5 +36,107 @@ int main() {
     ll_destroy(list);
     free(list);
 
+    /* ll_find */
+    list = malloc(sizeof(List));
+    ll_init(list, NULL);
+    list->match = match_int;
+
+    int a = 10, b = 20, c = 30;
+    ll_ins_next(list, list->head, &a);
+    ll_ins_next(list, list->tail, &b);
+    ll_ins_next(list, list->tail, &c);
+
+    ListNode *found = ll_find(list, &b);
+    ASSERT_NOT_NULL(found);
+    ASSERT_EQ(*(int *)found->data, 20);
+
+    int missing = 99;
+    found = ll_find(list, &missing);
+    ASSERT_NULL(found);
+
+    /* ll_find with no match callback */
+    list->match = NULL;
+    found = ll_find(list, &a);
+    ASSERT_NULL(found);
+    list->match = match_int;
+
+    /* ll_reverse */
+    ll_reverse(list);
+    ASSERT_EQ(*(int *)list->head->data, 30);
+    ASSERT_EQ(*(int *)list->tail->data, 10);
+
+    int expected_rev[] = {30, 20, 10};
+    int idx = 0;
+    LL_ITER(list) {
+        ASSERT_EQ(*(int *)node->data, expected_rev[idx++]);
+    }
+
+    /* LL_ITER_REV */
+    int expected_fwd[] = {10, 20, 30};
+    idx = 0;
+    LL_ITER_REV(list) {
+        ASSERT_EQ(*(int *)node->data, expected_fwd[idx++]);
+    }
+
+    /* ll_to_array */
+    void **arr = ll_to_array(list);
+    ASSERT_NOT_NULL(arr);
+    ASSERT_EQ(*(int *)arr[0], 30);
+    ASSERT_EQ(*(int *)arr[1], 20);
+    ASSERT_EQ(*(int *)arr[2], 10);
+    free(arr);
+
+    ll_destroy(list);
+    free(list);
+
+    /* ll_sort */
+    list = malloc(sizeof(List));
+    ll_init(list, NULL);
+
+    int s1 = 3, s2 = 1, s3 = 5, s4 = 2, s5 = 4;
+    ll_ins_next(list, list->head, &s1);
+    ll_ins_next(list, list->tail, &s2);
+    ll_ins_next(list, list->tail, &s3);
+    ll_ins_next(list, list->tail, &s4);
+    ll_ins_next(list, list->tail, &s5);
+
+    ll_sort(list, cmp_int);
+    ASSERT_EQ(list->size, 5);
+    ASSERT_EQ(*(int *)list->head->data, 1);
+    ASSERT_EQ(*(int *)list->tail->data, 5);
+
+    int expected_sorted[] = {1, 2, 3, 4, 5};
+    idx = 0;
+    LL_ITER(list) {
+        ASSERT_EQ(*(int *)node->data, expected_sorted[idx++]);
+    }
+
+    /* verify prev pointers are correct after sort */
+    idx = 4;
+    LL_ITER_REV(list) {
+        ASSERT_EQ(*(int *)node->data, expected_sorted[idx--]);
+    }
+
+    /* sort empty and single-element lists */
+    ll_destroy(list);
+    ll_init(list, NULL);
+    ll_sort(list, cmp_int);
+    ASSERT_EQ(list->size, 0);
+
+    int solo = 42;
+    ll_ins_next(list, list->head, &solo);
+    ll_sort(list, cmp_int);
+    ASSERT_EQ(list->size, 1);
+    ASSERT_EQ(*(int *)list->head->data, 42);
+
+    /* ll_to_array on empty list */
+    ll_destroy(list);
+    ll_init(list, NULL);
+    arr = ll_to_array(list);
+    ASSERT_NULL(arr);
+
+    ll_destroy(list);
+    free(list);
+
     TEST_REPORT();
 }