summaryrefslogtreecommitdiffstats
path: root/src/mergesort.c
blob: a8353203757dd28caaab88b59e4631e2212fbef2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*
 * Copyright (c) 2017 Elise Lennion <elise.lennion@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <stdint.h>
#include <expression.h>
#include <gmputil.h>
#include <list.h>

static int expr_msort_cmp(const struct expr *e1, const struct expr *e2);

static int concat_expr_msort_cmp(const struct expr *e1, const struct expr *e2)
{
	struct list_head *l = (&e2->expressions)->next;
	const struct expr *i1, *i2;
	int ret;

	list_for_each_entry(i1, &e1->expressions, list) {
		i2 = list_entry(l, typeof(struct expr), list);

		ret = expr_msort_cmp(i1, i2);
		if (ret)
			return ret;

		l = l->next;
	}

	return false;
}

static int expr_msort_cmp(const struct expr *e1, const struct expr *e2)
{
	switch (e1->ops->type) {
	case EXPR_SET_ELEM:
		return expr_msort_cmp(e1->key, e2->key);
	case EXPR_VALUE:
		return mpz_cmp(e1->value, e2->value);
	case EXPR_CONCAT:
		return concat_expr_msort_cmp(e1, e2);
	case EXPR_MAPPING:
		return expr_msort_cmp(e1->left, e2->left);
	default:
		BUG("Unknown expression %s\n", e1->ops->name);
	}
}

static void list_splice_sorted(struct list_head *list, struct list_head *head)
{
	struct list_head *h = head->next;
	struct list_head *l = list->next;

	while (l != list) {
		if (h == head ||
		    expr_msort_cmp(list_entry(l, typeof(struct expr), list),
				   list_entry(h, typeof(struct expr), list)) < 0) {
			l = l->next;
			list_add_tail(l->prev, h);
			continue;
		}

		h = h->next;
	}
}

static void list_cut_middle(struct list_head *list, struct list_head *head)
{
	struct list_head *s = head->next;
	struct list_head *e = head->prev;

	while (e != s) {
		e = e->prev;

		if (e != s)
			s = s->next;
	}

	__list_cut_position(list, head, s);
}

void list_expr_sort(struct list_head *head)
{
	struct list_head *list;
	LIST_HEAD(temp);

	list = &temp;

	if (list_empty(head) || list_is_singular(head))
		return;

	list_cut_middle(list, head);

	list_expr_sort(head);
	list_expr_sort(list);

	list_splice_sorted(list, head);
}