ldmud-3.4.1/doc/
ldmud-3.4.1/doc/efun.de/
ldmud-3.4.1/doc/efun/
ldmud-3.4.1/doc/man/
ldmud-3.4.1/doc/other/
ldmud-3.4.1/mud/
ldmud-3.4.1/mud/heaven7/
ldmud-3.4.1/mud/lp-245/
ldmud-3.4.1/mud/lp-245/banish/
ldmud-3.4.1/mud/lp-245/doc/
ldmud-3.4.1/mud/lp-245/doc/examples/
ldmud-3.4.1/mud/lp-245/doc/sefun/
ldmud-3.4.1/mud/lp-245/log/
ldmud-3.4.1/mud/lp-245/obj/Go/
ldmud-3.4.1/mud/lp-245/players/lars/
ldmud-3.4.1/mud/lp-245/room/death/
ldmud-3.4.1/mud/lp-245/room/maze1/
ldmud-3.4.1/mud/lp-245/room/sub/
ldmud-3.4.1/mud/lp-245/secure/
ldmud-3.4.1/mud/morgengrauen/
ldmud-3.4.1/mud/morgengrauen/lib/
ldmud-3.4.1/mud/sticklib/
ldmud-3.4.1/mud/sticklib/src/
ldmud-3.4.1/mudlib/uni-crasher/
ldmud-3.4.1/pkg/
ldmud-3.4.1/pkg/debugger/
ldmud-3.4.1/pkg/diff/
ldmud-3.4.1/pkg/misc/
ldmud-3.4.1/src/autoconf/
ldmud-3.4.1/src/hosts/
ldmud-3.4.1/src/hosts/GnuWin32/
ldmud-3.4.1/src/hosts/amiga/
ldmud-3.4.1/src/hosts/win32/
ldmud-3.4.1/src/ptmalloc/
ldmud-3.4.1/src/util/
ldmud-3.4.1/src/util/erq/
ldmud-3.4.1/src/util/indent/hosts/next/
ldmud-3.4.1/src/util/xerq/
ldmud-3.4.1/src/util/xerq/lpc/
ldmud-3.4.1/src/util/xerq/lpc/www/
ldmud-3.4.1/test/t-030925/
ldmud-3.4.1/test/t-040413/
ldmud-3.4.1/test/t-041124/
/*---------------------------------------------------------------------------
 * Alist handling functions.
 *
 *---------------------------------------------------------------------------
 * One special application of arrays are alists: associative lists.
 * Alists allow the association of data (single values or tuples) with
 * a key value, which is then used to locate the data in the alist structure.
 *
 * Nowadays the same functionality is offered by mappings in a much more
 * efficient manner, so this usage of alists is deprecated. However, for
 * reasons explained below, alists can be used as an efficient way to
 * construct lookup arrays.
 *
 * It might be historically interesting to know that the very first
 * implementations of mappings were mere syntactic sugar for alists.
 * Furthermore, the LPMud variant of alists offers only a part of the
 * functionality of 'real' alists.
 *
 * Alists are implemented by a vector of vectors. A typical alist
 * for (key:data1,...,dataN) tuples looks like this:
 *
 *   alist = ({ ({ key values })
 *            , ({ data1 values })
 *            , ...
 *            , ({ dataN values })
 *           })
 *
 * All subarrays are of the same length, and all the values for one tuple
 * is found at the same index. For example, if the key for a tuple
 * is found in alist[0][3], the data values are found in alist[1..N][3].
 *
 * The key value array is sorted to allow fast lookups, the sorting order
 * uses the internal representation of the key values (which usually has
 * nothing in common with the meaning of the key values). Three things
 * however can be guaranteed:
 *
 *   - integer key values appear in rising order in the key array, though
 *     not necessarily consecutive.
 *   - removing one or more keys does not break the order of the
 *     other keys.
 *   - all strings used as key values are made shared strings.
 *---------------------------------------------------------------------------
 */

#include "driver.h"
#include "typedefs.h"

#ifdef USE_ALISTS

#include "pkg-alists.h"

#include "my-alloca.h"
#include <stddef.h>

#include "array.h"
#include "interpret.h" /* destructed_object_ref(), error functions */
#include "main.h"
#include "mstrings.h"
#include "simulate.h"  /* errorf() */
#include "svalue.h"
#include "xalloc.h"

#include "i-svalue_cmp.h"

/*-------------------------------------------------------------------------*/
static vector_t *
intersect_ordered_arr (vector_t *a1, vector_t *a2)

/* Compute the intersection of the two ordered arrays <a1> and <a2>.
 *
 * The result is a new sorted(!) vector with all elements, which are present
 * in both input vectors.
 * This function is called by intersect_array() and f_intersect_alists().
 */

{
    vector_t *a3;
    mp_int d, l, i1, i2, a1s, a2s;

    a1s = (mp_int)VEC_SIZE(a1);
    a2s = (mp_int)VEC_SIZE(a2);
    a3 = allocate_array( a1s < a2s ? a1s : a2s);
    for (i1=i2=l=0; i1 < a1s && i2 < a2s; ) {
        d = svalue_cmp(&a1->item[i1], &a2->item[i2]);
        if (d<0)
            i1++;
        else if (d>0)
            i2++;
        else {
            assign_svalue_no_free(&a3->item[l++], &a2->item[(i1++,i2++)] );
        }
    }
    return shrink_array(a3, l);
} /* intersect_ordered_arr() */

/*-------------------------------------------------------------------------*/
vector_t *
order_alist (svalue_t *inlists, int listnum, Bool reuse)

/* Order the alist <inlists> and return a new vector with it. The sorting
 * order is the internal order defined by alist_cmp().
 *
 * <inlists> is a vector of <listnum> vectors:
 *   <inlists> = ({ ({ keys }), ({ data1 }), ..., ({ data<listnum-1> }) })
 *
 * If <reuse> is true, the vectors of <inlists> are reused for the
 * vectors of the result when possible, and their entries in <inlists> are
 * set to T_INVALID.
 *
 * As a side effect, strings in the key vector are made shared, and
 * destructed objects in key and data vectors are replaced by svalue 0s.
 *
 * This function is also called by the compiler for constant expressions.
 */

{
    vector_t *outlist;   /* The result vector of vectors */
    vector_t *v;         /* Aux vector pointer */
    svalue_t *outlists;  /* Next element in outlist to fill in */
    ptrdiff_t * sorted;  /* The vector elements in sorted order */
    svalue_t *inpnt;     /* Pointer to the value to copy into the result */
    mp_int keynum;       /* Number of keys */
    int i, j;

    keynum = (mp_int)VEC_SIZE(inlists[0].u.vec);

    /* Get the sorting order */

    sorted = get_array_order(inlists[0].u.vec);

    /* Generate the result vectors from the sorting order.
     */

    outlist = allocate_array(listnum);
    outlists = outlist->item;

    /* Copy the elements from all inlist vectors into the outlist
     * vectors.
     *
     * At the beginning of every loop v points to the vector to
     * use as the next 'out' vector. It may be a re-used 'in' vector
     * from the previous run.
     */
    v = allocate_array(keynum);
    for (i = listnum; --i >= 0; ) {

        svalue_t *outpnt; /* Next result value element to fill in */

        /* Set the new array v as the next 'out' vector, and init outpnt
         * and offs.
         */
        put_array(outlists + i, v);
        outpnt = v->item;

        v = inlists[i].u.vec; /* Next vector to fill if reusable */

        /* Copy the elements.
         * For a reusable 'in' vector, a simple memory copy is sufficient.
         * For a new vector, a full assignment is due to keep the refcounters
         * happy.
         */
        if (reuse && inlists[i].u.vec->ref == 1) {

            if (i) /* not the last iteration */
                inlists[i].type = T_INVALID;

            for (j = keynum; --j >= 0; ) {
                inpnt = inlists[i].u.vec->item + sorted[j];
                if (destructed_object_ref(inpnt))
                {
                    free_svalue(inpnt);
                    put_number(outpnt, 0);
                    outpnt++;
                } else {
                    *outpnt++ = *inpnt;
                }
                inpnt->type = T_INVALID;
            }

        } else {

            if (i) /* Not the last iteration: get new out-vector */
                v = allocate_array(keynum);

            for (j = keynum; --j >= 0; ) {
                inpnt = inlists[i].u.vec->item + sorted[j];
                if (destructed_object_ref(inpnt))
                {
                    put_number(outpnt, 0);
                    outpnt++;
                } else {
                    assign_svalue_no_free(outpnt++, inpnt);
                }
            }
        } /* if (reuse) */
    } /* for (listnum) */

    xfree(sorted);

    return outlist;
} /* order_alist() */

/*-------------------------------------------------------------------------*/
static svalue_t *
insert_alist (svalue_t *key, svalue_t * /* TODO: bool */ key_data, vector_t *list)

/* Implementation of efun insert_alist()
 *
 * The function can be used in two ways:
 *
 * 1. Insert/replace a (new) <key>:<keydata> tuple into the alist <list>.
 *    <key> and <key_data> have to point to an array of svalues. The first
 *    element is the key value, the following values the associated
 *    data values. The function will read as many elements from the
 *    array as necessary to fill the alist <list>.
 *    Result is a fresh copy of the modified alist.
 *
 * 2. Lookup a <key> in the alist <list> and return its index. If the key
 *    is not found, return  the position at which it would be inserted.
 *    <key_data> must be NULL, <key> points to the svalue to be looked
 *    up, and <list> points to an alist with at least the key vector.
 *
 * If <list> is no alist, the result can be wrong (case 2.) or not
 * an alist either (case 1.).
 *
 * If the <key> is a string, it is made shared.
 *
 * TODO: Make the hidden flag 'key_data' a real flag.
 */

{
    static svalue_t stmp; /* Result value */
    mp_int i,j,ix;
    mp_int keynum, list_size;  /* Number of keys, number of alist vectors */
    int new_member;            /* Flag if a new tuple is given */

    /* If key is a string, make it shared */
    if (key->type == T_STRING && !mstr_tabled(key->u.str))
    {
        key->u.str = make_tabled(key->u.str);
    }

    keynum = (mp_int)VEC_SIZE(list->item[0].u.vec);

    /* Locate the key */
    ix = lookup_key(key, list->item[0].u.vec);

    /* If its just a lookup: return the result.
     */
    if (key_data == NULL) {
         put_number(&stmp, ix < 0 ? -ix-1 : ix);
         return &stmp;
    }

    /* Prepare the result alist vector */
    put_array(&stmp, allocate_array(list_size = (mp_int)VEC_SIZE(list)));

    new_member = ix < 0;
    if (new_member)
        ix = -ix-1;

    /* Loop over all key/data vectors in <list>, insert/replace the
     * new value and put the new vector into <stmp>.
     */
    for (i = 0; i < list_size; i++) {
        vector_t *vtmp;

        if (new_member) {

            svalue_t *pstmp = list->item[i].u.vec->item;

            vtmp = allocate_array(keynum+1);
            for (j=0; j < ix; j++) {
               assign_svalue_no_free(&vtmp->item[j], pstmp++);
            }
            assign_svalue_no_free(&vtmp->item[ix], i ? &key_data[i] : key );
            for (j = ix+1; j <= keynum; j++) {
               assign_svalue_no_free(&vtmp->item[j], pstmp++);
            }

        } else {

            vtmp = slice_array(list->item[i].u.vec, 0, keynum-1);
            if (i)
                assign_svalue(&vtmp->item[ix], &key_data[i]);
                /* No need to assign the key value: it's already there. */

        }

        stmp.u.vec->item[i].type=T_POINTER;
        stmp.u.vec->item[i].u.vec=vtmp;
    }

    /* Done */
    return &stmp;
} /* insert_alist() */

/*=========================================================================*/

/*                            EFUNS                                        */

/*-------------------------------------------------------------------------*/
svalue_t *
v_insert_alist (svalue_t *sp, int num_arg)

/* EFUN insert_alist()
 *
 *   mixed* insert_alist (mixed key, mixed data..., mixed * alist)
 *   int    insert_alist (mixed key, mixed * keys)
 *
 * 1. Form: Alist Insertion
 *
 *   The <key> and all following <data> values are inserted
 *   into the <alist>. If an entry for <key> already exists
 *   in the list, just the data values are replaced. The number
 *   of <data> values must match the number of data arrays
 *   in the alist, naturally.
 *
 *   Result is the updated <alist>.
 *
 * 2. Form: Key Insertion
 *
 *   Insert the <key> into the (ordered) array of <keys>, so that
 *   subsequent assoc()s can perform quick lookups. Result is the
 *   index at which <key> was inserted (or already found).
 *
 *   CAVEAT: when working with string keys, the index might no longer
 *     be valid after the next call to insert_alist().
 */
/* When the key list of an alist contains destructed objects
   it is better not to free them till the next reordering by
   order_alist to retain the alist property.
 */

{
    int i;
    vector_t *list;
    long listsize;
    size_t keynum;
    svalue_t *key,*key_data,*ret;
    static LOCAL_VEC1(insert_alist_vec, T_NUMBER);
      /* Mock-alist for the insert_alist() key-insertion form.
       */

    if (sp->type != T_POINTER)
        vefun_arg_error(num_arg, T_POINTER, sp->type, sp);

    /* Make up an alist if only a key-insertion is required */
    if ( !(listsize = (long)VEC_SIZE(sp->u.vec))
     ||  sp->u.vec->item[0].type != T_POINTER )
    {
        list = &insert_alist_vec.v;
        *list->item = *sp;
        listsize = 1;
    }
    else
        list = sp->u.vec;

    /* Check the validity of the alist */
    keynum = VEC_SIZE(list->item[0].u.vec);
    for (i = 1; i < listsize; i++)
    {
        if (list->item[i].type != T_POINTER
         || (size_t)VEC_SIZE(list->item[i].u.vec) != keynum)
        {
            errorf("Type or size mismatch of the data arrays.\n");
            /* NOTREACHED */
            return sp;
        }
    }

    /* Get and test the data to insert */
    if (num_arg == 2)
    {
        if (sp[-1].type != T_POINTER)
        {
            key_data = NULL;
            key = sp-1;
        }
        else
        {
            if (VEC_SIZE(sp[-1].u.vec) != listsize)
            {
                errorf("Size mismatch of the data arrays: "
                      "vec size %ld, list size %ld.\n"
                     , (long)VEC_SIZE(sp[-1].u.vec), (long)listsize
                     );
                /* NOTREACHED */
                return sp;
            }
            key_data = key = sp[-1].u.vec->item;
        }
    }
    else
    {
        if (num_arg - 1 != listsize)
        {
            errorf("Not enough data given: %ld arguments, %ld listsize.\n"
                 , (long)num_arg - 1, (long)listsize);
            /* NOTREACHED */
            return sp;
        }
        key_data = key = sp-num_arg+1;
    }

    /* Do the insertion */
    ret = insert_alist(key,key_data,list);
    sp = pop_n_elems(num_arg, sp);
    sp++;
    *sp = *ret;

    return sp;
} /* v_insert_alist() */

/*-------------------------------------------------------------------------*/
svalue_t *
v_assoc (svalue_t *sp, int num_arg)

/* EFUN assoc()
 *
 *     int   assoc (mixed key, mixed *keys)
 *     mixed assoc (mixed key, mixed *alist [, mixed fail] )
 *     mixed assoc (mixed key, mixed *keys, mixed *data [, mixed fail])
 *
 * Search for <key> in the <alist> resp. in the <keys>.
 *
 * When the key list of an alist contains destructed objects
 * it is better not to free them till the next reordering by
 * order_alist to retain the alist property.
 */

{
    svalue_t *args;
    vector_t *keys,*data;
    svalue_t *fail_val;
    int ix;

    args = sp -num_arg +1;

    /* Analyse the arguments */
    if ( !VEC_SIZE(args[1].u.vec)
     ||  args[1].u.vec->item[0].type != T_POINTER )
    {
        keys = args[1].u.vec;
        if (num_arg == 2)
        {
            data = NULL;
        }
        else
        {
            if (args[2].type != T_POINTER
             || VEC_SIZE(args[2].u.vec) != VEC_SIZE(keys))
            {
                errorf("Number of values in key and data arrays differ.\n");
                /* NOTREACHED */
                return sp;
            }
            data = args[2].u.vec;
        }
        if (num_arg == 4)
        {
            fail_val = &args[3];
        }
        else
        {
            fail_val = &const0;
        }
    }
    else
    {
        keys = args[1].u.vec->item[0].u.vec;
        if (VEC_SIZE(args[1].u.vec) > 1)
        {
            if (args[1].u.vec->item[1].type != T_POINTER
             || VEC_SIZE(args[1].u.vec->item[1].u.vec) != VEC_SIZE(keys))
            {
                errorf("Number of values in key and data arrays differ.\n");
                /* NOTREACHED */
                return sp;
            }
            data = args[1].u.vec->item[1].u.vec;
        }
        else
        {
            data = NULL;
        }

        if (num_arg == 3) fail_val = &args[2];
        else if (num_arg == 2) fail_val = &const0;
        else
        {
            errorf("too many args to efun assoc\n");
            /* NOTREACHED */
            return sp;
        }
    }

    /* Call lookup_key() and push the result */
    ix = lookup_key(&args[0],keys);
    if (data == NULL)
    {
        sp = pop_n_elems(num_arg, sp);
        push_number(sp, ix < 0 ? -1 : ix);
    }
    else
    {
        assign_svalue(args
                     , ix < 0
                       ? fail_val
                       : (destructed_object_ref(&data->item[ix])
                         ? &const0
                         : &data->item[ix])
                     );
        sp = pop_n_elems(num_arg-1, sp);
    }

    return sp;
} /* v_assoc() */

/*-------------------------------------------------------------------------*/
svalue_t *
f_intersect_alist (svalue_t *sp)

/* EFUN intersect_alist()
 *
 *   mixed * intersect_alist (mixed * list1, mixed * list2)
 *
 * Does a fast set intersection on alist key vectors (NOT on full
 * alists!).
 *
 * The result is a new sorted(!) vector with all elements, which are present
 * in both input vectors.
 *
 * The operator '&' does set intersection on arrays in
 * general.
 *
 * TODO: Maybe rename the efun.
 */

{
    vector_t *rc;

    rc = intersect_ordered_arr(sp[-1].u.vec, sp->u.vec);

    free_svalue(sp--);
    free_array(sp->u.vec);
    sp->u.vec = rc;

    return sp;
} /* f_intersect_alist() */

/*-------------------------------------------------------------------------*/
svalue_t *
v_order_alist (svalue_t *sp, int num_arg)

/* EFUN order_alist()
 *
 *   mixed *order_alist(mixed *keys, mixed *|void data, ...)
 *
 * Creates an alist.
 *
 * Either takes an array containing keys, and others containing
 * the associated data, where all arrays are to be of the same
 * length, or takes a single array that contains as first member
 * the array of keys and has an arbitrary number of other members
 * containing data, each of wich has to be of the same length as
 * the key array. Returns an array holding the sorted key array
 * and the data arrays; the same permutation that is applied to
 * the key array is applied to all data arrays.
 */

{
    int i;
    svalue_t *args;
    vector_t *list;
    long listsize;
    Bool reuse;
    size_t keynum;

    args = sp-num_arg+1;

    /* Get the key array to order */
    if (num_arg == 1
      && ((list = args->u.vec), (listsize = (long)VEC_SIZE(list)))
      && list->item[0].type == T_POINTER)
    {
        args     = list->item;
        reuse = (list->ref == 1);
    }
    else
    {
        listsize = num_arg;
        reuse = MY_TRUE;
    }
    keynum = VEC_SIZE(args[0].u.vec);

    /* Get the data arrays to order */
    for (i = 0; i < listsize; i++)
    {
        if (args[i].type != T_POINTER
         || (size_t)VEC_SIZE(args[i].u.vec) != keynum)
        {
            errorf("bad data array %d in call to order_alist\n",i);
        }
    }

    /* Create the alist */
    list = order_alist(args, listsize, reuse);
    sp = pop_n_elems(num_arg, sp);
    sp++;
    put_array(sp, list);

    return sp;
} /* v_order_alist() */

#endif /* USE_ALISTS */

/***************************************************************************/