Skip to content

Commit

Permalink
Add timsort to lab0-c
Browse files Browse the repository at this point in the history
Use option sort [0, 1] to choose use merge sort or timsort
  • Loading branch information
aftuta85 committed Mar 28, 2024
1 parent a54a0e4 commit f9ac956
Show file tree
Hide file tree
Showing 2 changed files with 261 additions and 2 deletions.
19 changes: 17 additions & 2 deletions qtest.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ static int fail_count = 0;
static int string_length = MAXSTRING;

static int descend = 0;
static int sort_algo = 0;

#define MIN_RANDSTR_LEN 5
#define MAX_RANDSTR_LEN 10
Expand All @@ -85,6 +86,7 @@ typedef enum {
} position_t;
/* Forward declarations */
static bool q_show(int vlevel);
void timsort(void *priv, struct list_head *head, bool descend);

static bool do_free(int argc, char *argv[])
{
Expand Down Expand Up @@ -542,6 +544,11 @@ static bool do_size(int argc, char *argv[])

int reps = 1;
bool ok = true;
if (argc != 1 && argc != 2) {
report(1, "%s needs 0-1 arguments", argv[0]);
return false;
}

if (argc == 2) {
if (!get_int(argv[1], &reps))
report(1, "Invalid number of calls to size '%s'", argv[2]);
Expand Down Expand Up @@ -594,9 +601,14 @@ bool do_sort(int argc, char *argv[])
report(3, "Warning: Calling sort on single node");
error_check();

int cmp_count = 0;
set_noallocate_mode(true);
if (current && exception_setup(true))
q_sort(current->q, descend);
if (current && exception_setup(true)) {
if (sort_algo == 0)
q_sort(current->q, descend);
else
timsort(&cmp_count, current->q, descend);
}
exception_cancel();
set_noallocate_mode(false);

Expand Down Expand Up @@ -1057,6 +1069,9 @@ static void console_init()
"Number of times allow queue operations to return false", NULL);
add_param("descend", &descend,
"Sort and merge queue in ascending/descending order", NULL);
add_param("sort", &sort_algo,
"Select sort algorithm. 0: Merge sort (default), 1: Timsort",
NULL);
}

/* Signal handlers */
Expand Down
244 changes: 244 additions & 0 deletions queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
* cppcheck-suppress nullPointer
*/

typedef int (*list_cmp_func_t)(void *,
const struct list_head *,
const struct list_head *);

/* Create an empty queue */
struct list_head *q_new()
Expand Down Expand Up @@ -359,3 +362,244 @@ int q_merge(struct list_head *head, bool descend)
q_sort(qc_first->q, descend);
return qc_first->size;
}

static int compare(void *priv,
const struct list_head *a,
const struct list_head *b)
{
if (a == b)
return 0;
element_t *a_e = list_entry(a, element_t, list);
element_t *b_e = list_entry(b, element_t, list);
int res = strcmp(a_e->value, b_e->value);

if (priv)
*((int *) priv) += 1;

return res;
}

static inline size_t run_size(struct list_head *head)
{
if (!head)
return 0;
if (!head->next)
return 1;
return (size_t) (head->next->prev);
}

struct pair {
struct list_head *head, *next;
};

static size_t stk_size;

static struct list_head *merge_run(void *priv,
list_cmp_func_t cmp,
struct list_head *a,
struct list_head *b)
{
struct list_head *head = NULL;
struct list_head **tail = &head;

for (;;) {
/* if equal, take 'a' -- important for sort stability */
if (cmp(priv, a, b) <= 0) {
*tail = a;
tail = &(*tail)->next;
a = a->next;
if (!a) {
*tail = b;
break;
}
} else {
*tail = b;
tail = &(*tail)->next;
b = b->next;
if (!b) {
*tail = a;
break;
}
}
}
return head;
}

static void build_prev_link(struct list_head *head,
struct list_head *tail,
struct list_head *list)
{
tail->next = list;
do {
list->prev = tail;
tail = list;
list = list->next;
} while (list);

/* The final links to make a circular doubly-linked list */
tail->next = head;
head->prev = tail;
}

static void merge_final(void *priv,
list_cmp_func_t cmp,
struct list_head *head,
struct list_head *a,
struct list_head *b)
{
struct list_head *tail = head;

for (;;) {
/* if equal, take 'a' -- important for sort stability */
if (cmp(priv, a, b) <= 0) {
tail->next = a;
a->prev = tail;
tail = a;
a = a->next;
if (!a)
break;
} else {
tail->next = b;
b->prev = tail;
tail = b;
b = b->next;
if (!b) {
b = a;
break;
}
}
}

/* Finish linking remainder of list b on to tail */
build_prev_link(head, tail, b);
}

static struct pair find_run(void *priv,
struct list_head *list,
list_cmp_func_t cmp)
{
size_t len = 1;
struct list_head *next = list->next, *head = list;
struct pair result;

if (!next) {
result.head = head, result.next = next;
return result;
}

if (cmp(priv, list, next) > 0) {
/* decending run, also reverse the list */
struct list_head *prev = NULL;
do {
len++;
list->next = prev;
prev = list;
list = next;
next = list->next;
head = list;
} while (next && cmp(priv, list, next) > 0);
list->next = prev;
} else {
do {
len++;
list = next;
next = list->next;
} while (next && cmp(priv, list, next) <= 0);
list->next = NULL;
}
head->prev = NULL;
head->next->prev = (struct list_head *) len;
result.head = head, result.next = next;
return result;
}

static struct list_head *merge_at(void *priv,
list_cmp_func_t cmp,
struct list_head *at)
{
size_t len = run_size(at) + run_size(at->prev);
struct list_head *prev = at->prev->prev;
struct list_head *list = merge_run(priv, cmp, at->prev, at);
list->prev = prev;
list->next->prev = (struct list_head *) len;
--stk_size;
return list;
}

static struct list_head *merge_force_collapse(void *priv,
list_cmp_func_t cmp,
struct list_head *tp)
{
while (stk_size >= 3) {
if (run_size(tp->prev->prev) < run_size(tp)) {
tp->prev = merge_at(priv, cmp, tp->prev);
} else {
tp = merge_at(priv, cmp, tp);
}
}
return tp;
}

static struct list_head *merge_collapse(void *priv,
list_cmp_func_t cmp,
struct list_head *tp)
{
int n;
while ((n = stk_size) >= 2) {
if ((n >= 3 &&
run_size(tp->prev->prev) <= run_size(tp->prev) + run_size(tp)) ||
(n >= 4 && run_size(tp->prev->prev->prev) <=
run_size(tp->prev->prev) + run_size(tp->prev))) {
if (run_size(tp->prev->prev) < run_size(tp)) {
tp->prev = merge_at(priv, cmp, tp->prev);
} else {
tp = merge_at(priv, cmp, tp);
}
} else if (run_size(tp->prev) <= run_size(tp)) {
tp = merge_at(priv, cmp, tp);
} else {
break;
}
}

return tp;
}


void timsort(void *priv, struct list_head *head, bool descend)
{
if (!head || list_empty(head) || list_is_singular(head))
return;

stk_size = 0;
list_cmp_func_t cmp = compare;
struct list_head *list = head->next, *tp = NULL;
if (head == head->prev)
return;

/* Convert to a null-terminated singly-linked list. */
head->prev->next = NULL;

do {
/* Find next run */
struct pair result = find_run(priv, list, cmp);
result.head->prev = tp;
tp = result.head;
list = result.next;
stk_size++;
tp = merge_collapse(priv, cmp, tp);
} while (list);

/* End of input; merge together all the runs. */
tp = merge_force_collapse(priv, cmp, tp);

/* The final merge; rebuild prev links */
struct list_head *stk0 = tp, *stk1 = stk0->prev;
while (stk1 && stk1->prev)
stk0 = stk0->prev, stk1 = stk1->prev;
if (stk_size <= 1) {
build_prev_link(head, head, stk0);
return;
}
merge_final(priv, cmp, head, stk1, stk0);
}

0 comments on commit f9ac956

Please sign in to comment.