tmuck2.4/
tmuck2.4/admin/scripts/
tmuck2.4/docs/
tmuck2.4/minimal-db/
tmuck2.4/minimal-db/data/
tmuck2.4/minimal-db/logs/
tmuck2.4/minimal-db/muf/
tmuck2.4/old/
tmuck2.4/src/
tmuck2.4/src/compile/
tmuck2.4/src/editor/
tmuck2.4/src/game/
tmuck2.4/src/interface/
tmuck2.4/src/scripts/
tmuck2.4/src/utilprogs/
/* Copyright (c) 1992 by David Moore.  All rights reserved. */
/* olist_prims.c,v 2.4 1996/01/26 01:04:17 dmoore Exp */
#if defined(PRIM_map_n_lists) || defined(PRIM_apply_n_lists) || defined(PRIM_sort_n_lists)

#include "config.h"

#include "db.h"
#include "code.h"
#include "prim_offsets.h"
#include "externs.h"

/* This structure is used by all of the *-n-lists routines.  It's sufficient
   for map and apply, and has an extra field which only sort uses. */
struct nl_prim_data {
    int prim;			/* Primitive being used. */
    struct func_addr *func;	/* User function. */
    int num_lists;		/* Number of lists of data. */
    int elements;		/* Number of elements in each list. */
    int width;			/* Total width of all data on stack. */
    int depth;			/* Expected depth of the stack. */
    int position;		/* Current location while going down array. */
    int cmp_pos;		/* What we are comparing against. (sort) */
};

#define SETUP_CONT(func) \
    do { push_context_cont(fr, func, data); if (fr->error) return; } while (0)

#define CHK_ADDR_DEPTH() \
    do { \
        if (st->depth > data->depth) { \
            interp_error(fr, data->prim, \
			 "Address left too much (%i) on stack", \
			 st->depth - data->depth); \
            return; \
        } else if (st->depth < data->depth) { \
            interp_error(fr, data->prim, \
			 "Address took too much (%i) off stack", \
			 data->depth - st->depth); \
	     return; \
	} \
    } while (0)


static void nl_prim_cleanup(frame *fr, data_stack *st, void *vdata)
{
    struct nl_prim_data *data = vdata;

    clear_code(data->func->code);
    FREE(data);
}


#define ONE_CONT_FUNC	1
#define ONE_SET_DATA	1
#define TWO_SET_DATA	2
#define ADDR_NO_RETURN	0
#define ADDR_ONE_RETURN	1

/* All the primitives share common initialization code. */
static struct nl_prim_data *nl_prim_init(frame *fr, data_stack *st, const int cont_overhead, const int data_overhead_mul, const inst result_overhead, const int prim)
{
    inst func, count;
    int num_lists, elements, width;
    int data_overhead;
    struct nl_prim_data *data;

    pop_data_st(st, &func);
    pop_data_st(st, &count);

    num_lists = count.un.integer;
    elements = top_data_st(st)->un.integer; /* Size of sub lists. */
    width = num_lists * (elements + 1);	/* Total # of items in lists. */
    data_overhead = (num_lists * data_overhead_mul) + result_overhead;

    if (num_lists == 0) {
	/* If it's empty we don't really have to do much. */
	return NULL;
    }

    if (!same_depth_lists(st, num_lists, elements)) {
	clear_inst_interp(&func);
	interp_error(fr, prim, "All lists must have the same depth");
	return NULL;
    }

    /* Check that there will be enough room to push all the data over
       the entire run of the program. */
    if ((st->depth + data_overhead) > DATA_STACK_SIZE) {
	clear_inst_interp(&func);
	interp_error(fr, prim, "Data stack too full for operation");
	return NULL;
    }

    /* 2+ is for the cleanup address we push here, and the muf addr. */
    if ((fr->addr_stack->depth + addr_overhead + 2) > ADDR_STACK_SIZE) {
	clear_inst_interp(&func);
	interp_error(fr, prim, "Address stack too full for operation");
	return NULL;
    }

    MALLOC(data, struct nl_prim_data, 1);

    data->prim = prim;
    data->func = func.un.address;
    data->num_lists = num_lists;
    data->elements = elements;
    data->width = width;
    data->depth = st->depth + data_overhead;
    data->position = 0;
    data->cmp_pos = 0;

    SETUP_CONT(nl_prim_cleanup);

    return data;
}


static void nl_push_data(frame *fr, data_stack *st, struct nl_prim_data *data, const int pos)
{
    int i;
    inst *top = top_data_st(st);
    inst *start;

    for (i = 0; i < data->num_lists; i++) {
	/* Yes, there is a nicer (and more optimal way to do this.
	   Unfortunately it's not ansi C cause it will go off the beginning
	   of the array on the last iteration. */
	start = top - (i * (data->elements + 1));
	push_data_st(st, start[-pos]);
    }
}


#ifdef PRIM_map_n_lists
static void mnl_loop(frame *fr, data_stack *st, void *vdata)
{
    struct nl_prim_data *data = vdata;

    /* Some sort of interp error, let the cleanup function handle it. */
    if (!fr) return;

    /* We have processed all of the data. */
    if (data->position >= data->num_lists) return;

    /* Make sure that the address has done the proper things. */
    CHK_ADDR_DEPTH();

    /* Push on the desired data. */
    nl_push_data(fr, st, data, data->position);

    /* Slide down the remaining portions.  This alters data->depth. */
    map_list_shift(fr, st, data, data->position);

    /* Setup next iteration, and then setup for the address call. */
    SETUP_CONT(mnl_loop);
    push_context_muf(fr, data->func);
}


void prim_map_n_lists(frame *fr, data_stack *st)
{
    struct nl_prim_data *data;

    data = nl_prim_init(fr, st,
			ONE_CONT_FUNC, ONE_SET_DATA, ADDR_NO_RETURN,
			PRIM_map_n_lists);
    if (!data) return;

    SETUP_CONT(mnl_loop);
}
#endif /* map-n-lists */

#ifdef PRIM_apply_n_lists
#endif /* apply-n-lists */


#if 0 
{
    /* Normal way to insertion sort:  This is how I do it. :) */
    for (pos = 1; pos < elements; pos++) {
	for (cmp_pos = 0; cmp_pos < pos; cmp_pos++) {
	    if (!cmp_func(cmp_pos, pos)) break;
	}
	insert(pos, cmp_pos);
    }
    
    /* Now twist your mind a bit as the above is rewritten.  It does add an
       extra insert(0, 0) onto the very beginning.  No real loss. */

    pos = 0;
    cmp_pos = 0;
    while (pos < elements) {
	insert(pos, cmp_pos);
	pos++;
	cmp_pos = 0;
	result = cmp_func(cmp_pos, pos);
	while (cmp_pos < pos && result) {
	    cmp_pos++;
	    result = cmp_func(cmp_pos, pos);
	}
    }

    /* Now encode the above recursively: */
    func inner_loop {
	if (cmp_pos >= pos || !result) return;
	cmp_pos++;
	result = cmp_func(cmp_pos, pos);
	inner_loop();
    }

    func outer_loop {
	if (pos >= elements) return;
	insert(pos, cmp_pos);
	pos++;
	cmp_pos = 0;
	result = cmp_func(cmp_pos, pos);
	inner_loop();
	outer_loop();
    }

    func sort {
	pos = 0;
	cmp_pos = 0;
	outer_loop();
    }

    /* It is really programmed below like this: */

    func inner_loop {
	if (cmp_pos >= pos || !result) return;
	cmp_pos++;
	SETUP inner_loop;
	result = cmp_func(cmp_pos, pos);
    }

    func outer_loop {
	if (pos >= elements) return;
	insert(pos, cmp_pos);
	pos++;
	cmp_pos = 0;
	SETUP outer_loop;
	SETUP inner_loop;
	result = cmp_func(cmp_pos, pos);
    }

    func sort {
	pos = 0;
	cmp_pos = 0;
	SETUP outer_loop;
    }

}
#endif

#ifdef PRIM_sort_n_lists


static void snl_insert(data_stack *st, struct snl_data *data)
{
    int i;
    inst *curr_pos, *curr_cmp_pos, *move;
    inst temp;

    curr_pos = data->start + data->position;
    curr_cmp_pos = data->start + data->cmp_pos;

    for (i = 0; i < data->num_lists; i++) {
	temp = *curr_pos;
	for (move = curr_pos; move > curr_cmp_pos; move--)
	    *move = *(move-1);
	*curr_cmp_pos = temp;

	curr_pos += data->elements + 1;
	curr_cmp_pos += data->elements + 1;
    }

}


static void snl_do_compare(frame *fr, data_stack *st, struct snl_data *data)
{
    SNL_PUSH_DATA(data->cmp_pos);
    SNL_PUSH_DATA(data->position);
    push_context_muf(fr, data->func);
}


static void snl_cleanup(frame *fr, data_stack *st, void *vdata)
{
    struct snl_data *data = vdata;
    inst temp;

    if (fr) {
	temp.type = INST_INTEGER;
	temp.un.integer = data->num_lists;

	safe_push_data_st(fr, st, &temp, PRIM_sort_n_lists);
    }

    clear_code(data->func->code);
    FREE(data);
}


static void snl_inner_loop(frame *fr, data_stack *st, void *vdata)
{
    struct snl_data *data = vdata;
    inst result;

    if (!fr) return;		/* Some error occured. */

    depth_delta = st->depth - (data->depth + 1);

    CHK_ADDR_DEPTH(data->depth + 1, PRIM_sort_n_lists);

    pop_data_st(st, &result);

    if (result.type != INST_INTEGER) {
	clear_inst_interp(&result);
	interp_error(fr, PRIM_sort_n_lists,
		     "Comparison address did not return an int.");
	return;
    }

    if (result.un.integer < 0) return;

    data->cmp_pos++;

    /* Check if we've found the correct location, return if so. */
    if (data->cmp_pos >= data->position)
	return;

    SNL_SETUP(snl_inner_loop);

    snl_do_compare(fr, st, data);
}


static void snl_outer_loop(frame *fr, data_stack *st, void *vdata)
{
    struct snl_data *data = vdata;

    if (!fr) return;		/* Some error occured. */

    snl_insert(st, data);

    data->position++;
    data->cmp_pos = 0;

    if (data->position >= data->elements) return; /* All sorted, whee. */

    SNL_SETUP(snl_outer_loop);
    SNL_SETUP(snl_inner_loop);

    snl_do_compare(fr, st, data);
}


void prim_sort_n_lists(frame *fr, data_stack *st)
{
    
    SNL_SETUP(snl_cleanup);
    SNL_SETUP(snl_outer_loop);
}
#endif /* sort-n-lists */

#endif /* map-n-lists, apply-n-lists, or sort-n-lists */