/*
 * $Header: /san01/cvs/ashpool/csrc/continuity/mecha/Attic/bsp.c,v 1.18 2004/04/30 13:41:56 aleigh Exp $ 
 */

/*
 * Copyright (c) 1994, 2004 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Use, disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include "mecha.h"

#undef BSP_DEBUG
#undef BSP_DEBUG_LOTS

struct bspSentry {
    unsigned int l_key;         /* the lowest value of this key range */
    unsigned int h_key;         /* the highest value of this key range */
    void *p;
    int is_copy;                /* 1 if p is a copy and shouldn't be
                                 * destructed */
    struct bspSentry *next;
};
typedef struct bspSentry bspTentry;

struct bspSnode {
    unsigned int split;         /* the key value on which the nodes are split */
    struct bspSnode *high;      /* The next list, high */
    struct bspSnode *low;       /* The next list, low */
    bspTentry *ents;
};

static void bspFadd_entry(bspTnode * node, bspTentry * ent);
static void bspF_balance(bspTnode * node, unsigned int count,
                         unsigned int depth);
static bspTentry *bspFdup_entry(bspTentry * ent);

void bspFdestroy(bspTnode * node, void (*destructor) (void *))
{
    bspTentry *e, *ne;

    assert(node != NULL);

    /* Destroy any member entries */

    for (e = node->ents; e != NULL; e = ne) {
        ne = e->next;
        if (e->p != NULL) {
            if (destructor != NULL && e->is_copy == 0)
                destructor(e->p);
        }
        free(e);
    }

    if (node->high != NULL)
        bspFdestroy(node->high, destructor);
    if (node->low != NULL)
        bspFdestroy(node->low, destructor);

    free(node);
}

static void bspFadd_entry(bspTnode * node, bspTentry * ent)
{
    assert(node != NULL);
    assert(ent != NULL);
    ent->next = node->ents;
    node->ents = ent;
}

static bspTentry *bspFdup_entry(bspTentry * ent)
{
    bspTentry *e;

    assert(ent != NULL);

    e = (bspTentry *) malloc(sizeof(bspTentry));
    assert(e != NULL);

    memFcopy(ent, e, sizeof(bspTentry));

    e->next = NULL;
    return e;
}

/*
 * Allocate and return an empty bspTnode
 */
bspTnode *bspFinit(void)
{
    bspTnode *p;

    p = (bspTnode *) malloc(sizeof(bspTnode));
    assert(p != NULL);
    memFclear(p, sizeof(bspTnode));

    return p;
}

/*
 * The the given node and split it into two nodes based on the median value.
 */
void bspFsplit(bspTnode * node)
{
    unsigned int max = 0, min = 0, size = 0;
    bspTentry *ent, *tmpent, *next;

    assert(node != NULL);

    /* Determine the min/max range of the low keys */
    for (ent = node->ents; ent != NULL; ent = ent->next) {
#ifdef BSP_DEBUG_LOTS
        logFmsg(3,"bspFsplit: entry: %u low key: %u min/max: %u/%u", size, ent->l_key, min, max);
#endif 
        if (ent->l_key < min || min == 0)
            min = ent->l_key;
        if (ent->l_key > max || max == 0)
            max = ent->l_key;
        size++;
    }

    /* Calculate the splitpoint */
    node->split = min + ((max - min) / 2);

#ifdef BSP_DEBUG
    logFmsg(3,"bspFsplit: Split point calculated: %u from %u / %u. I have %u elements.", node->split, min, max, size);
#endif

    node->high = bspFinit();
    node->low = bspFinit();

    ent = node->ents;
    while (1) {
        int e;

        /*
         * e0, Load the range low e1, Load the range high e2, Split the range
         * into two ranges and load high and low
         */

        if (ent->l_key < node->split)
            e = 0;
        else
            e = 1;

        if (ent->l_key < node->split && ent->h_key > node->split)
            e = 2;

        next = ent->next;

        if (e == 0) {
            bspFadd_entry(node->low, ent);
        }
        if (e == 1) {
            bspFadd_entry(node->high, ent);
        }
        if (e == 2) {
            tmpent = bspFdup_entry(ent);
            tmpent->h_key = (node->split - 1);
            tmpent->is_copy = 1;
            bspFadd_entry(node->low, tmpent);

            ent->l_key = node->split;
            bspFadd_entry(node->high, ent);
        }
        if (next == NULL)
            break;
        ent = next;
    }

    /* This is intentional */
    node->ents = NULL;
}

/*
 * Count the number of entries in the given node.
 */
unsigned int bspFcount(bspTnode * node)
{
    bspTentry *ent;
    unsigned int size = 0;

    assert(node != NULL);

    if (node->ents == NULL)
        return 0;

    for (ent = node->ents; ent != NULL; ent = ent->next) {
        size++;
    }

    return size;
}

/*
 * Balance an entire tree, up to the given depth. Size indicates the number
 * of entries that a given node must have before it is split. Let depth be 0
 * for the first invocation.
 */

void bspFbalance(bspTnode * node, unsigned int count)
{
    assert(node != NULL);
    bspF_balance(node, count, 0);
}

static void bspF_balance(bspTnode * node, unsigned int size,
                         unsigned int depth)
{
    unsigned int count;

    assert(node != NULL);
    assert(depth<20);

    depth++;

    count=bspFcount(node);
#ifdef BSP_DEBUG
    logFmsg(3,"bspF_balance: Will split if %u > %u", count, size);
#endif

    if (count > size) {
        /* Split this node */
        bspFsplit(node);
        bspF_balance(node->high, size, depth);
        bspF_balance(node->low, size, depth);
    }
}

int bspFadd(bspTnode * list, void *p, int high_key, int low_key)
{
    bspTentry *ep;

    assert(list != NULL);
    assert(p != NULL);

    ep = (bspTentry *) malloc(sizeof(bspTentry));

    assert(ep != NULL);

    ep->l_key = low_key;
    ep->h_key = high_key;
    ep->p = p;
    ep->next = list->ents;
    list->ents = ep;

    return 0;
}

/*
 * Searches for key in the BSP range *list. Returns NULL if the value is not
 * found in any of the ranges.
 */

void *bspFsearch(bspTnode * node, unsigned int x)
{
    bspTentry *e;
    bspTnode *n;

    assert(node != NULL);

    n = node;

    while (1) {
        if (n->ents == NULL) {
            /* Transverse */
            if (x >= n->split) {
                if (n->high == NULL)
                    return NULL;
                n = n->high;
            } else {
                if (n->low == NULL)
                    return NULL;
                n = n->low;
            }
        } else {
            if (n->ents == NULL)
                return NULL;
            e = n->ents;
            while (1) {
                if (e->l_key <= x && e->h_key >= x)
                    return e->p;
                if (e->next == NULL)
                    return NULL;
                e = e->next;
            }
        }
    }
}
