176 lines
4.0 KiB
C
176 lines
4.0 KiB
C
#include "util.h"
|
|
|
|
#include <gint/display.h>
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
//---
|
|
// Heap sort
|
|
//---
|
|
|
|
typedef struct {
|
|
/* Number of slots allocated */
|
|
uint16_t alloc_size;
|
|
/* Number of slots used */
|
|
uint16_t size;
|
|
/* Size of elements */
|
|
uint16_t elsize;
|
|
/* Array of elements, of size alloc_size * elsize */
|
|
void *array;
|
|
/* Comparison function */
|
|
int (*compare)(void const *, void const *);
|
|
} pqueue_t;
|
|
|
|
/* Edges in the heap's tree, materialized as array index computations */
|
|
#define TOP 0
|
|
#define parent(i) (((i)-1)/2)
|
|
#define child_left(i) (2*(i)+1)
|
|
#define child_right(i) (2*(i)+2)
|
|
|
|
/* Create a priority queue with the requested number of elements. */
|
|
static pqueue_t pqueue_alloc(size_t size, size_t elsize,
|
|
int (*compare)(void const *, void const *))
|
|
{
|
|
/* First element of the queue is unused, it just makes edges simpler */
|
|
return (pqueue_t){
|
|
.alloc_size = size,
|
|
.size = 0,
|
|
.elsize = elsize,
|
|
.array = malloc(size * elsize),
|
|
.compare = compare,
|
|
};
|
|
}
|
|
|
|
/* Pointer to nth element. */
|
|
static inline void *pqueue_nth(pqueue_t *q, int n)
|
|
{
|
|
return q->array + n * q->elsize;
|
|
}
|
|
|
|
/* Swap elements i and j. */
|
|
static void pqueue_swap(pqueue_t *q, int i, int j)
|
|
{
|
|
if(i == j) return;
|
|
|
|
uint8_t tmp[q->elsize];
|
|
memcpy(&tmp, pqueue_nth(q, i), q->elsize);
|
|
memcpy(pqueue_nth(q, i), pqueue_nth(q, j), q->elsize);
|
|
memcpy(pqueue_nth(q, j), &tmp, q->elsize);
|
|
}
|
|
|
|
/* Free a priority queue */
|
|
static void pqueue_destroy(pqueue_t *q)
|
|
{
|
|
free(q->array);
|
|
}
|
|
|
|
/* Whether the queue is empty */
|
|
static bool pqueue_empty(pqueue_t const *q)
|
|
{
|
|
return (q->size <= 0);
|
|
}
|
|
|
|
/* Add a value to the queue. */
|
|
static void pqueue_add(pqueue_t *q, void const *element)
|
|
{
|
|
if(q->size >= q->alloc_size) return;
|
|
int i = q->size++;
|
|
|
|
if(element != pqueue_nth(q, i))
|
|
memcpy(pqueue_nth(q, i), element, q->elsize);
|
|
|
|
/* Percolate up */
|
|
while(i > TOP) {
|
|
int p = parent(i);
|
|
if(q->compare(pqueue_nth(q, p), pqueue_nth(q, i)) <= 0) break;
|
|
|
|
pqueue_swap(q, p, i);
|
|
i = p;
|
|
}
|
|
}
|
|
|
|
/* Pop the minimum value from the queue. */
|
|
static void pqueue_pop(pqueue_t *q, void *ret)
|
|
{
|
|
if(pqueue_empty(q)) return;
|
|
memcpy(ret, pqueue_nth(q, TOP), q->elsize);
|
|
|
|
/* Move last element at the top */
|
|
int i = TOP;
|
|
memcpy(pqueue_nth(q, i), pqueue_nth(q, q->size-1), q->elsize);
|
|
q->size--;
|
|
|
|
/* Percolate down */
|
|
while(1) {
|
|
int l = child_left(i);
|
|
int r = child_right(i);
|
|
|
|
/* If there are no children, we're done */
|
|
if(l >= q->size) break;
|
|
|
|
/* Exchange with minimum of children */
|
|
int minimum = l;
|
|
if(r < q->size && q->compare(pqueue_nth(q, r), pqueue_nth(q, l)) < 0)
|
|
minimum = r;
|
|
|
|
if(q->compare(pqueue_nth(q, i), pqueue_nth(q, minimum)) <= 0)
|
|
break;
|
|
|
|
pqueue_swap(q, minimum, i);
|
|
i = minimum;
|
|
}
|
|
}
|
|
|
|
/* TODO: heap_sort can be made in-place with smart layout within (base) */
|
|
void heap_sort(void *base, size_t n, size_t elsize,
|
|
int (*compare)(void const *, void const *))
|
|
{
|
|
pqueue_t q = pqueue_alloc(n, elsize, compare);
|
|
if(!q.array) return;
|
|
|
|
for(size_t i = 0; i < n; i++)
|
|
pqueue_add(&q, base + i * elsize);
|
|
for(size_t i = 0; i < n; i++)
|
|
pqueue_pop(&q, base + i * elsize);
|
|
|
|
pqueue_destroy(&q);
|
|
}
|
|
|
|
//---
|
|
// Integer square root
|
|
//---
|
|
|
|
int64_t sqrtll(int64_t n)
|
|
{
|
|
if(n <= 0) return 0;
|
|
if(n < 4) return 1;
|
|
|
|
int64_t low_bound = sqrtll(n / 4) * 2;
|
|
int64_t high_bound = low_bound + 1;
|
|
|
|
return (high_bound * high_bound <= n) ? high_bound : low_bound;
|
|
}
|
|
|
|
//---
|
|
// Rendering
|
|
//---
|
|
|
|
void fkey_button(int position, char const *text, int color)
|
|
{
|
|
int width;
|
|
dsize(text, NULL, &width, NULL);
|
|
|
|
int x = 4 + 65 * (position - 1);
|
|
int y = 207;
|
|
int w = 63;
|
|
|
|
dline(x + 1, y, x + w - 2, y, C_BLACK);
|
|
dline(x + 1, y + 14, x + w - 2, y + 14, C_BLACK);
|
|
drect(x, y + 1, x + w - 1, y + 13, C_BLACK);
|
|
|
|
dtext(x + ((w - width) >> 1), y + 3, color, text);
|
|
}
|