/*
 * NAME:	badarg()
 * DESCRIPTION:	cause a bad argument error
 */
private void badarg(string func, int arg)
{
    error("Bad argument " + arg + " to function " + func);
}

/*
 * NAME:	capitalize()
 * DESCRIPTION:	capitalize a string
 */
static string capitalize(string str)
{
    ARGCHECK(str, capitalize, 1);

    if (str[0] >= 'a' && str[0] <= 'z') {
	str[0] -= 'a' - 'A';
    }
    return str;
}

/*
 * NAME:	lower_case()
 * DESCRIPTION:	convert a string to lower case
 */
static string lower_case(string str)
{
    int i, len, c;

    ARGCHECK(str, lower_case, 1);

    for (i = 0, len = strlen(str); i < len; i++) {
	c = str[i];
	if (c >= 'A' && c <= 'Z') {
	    str[i] += 'a' - 'A';
	}
    }
    return str;
}

/*
 * NAME:	set_bit()
 * DESCRIPTION:	set a bit in a bit string
 */
static string set_bit(string str, int bit)
{
    int ind, len, c;

    ARGCHECK(str, set_bit, 1);

    if (bit > MAX_BITS) {
	error("Too big bit number " + bit);
    }
    ind = bit / 6;
    len = strlen(str);
    if (ind >= len) {
	do {
	    str += "                    ";
	    len += 20;
	} while (ind >= len);
	str = str[0 .. ind];
    }
    c = str[ind];
    if (c < ' ') {
	error("Illegal bit pattern in character " + ind);
    }
    str[ind] = (c - ' ' | 1 << bit % 6) + ' ';

    return str;
}

/*
 * NAME:	clear_bit()
 * DESCRIPTION:	clear a bit in a bit string
 */
static string clear_bit(string str, int bit)
{
    int ind, c;

    ARGCHECK(str, clear_bit, 1);

    if (bit > MAX_BITS) {
	error("Too big bit number " + bit);
    }
    ind = bit / 6;
    if (ind >= strlen(str)) {
	return str;
    }
    c = str[ind];
    if (c < ' ') {
	error("Illegal bit pattern in character " + ind);
    }
    str[ind] = (c - ' ' & ~(1 << bit % 6)) + ' ';

    return str;
}

/*
 * NAME:	test_bit()
 * DESCRIPTION:	test a bit in a bit string
 */
static int test_bit(string str, int bit)
{
    int ind;

    ARGCHECK(str, test_bit, 1);

    ind = bit / 6;
    if (ind >= strlen(str)) {
	return 0;
    }

    return (str[ind] - ' ' & 1 << bit % 6) != 0;
}

/*
 * NAME:	member_array()
 * DESCRIPTION:	return the index of the element in the array, or -1
 */
static int member_array(mixed elt, mixed *arr)
{
    int i, sz;

    ARGCHECK(arr, member_array, 1);

    for (i = 0, sz = sizeof(arr); i < sz; i++) {
	if (arr[i] == elt) {
	    return i;
	}
    }
    return -1;
}

/*
 * NAME:	filter_array
 * DESCRIPTION:	filter the elements of an array
 */
static varargs mixed *
filter_array(mixed *arr, mixed func, mixed obj, mixed arg)
{
    mixed *copy, elt;
    int i, j, sz;

    ARGCHECK(arr, filter_array, 1);
    if (closurep(func) && obj == 0 && arg == 0) {
	return relay("lambda_filter_array", arr, func);
    }
    ARGCHECK(stringp(func), filter_array, 2);
    if (stringp(obj)) {
	call_other(obj, "???");
	obj = find_object(obj);
    }
    ARGCHECK(objectp(obj), filter_array, 3);

    copy = allocate(sz = sizeof(arr));
    for (i = 0, j = -1; i < sz; i++) {
	if (call_other(obj, func, elt = arr[i], arg)) {
	    copy[++j] = elt;
	}
    }

    return copy[0 .. j];
}

/*
 * NAME:	map_array
 * DESCRIPTION:	map the elements of an array
 */
static varargs mixed *map_array(mixed *arr, mixed func, mixed obj, mixed arg)
{
    mixed *copy;
    int i, sz;

    ARGCHECK(arr, map_array, 1);
    if (closurep(func) && obj == 0 && arg == 0) {
	return relay("lambda_map_array", arr, func);
    }
    ARGCHECK(stringp(func), map_array, 2);
    if (stringp(obj)) {
	call_other(obj, "???");
	obj = find_object(obj);
    }
    ARGCHECK(objectp(obj), map_array, 3);

    copy = allocate(sz = sizeof(arr));
    for (i = 0; i < sz; i++) {
	copy[i] = call_other(obj, func, arr[i], arg);
    }

    return copy;
}

/*
 * NAME:	extract()
 * DESCRIPTION:	extract a substring
 */
static varargs string extract(string str, int first, int last)
{
    ARGCHECK(str, extract, 1);

    /* will mistake extract(str, 0) for extract(str, 0, 0) */
    if (first != 0 && last == 0) {
	last = strlen(str) - 1;
    } else if (first >= strlen(str) || last < first) {
	return "";
    }

    return str[first .. last];
}

/*
 * NAME:	slice_array()
 * DESCRIPTION:	return part on an array
 */
static mixed *slice_array(mixed *arr, int first, int last)
{
    ARGCHECK(arr, slice_array, 1);

    if (first >= sizeof(arr) || last < first) {
	return ({ });
    }
    return arr[first .. last];
}

/*
 * NAME:	sort_array()
 * DESCRIPTION:	sort an array
 */
static varargs mixed *sort_array(mixed *arr, mixed func, mixed obj)
{
    mixed elt, val;
    int n, i, j, size;

    ARGCHECK(arr, sort_array, 1);
    if (closurep(func) && obj == 0) {
	return relay("lambda_sort_array", arr, func);
    }
    ARGCHECK(stringp(func), sort_array, 2);
    if (obj == 0) {
	obj = this_object();
    } else if (stringp(obj)) {
	call_other(obj, "???");
	obj = find_object(obj);
    }
    ARGCHECK(objectp(obj), sort_array, 3);

    arr = arr[..];
    for (n = 1, size = sizeof(arr); n < size; n <<= 1) ;

    for (n >>= 1; n > 0; --n) {
	elt = arr[n - 1];
	for (i = n, j = n << 1; j <= size; i = j, j <<= 1) {
	    val = arr[j - 1];
	    if (j < size && call_other(obj, func, arr[j], val)) {
		val = arr[j++];
	    }
	    if (call_other(obj, func, elt, val)) {
		break;
	    }
	    arr[i - 1] = val;
	}
	arr[i - 1] = elt;
    }

    for (n = size - 1; n > 0; --n) {
	elt = arr[n];
	arr[n] = arr[0];
	for (i = 1, j = 2; j <= n; i = j, j <<= 1) {
	    val = arr[j - 1];
	    if (j < n && call_other(obj, func, arr[j], val)) {
		val = arr[j++];
	    }
	    if (call_other(obj, func, elt, val)) {
		break;
	    }
	    arr[i - 1] = val;
	}
	arr[i - 1] = elt;
    }

    return arr;
}

/*
 * NAME:	unique_array()
 * DESCRIPTION:	subdevide an array of objects into arrays with objects where
 *		obj->func() returned identical descriptions
 */
static varargs mixed *unique_array(mixed *arr, string func, mixed exclude)
{
    mapping map;
    int i, sz;
    mixed val, elt;
    object *list;

    ARGCHECK(arr, unique_array, 1);
    ARGCHECK(func, unique_array, 2);

    map = ([ ]);
    for (i = 0, sz = sizeof(arr); i < sz; i++) {
	elt = arr[i];
	if (objectp(elt) && (val=call_other(elt, func)) != exclude) {
	    list = map[val];
	    if (list == 0) {
		list = ({ elt });
	    } else {
		list = ({ elt }) + list;
	    }
	    map[val] = list;
	}
    }
    return map_values(map);
}