/*
 * The Python Imaging Library
 * $Id$
 *
 * heap data type used by the image quantizer
 *
 * history:
 * 98-09-10 tjs  Contributed
 * 98-12-29 fl   Added to PIL 1.0b1
 *
 * Written by Toby J Sargeant <tjs@longford.cs.monash.edu.au>.
 *
 * Copyright (c) 1998 by Toby J Sargeant
 * Copyright (c) 1998 by Secret Labs AB
 *
 * See the README file for information on usage and redistribution.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h>

#include "QuantHeap.h"

struct _Heap {
    void **heap;
    unsigned int heapsize;
    unsigned int heapcount;
    HeapCmpFunc cf;
};

#define INITIAL_SIZE 256

// #define DEBUG

#ifdef DEBUG
static int
_heap_test(Heap *);
#endif

void
ImagingQuantHeapFree(Heap *h) {
    free(h->heap);
    free(h);
}

static int
_heap_grow(Heap *h, unsigned int newsize) {
    void *newheap;
    if (!newsize) {
        newsize = h->heapsize << 1;
    }
    if (newsize < h->heapsize) {
        return 0;
    }
    if (newsize > INT_MAX / sizeof(void *)) {
        return 0;
    }
    /* malloc check ok, using calloc for overflow, also checking
       above due to memcpy below*/
    newheap = calloc(newsize, sizeof(void *));
    if (!newheap) {
        return 0;
    }
    memcpy(newheap, h->heap, sizeof(void *) * h->heapsize);
    free(h->heap);
    h->heap = newheap;
    h->heapsize = newsize;
    return 1;
}

#ifdef DEBUG
static int
_heap_test(Heap *h) {
    unsigned int k;
    for (k = 1; k * 2 <= h->heapcount; k++) {
        if (h->cf(h, h->heap[k], h->heap[k * 2]) < 0) {
            printf("heap is bad\n");
            return 0;
        }
        if (k * 2 + 1 <= h->heapcount && h->cf(h, h->heap[k], h->heap[k * 2 + 1]) < 0) {
            printf("heap is bad\n");
            return 0;
        }
    }
    return 1;
}
#endif

int
ImagingQuantHeapRemove(Heap *h, void **r) {
    unsigned int k, l;
    void *v;

    if (!h->heapcount) {
        return 0;
    }
    *r = h->heap[1];
    v = h->heap[h->heapcount--];
    for (k = 1; k * 2 <= h->heapcount; k = l) {
        l = k * 2;
        if (l < h->heapcount) {
            if (h->cf(h, h->heap[l], h->heap[l + 1]) < 0) {
                l++;
            }
        }
        if (h->cf(h, v, h->heap[l]) > 0) {
            break;
        }
        h->heap[k] = h->heap[l];
    }
    h->heap[k] = v;
#ifdef DEBUG
    if (!_heap_test(h)) {
        printf("oops - heap_remove messed up the heap\n");
        exit(1);
    }
#endif
    return 1;
}

int
ImagingQuantHeapAdd(Heap *h, void *val) {
    int k;
    if (h->heapcount == h->heapsize - 1) {
        _heap_grow(h, 0);
    }
    k = ++h->heapcount;
    while (k != 1) {
        if (h->cf(h, val, h->heap[k / 2]) <= 0) {
            break;
        }
        h->heap[k] = h->heap[k / 2];
        k >>= 1;
    }
    h->heap[k] = val;
#ifdef DEBUG
    if (!_heap_test(h)) {
        printf("oops - heap_add messed up the heap\n");
        exit(1);
    }
#endif
    return 1;
}

int
ImagingQuantHeapTop(Heap *h, void **r) {
    if (!h->heapcount) {
        return 0;
    }
    *r = h->heap[1];
    return 1;
}

Heap *
ImagingQuantHeapNew(HeapCmpFunc cf) {
    Heap *h;

    /* malloc check ok, small constant allocation */
    h = malloc(sizeof(Heap));
    if (!h) {
        return NULL;
    }
    h->heapsize = INITIAL_SIZE;
    /* malloc check ok, using calloc for overflow */
    h->heap = calloc(h->heapsize, sizeof(void *));
    if (!h->heap) {
        free(h);
        return NULL;
    }
    h->heapcount = 0;
    h->cf = cf;
    return h;
}
