Skip to content
Snippets Groups Projects
Select Git revision
  • eb48fb68925e7b3b14ca68118d87585db63c43c8
  • master default protected
  • 9.0
  • 8.0
  • nt-tools
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • grubba/wip/sakura/8.0
  • v8.0.2020
  • v8.0.2018
  • v8.0.2016
  • v8.0.2014
  • v8.0.2012
  • v8.0.2008
  • v8.0.2006
  • v8.0.2004
  • v8.0.2002
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
41 results

builtin_functions.c

Blame
  • user avatar
    Fredrik Hübinette (Hubbe) authored
    Rev: lib/master.pike.in:1.59
    Rev: src/bignum.c:1.2
    Rev: src/bignum.h:1.3
    Rev: src/builtin_functions.c:1.184
    Rev: src/language.yacc:1.125
    Rev: src/main.c:1.77
    Rev: src/operators.c:1.62
    Rev: src/program.c:1.158
    e37a3eed
    History
    builtin_functions.c 116.34 KiB
    /*\
    ||| This file a part of Pike, and is copyright by Fredrik Hubinette
    ||| Pike is distributed as GPL (General Public License)
    ||| See the files COPYING and DISCLAIMER for more information.
    \*/
    /**/
    #include "global.h"
    RCSID("$Id: builtin_functions.c,v 1.184 1999/10/09 23:28:57 hubbe Exp $");
    #include "interpret.h"
    #include "svalue.h"
    #include "pike_macros.h"
    #include "object.h"
    #include "program.h"
    #include "array.h"
    #include "error.h"
    #include "constants.h"
    #include "mapping.h"
    #include "stralloc.h"
    #include "multiset.h"
    #include "pike_types.h"
    #include "rusage.h"
    #include "operators.h"
    #include "fsort.h"
    #include "callback.h"
    #include "gc.h"
    #include "backend.h"
    #include "main.h"
    #include "pike_memory.h"
    #include "threads.h"
    #include "time_stuff.h"
    #include "version.h"
    #include "encode.h"
    #include <math.h>
    #include <ctype.h>
    #include "module_support.h"
    #include "module.h"
    #include "opcodes.h"
    #include "cyclic.h"
    #include "signal_handler.h"
    #include "security.h"
    #include "builtin_functions.h"
    
    #ifdef HAVE_POLL
    #ifdef HAVE_POLL_H
    #include <poll.h>
    #endif /* HAVE_POLL_H */
    
    #ifdef HAVE_SYS_POLL_H
    #include <sys/poll.h>
    #endif /* HAVE_SYS_POLL_H */
    #endif /* HAVE_POLL */
    
    #ifdef HAVE_CRYPT_H
    #include <crypt.h>
    #endif
    
    /* #define DIFF_DEBUG */
    /* #define ENABLE_DYN_DIFF */
    
    void f_equal(INT32 args)
    {
      int i;
      if(args != 2)
        PIKE_ERROR("equal", "Bad number of arguments.\n", sp, args);
    
      i=is_equal(sp-2,sp-1);
      pop_n_elems(args);
      push_int(i);
    }
    
    #ifdef DEBUG_MALLOC
    void _f_aggregate(INT32 args)
    #else
    void f_aggregate(INT32 args)
    #endif
    {
      struct array *a;
    #ifdef PIKE_DEBUG
      if(args < 0) fatal("Negative args to f_aggregate() (%d)\n",args);
    #endif
    
      a=aggregate_array(args);
      push_array(a); /* beware, macro */
    }
    
    void f_trace(INT32 args)
    {
      extern int t_flag;
      INT_TYPE t;
    
      get_all_args("trace", args, "%i", &t);
      pop_n_elems(args);
      push_int(t_flag);
      t_flag = t;
    }
    
    void f_hash(INT32 args)
    {
      INT32 i;
      if(!args)
        SIMPLE_TOO_FEW_ARGS_ERROR("hash",1);
      if(sp[-args].type != T_STRING)
        SIMPLE_BAD_ARG_ERROR("hash", 1, "string");
    
      switch(sp[-args].u.string->size_shift)
      {
        case 0:
          i=hashstr((unsigned char *)sp[-args].u.string->str,100);
          break;
    
        case 1:
          i=simple_hashmem((unsigned char *)sp[-args].u.string->str,
    		       sp[-args].u.string->len << 1,
    		       200);
          break;
    
        case 2:
          i=simple_hashmem((unsigned char *)sp[-args].u.string->str,
    		       sp[-args].u.string->len << 2,
    		       400);
          break;
    
        default:
          fatal("hash(): Bad string shift:%d\n", sp[-args].u.string->size_shift);
      }
      
      if(args > 1)
      {
        if(sp[1-args].type != T_INT)
          SIMPLE_BAD_ARG_ERROR("hash",2,"int");
        
        if(!sp[1-args].u.integer)
          PIKE_ERROR("hash", "Modulo by zero.\n", sp, args);
    
        i%=(unsigned INT32)sp[1-args].u.integer;
      }
      pop_n_elems(args);
      push_int(i);
    }
    
    void f_copy_value(INT32 args)
    {
      if(!args)
        SIMPLE_TOO_FEW_ARGS_ERROR("copy_value",1);
    
      pop_n_elems(args-1);
      copy_svalues_recursively_no_free(sp,sp-1,1,0);
      free_svalue(sp-1);
      sp[-1]=sp[0];
    }
    
    void f_ctime(INT32 args)
    {
      time_t i;
      INT_TYPE x;
      get_all_args("ctime",args,"%i",&x);
      i=(time_t)x;
      pop_n_elems(args);
      push_string(make_shared_string(ctime(&i)));
    }
    
    struct case_info {
      int low;	/* low end of range. */
      int mode;
      int data;
    };
    
    #define CIM_NONE	0	/* Case-less */
    #define CIM_UPPERDELTA	1	/* Upper-case, delta to lower-case in data */
    #define CIM_LOWERDELTA	2	/* Lower-case, -delta to upper-case in data */
    #define CIM_CASEBIT	3	/* Some case, case mask in data */
    #define CIM_CASEBITOFF	4	/* Same as above, but also offset by data */
    
    static struct case_info case_info[] = {
    #include "case_info.h"
      { 0x10000, CIM_NONE, 0x0000, },	/* End sentinel. */
    };
    
    static struct case_info *find_ci(int c)
    {
      static struct case_info *cache = NULL;
      struct case_info *ci = cache;
      int lo = 0;
      int hi = NELEM(case_info);
    
      if ((c < 0) || (c > 0xffff))
        return NULL;
    
      if ((ci) && (ci[0].low <= c) && (ci[1].low > c)) {
        return ci;
      }
    
      while (lo != hi-1) {
        int mid = (lo + hi)/2;
        if (case_info[mid].low < c) {
          lo = mid;
        } else if (case_info[mid].low == c) {
          lo = mid;
          break;
        } else {
          hi = mid;
        }
      }
    
      return(cache = case_info + lo);
    }
    
    #define DO_LOWER_CASE(C) do {\
        int c = C; \
        struct case_info *ci = find_ci(c); \
        if (ci) { \
          switch(ci->mode) { \
          case CIM_NONE: case CIM_LOWERDELTA: break; \
          case CIM_UPPERDELTA: C = c + ci->data; break; \
          case CIM_CASEBIT: C = c | ci->data; break; \
          case CIM_CASEBITOFF: C = ((c - ci->data) | ci->data) + ci->data; break; \
          default: fatal("lower_case(): Unknown case_info mode: %d\n", ci->mode); \
        } \
       } \
      } while(0)
    
    #define DO_UPPER_CASE(C) do {\
        int c = C; \
        struct case_info *ci = find_ci(c); \
        if (ci) { \
          switch(ci->mode) { \
          case CIM_NONE: case CIM_UPPERDELTA: break; \
          case CIM_LOWERDELTA: C = c - ci->data; break; \
          case CIM_CASEBIT: C = c & ~ci->data; break; \
          case CIM_CASEBITOFF: C = ((c - ci->data)& ~ci->data) + ci->data; break; \
          default: fatal("lower_case(): Unknown case_info mode: %d\n", ci->mode); \
        } \
       } \
      } while(0)
    
    void f_lower_case(INT32 args)
    {
      INT32 i;
      struct pike_string *orig;
      struct pike_string *ret;
      get_all_args("lower_case", args, "%W", &orig);
    
      ret = begin_wide_shared_string(orig->len, orig->size_shift);
    
      MEMCPY(ret->str, orig->str, orig->len << orig->size_shift);
    
      i = orig->len;
    
      if (!orig->size_shift) {
        p_wchar0 *str = STR0(ret);
    
        while(i--) {
          DO_LOWER_CASE(str[i]);
        }
      } else if (orig->size_shift == 1) {
        p_wchar1 *str = STR1(ret);
    
        while(i--) {
          DO_LOWER_CASE(str[i]);
        }
      } else if (orig->size_shift == 2) {
        p_wchar2 *str = STR2(ret);
    
        while(i--) {
          DO_LOWER_CASE(str[i]);
        }
      } else {
        fatal("lower_case(): Bad string shift:%d\n", orig->size_shift);
      }
    
      pop_n_elems(args);
      push_string(end_shared_string(ret));
    }
    
    void f_upper_case(INT32 args)
    {
      INT32 i;
      struct pike_string *orig;
      struct pike_string *ret;
      int widen = 0;
      get_all_args("upper_case",args,"%W",&orig);
    
      ret=begin_wide_shared_string(orig->len,orig->size_shift);
      MEMCPY(ret->str, orig->str, orig->len << orig->size_shift);
    
      i = orig->len;
    
      if (!orig->size_shift) {
        p_wchar0 *str = STR0(ret);
    
        while(i--) {
          if (str[i] != 0xff) {
    	DO_UPPER_CASE(str[i]);
          } else {
    	widen = 1;
          }
        }
      } else if (orig->size_shift == 1) {
        p_wchar1 *str = STR1(ret);
    
        while(i--) {
          DO_UPPER_CASE(str[i]);
        }
      } else if (orig->size_shift == 2) {
        p_wchar2 *str = STR2(ret);
    
        while(i--) {
          DO_UPPER_CASE(str[i]);
        }
      } else {
        fatal("lower_case(): Bad string shift:%d\n", orig->size_shift);
      }
    
      pop_n_elems(args);
      push_string(end_shared_string(ret));
    
      if (widen) {
        /* Widen the string, and replace any 0xff's with 0x178's. */
        orig = sp[-1].u.string;
        ret = begin_wide_shared_string(orig->len, 1);
    
        i = orig->len;
    
        while(i--) {
          if ((STR1(ret)[i] = STR0(orig)[i]) == 0xff) {
    	STR1(ret)[i] = 0x178;
          }
        }
        free_string(sp[-1].u.string);
        sp[-1].u.string = end_shared_string(ret);
      }
    }
    
    void f_random(INT32 args)
    {
      INT_TYPE i;
      get_all_args("random",args,"%i",&i);
    
      if(i <= 0)
      {
        i = 0;
      }else{
        i = my_rand() % i;
      }
      pop_n_elems(args);
      push_int(i);
    }
    
    void f_random_seed(INT32 args)
    {
      INT_TYPE i;
    #ifdef AUTO_BIGNUM
      check_all_args("random_seed",args,BIT_INT | BIT_OBJECT, 0);
      if(sp[-args].type == T_INT)
        i=sp[-args].u.integer;
      else
        i=hash_svalue(sp-args);
    #else
      get_all_args("random_seed",args,"%i",&i);
    #endif
      my_srand(i);
      pop_n_elems(args);
    }
    
    void f_query_num_arg(INT32 args)
    {
      pop_n_elems(args);
      push_int(fp ? fp->args : 0);
    }
    
    void f_search(INT32 args)
    {
      INT32 start;
    
      if(args < 2)
        SIMPLE_TOO_FEW_ARGS_ERROR("search", 2);
    
      switch(sp[-args].type)
      {
      case T_STRING:
      {
        char *ptr;
        if(sp[1-args].type != T_STRING)
          SIMPLE_BAD_ARG_ERROR("search", 2, "string");
        
        start=0;
        if(args > 2)
        {
          if(sp[2-args].type!=T_INT)
    	SIMPLE_BAD_ARG_ERROR("search", 3, "int");
    
          start=sp[2-args].u.integer;
          if(start<0) {
    	bad_arg_error("search", sp-args, args, 3, "int(0..)", sp+2-args,
    		   "Start must be greater or equal to zero.\n");
          }
        }
    
        if(sp[-args].u.string->len < start)
          bad_arg_error("search", sp-args, args, 1, "int(0..)", sp-args,
    		    "Start must not be greater than the "
    		    "length of the string.\n");
    
        start=string_search(sp[-args].u.string,
    			sp[1-args].u.string,
    			start);
        pop_n_elems(args);
        push_int(start);
        break;
      }
    
      case T_ARRAY:
        start=0;
        if(args > 2)
        {
          if(sp[2-args].type!=T_INT)
    	SIMPLE_BAD_ARG_ERROR("search", 3, "int");
    
          start=sp[2-args].u.integer;
        }
        start=array_search(sp[-args].u.array,sp+1-args,start);
        pop_n_elems(args);
        push_int(start);
        break;
    
      case T_MAPPING:
        if(args > 2)
          mapping_search_no_free(sp,sp[-args].u.mapping,sp+1-args,sp+2-args);
        else
          mapping_search_no_free(sp,sp[-args].u.mapping,sp+1-args,0);
        free_svalue(sp-args);
        sp[-args]=*sp;
        pop_n_elems(args-1);
        return;
    
      default:
        SIMPLE_BAD_ARG_ERROR("search", 1, "string|array|mapping");
      }
    }
    
    void f_backtrace(INT32 args)
    {
      INT32 frames;
      struct pike_frame *f,*of;
      struct array *a,*i;
    
      frames=0;
      if(args) pop_n_elems(args);
      for(f=fp;f;f=f->next) frames++;
    
      sp->type=T_ARRAY;
      sp->u.array=a=allocate_array_no_init(frames,0);
      sp++;
    
      /* NOTE: The first pike_frame is ignored, since it is the call to backtrace(). */
      of=0;
      for(f=fp;f;f=(of=f)->next)
      {
        char *program_name;
    
        frames--;
    
        if(f->current_object && f->context.prog)
        {
          INT32 args;
          args=f->num_args;
          args=MINIMUM(f->num_args, sp - f->locals);
          if(of)
    	args=MINIMUM(f->num_args, of->locals - f->locals);
          args=MAXIMUM(args,0);
    
          ITEM(a)[frames].u.array=i=allocate_array_no_init(3+args,0);
          ITEM(a)[frames].type=T_ARRAY;
          assign_svalues_no_free(ITEM(i)+3, f->locals, args, BIT_MIXED);
          if(f->current_object->prog)
          {
    	ITEM(i)[2].type=T_FUNCTION;
    	ITEM(i)[2].subtype=f->fun;
    	ITEM(i)[2].u.object=f->current_object;
    	add_ref(f->current_object);
          }else{
    	ITEM(i)[2].type=T_INT;
    	ITEM(i)[2].subtype=NUMBER_DESTRUCTED;
    	ITEM(i)[2].u.integer=0;
          }
    
          if(f->pc)
          {
    	program_name=get_line(f->pc, f->context.prog, & ITEM(i)[1].u.integer);
    	ITEM(i)[1].subtype=NUMBER_NUMBER;
    	ITEM(i)[1].type=T_INT;
    
    	ITEM(i)[0].u.string=make_shared_string(program_name);
    #ifdef __CHECKER__
    	ITEM(i)[0].subtype=0;
    #endif
    	ITEM(i)[0].type=T_STRING;
          }else{
    	ITEM(i)[1].u.integer=0;
    	ITEM(i)[1].subtype=NUMBER_NUMBER;
    	ITEM(i)[1].type=T_INT;
    
    	ITEM(i)[0].u.integer=0;
    	ITEM(i)[0].subtype=NUMBER_NUMBER;
    	ITEM(i)[0].type=T_INT;
          }
        }else{
          ITEM(a)[frames].type=T_INT;
          ITEM(a)[frames].u.integer=0;
        }
      }
      a->type_field = BIT_ARRAY | BIT_INT;
    }
    
    void f_add_constant(INT32 args)
    {
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY, ("add_constant: permission denied.\n"));
      if(args<1)
        SIMPLE_TOO_FEW_ARGS_ERROR("add_constant", 1);
    
      if(sp[-args].type!=T_STRING)
        SIMPLE_BAD_ARG_ERROR("add_constant", 1, "string");
    
      if(args>1)
      {
        low_add_efun(sp[-args].u.string, sp-args+1);
      }else{
        low_add_efun(sp[-args].u.string, 0);
      }
      pop_n_elems(args);
    }
    
    #ifndef __NT__
    #define IS_SEP(X) ( (X)=='/' )
    #define IS_ABS(X) (IS_SEP((X)[0])?1:0)
    #else   
    #define IS_SEP(X) ( (X) == '/' || (X) == '\\' )
    #define IS_ABS(X) ((isalpha((X)[0]) && (X)[1]==':' && IS_SEP((X)[2]))?3:0)
    #define IS_ROOT(X) (IS_SEP((X)[0])?1:0)
    #endif
    
    static char *combine_path(char *cwd,char *file)
    {
      /* cwd is supposed to be combined already */
      char *ret;
      register char *from,*to;
      char *my_cwd;
      char cwdbuf[10];
      my_cwd=0;
      
    
      if(IS_ABS(file))
      {
        MEMCPY(cwdbuf,file,IS_ABS(file));
        cwdbuf[IS_ABS(file)]=0;
        cwd=cwdbuf;
        file+=IS_ABS(file);
      }
    
    #ifdef IS_ROOT
      else if(IS_ROOT(file))
      {
        if(IS_ABS(cwd))
        {
          MEMCPY(cwdbuf,cwd,IS_ABS(cwd));
          cwdbuf[IS_ABS(cwd)]=0;
          cwd=cwdbuf;
          file+=IS_ROOT(file);
        }else{
          MEMCPY(cwdbuf,file,IS_ROOT(file));
          cwdbuf[IS_ROOT(file)]=0;
          cwd=cwdbuf;
          file+=IS_ROOT(file);
        }
      }
    #endif
    
    #ifdef PIKE_DEBUG    
      if(!cwd)
        fatal("No cwd in combine_path!\n");
    #endif
    
      if(!*cwd || IS_SEP(cwd[strlen(cwd)-1]))
      {
        ret=(char *)xalloc(strlen(cwd)+strlen(file)+1);
        strcpy(ret,cwd);
        strcat(ret,file);
      }else{
        ret=(char *)xalloc(strlen(cwd)+strlen(file)+2);
        strcpy(ret,cwd);
        strcat(ret,"/");
        strcat(ret,file);
      }
    
      from=to=ret;
    
      /* Skip all leading "./" */
      while(from[0]=='.' && IS_SEP(from[1])) from+=2;
      
      while(( *to = *from ))
      {
        if(IS_SEP(*from))
        {
          while(to>ret && to[-1]=='/') to--;
          if(from[1] == '.')
          {
    	switch(from[2])
    	{
    	case '.':
    	  if(IS_SEP(from[3]) || !from[3])
    	  {
    	    char *tmp=to;
    	    while(--tmp>=ret)
    	      if(IS_SEP(*tmp))
    		break;
    	    tmp++;
    
    	    if(tmp[0]=='.' && tmp[1]=='.' && (IS_SEP(tmp[2]) || !tmp[2]))
    	      break;
    	    
    	    from+=3;
    	    to=tmp;
    	    continue;
    	  }
    	  break;
    
    	case 0:
    	case '/':
    #ifdef __NT__
            case '\\':
    #endif
    	  from+=2;
    	  continue;
    	}
          }
        }
        from++;
        to++;
      }
    
      if(*ret && !IS_SEP(from[-1]) && IS_SEP(to[-1]))
          *--to=0;
    
      if(!*ret)
      {
        if(IS_SEP(*cwd))
        {
          ret[0]='/';
          ret[1]=0;
        }else{
          ret[0]='.';
          ret[1]=0;
        }
      }
    
      if(my_cwd) free(my_cwd);
      return ret;
    }
    
    void f_combine_path(INT32 args)
    {
      char *path=0;
      int e,dofree=0;
      struct pike_string *ret;
    
      if(args<1)
        SIMPLE_TOO_FEW_ARGS_ERROR("combine_path", 1);
    
      if(sp[-args].type != T_STRING)
        SIMPLE_BAD_ARG_ERROR("combine_path", 1, "string");
    
      path=sp[-args].u.string->str;
    
      for(e=1;e<args;e++)
      {
        char *newpath;
        if(sp[e-args].type != T_STRING)
        {
          if(dofree) free(path);
          SIMPLE_BAD_ARG_ERROR("combine_path", e+1, "string");
        }
    
        newpath=combine_path(path,sp[e-args].u.string->str);
        if(dofree) free(path);
        path=newpath;
        dofree=1;
      }
        
      ret=make_shared_string(path);
      if(dofree) free(path);
      pop_n_elems(args);
      push_string(ret);
    }
    
    void f_function_object(INT32 args)
    {
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("function_object",1);
      if(sp[-args].type != T_FUNCTION)
        SIMPLE_BAD_ARG_ERROR("function_object",1,"function");
    
      if(sp[-args].subtype == FUNCTION_BUILTIN)
      {
        pop_n_elems(args);
        push_int(0);
      }else{
        pop_n_elems(args-1);
        sp[-1].type=T_OBJECT;
      }
    }
    
    void f_function_name(INT32 args)
    {
      struct pike_string *s;
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("function_name", 1);
      if(sp[-args].type != T_FUNCTION)
        SIMPLE_BAD_ARG_ERROR("function_name", 1, "function");
    
      if(sp[-args].subtype == FUNCTION_BUILTIN)
      {
        pop_n_elems(args);
        push_int(0);
      }else{
        if(!sp[-args].u.object->prog)
          bad_arg_error("function_name", sp-args, args, 1, "function", sp-args,
    		    "Destructed object.\n");
    
        copy_shared_string(s,ID_FROM_INT(sp[-args].u.object->prog,
    				     sp[-args].subtype)->name);
        pop_n_elems(args);
      
        sp->type=T_STRING;
        sp->u.string=s;
        sp++;
      }
    }
    
    void f_zero_type(INT32 args)
    {
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("zero_type",1);
    
      if(sp[-args].type != T_INT)
      {
        pop_n_elems(args);
        push_int(0);
      }
      else if((sp[-args].type==T_OBJECT || sp[-args].type==T_FUNCTION)
    	   && !sp[-args].u.object->prog)
      {
        pop_n_elems(args);
        push_int(NUMBER_DESTRUCTED);
      }
      {
        pop_n_elems(args-1);
        sp[-1].u.integer=sp[-1].subtype;
        sp[-1].subtype=NUMBER_NUMBER;
      }
    }
    
    /*
     * Some wide-strings related functions
     */
    
    void f_string_to_unicode(INT32 args)
    {
      struct pike_string *in;
      struct pike_string *out = NULL;
      INT32 len;
      int i;
    
      get_all_args("string_to_unicode", args, "%W", &in);
    
      switch(in->size_shift) {
      case 0:
        /* Just 8bit characters */
        len = in->len * 2;
        out = begin_shared_string(len);
        MEMSET(out->str, 0, len);	/* Clear the upper (and lower) byte */
        for(i = in->len; i--;) {
          out->str[i * 2 + 1] = in->str[i];
        }
        out = end_shared_string(out);
        break;
      case 1:
        /* 16 bit characters */
        /* FIXME: Should we check for 0xfffe & 0xffff here too? */
        len = in->len * 2;
        out = begin_shared_string(len);
    #if (PIKE_BYTEORDER == 4321)
        /* Big endian -- We don't need to do much...
         *
         * FIXME: Future optimization: Check if refcount is == 1,
         * and perform sufficient magic to be able to convert in place.
         */
        MEMCPY(out->str, in->str, len);
    #else
        /* Other endianness, may need to do byte-order conversion also. */
        {
          p_wchar1 *str1 = STR1(in);
          for(i = in->len; i--;) {
    	unsigned INT32 c = str1[i];
    	out->str[i * 2 + 1] = c & 0xff;
    	out->str[i * 2] = c >> 8;
          }
        }
    #endif
        out = end_shared_string(out);
        break;
      case 2:
        /* 32 bit characters -- Is someone writing in Klingon? */
        {
          p_wchar2 *str2 = STR2(in);
          int j;
          len = in->len * 2;
          /* Check how many extra wide characters there are. */
          for(i = in->len; i--;) {
    	if (str2[i] > 0xfffd) {
    	  if (str2[i] < 0x10000) {
    	    /* 0xfffe: Byte-order detection illegal character.
    	     * 0xffff: Illegal character.
    	     */
    	    error("string_to_unicode(): Illegal character 0x%04x (index %d) "
    		  "is not a Unicode character.", str2[i], i);
    	  }
    	  if (str2[i] > 0x10ffff) {
    	    error("string_to_unicode(): Character 0x%08x (index %d) "
    		  "is out of range (0x00000000 - 0x0010ffff).", str2[i], i);
    	  }
    	  /* Extra wide characters take two unicode characters in space.
    	   * ie One unicode character extra.
    	   */
    	  len += 2;
    	}
          }
          out = begin_shared_string(len);
          j = len;
          for(i = in->len; i--;) {
    	unsigned INT32 c = str2[i];
    
    	j -= 2;
    
    	if (c > 0xffff) {
    	  /* Use surrogates */
    	  c -= 0x10000;
    	  
    	  out->str[j + 1] = c & 0xff;
    	  out->str[j] = 0xdc | ((c >> 8) & 0x03);
    	  j -= 2;
    	  c >>= 10;
    	  c |= 0xd800;
    	}
    	out->str[j + 1] = c & 0xff;
    	out->str[j] = c >> 8;
          }
    #ifdef PIKE_DEBUG
          if (j) {
    	fatal("string_to_unicode(): Indexing error: len:%d, j:%d.\n", len, j);
          }
    #endif /* PIKE_DEBUG */
          out = end_shared_string(out);
        }
        break;
      default:
        error("string_to_unicode(): Bad string shift: %d!\n", in->size_shift);
        break;
      }
      pop_n_elems(args);
      push_string(out);
    }
    
    void f_unicode_to_string(INT32 args)
    {
      struct pike_string *in;
      struct pike_string *out = NULL;
      INT32 len;
    
      get_all_args("unicode_to_string", args, "%S", &in);
    
      if (in->len & 1) {
        bad_arg_error("unicode_to_string", sp-args, args, 1, "string", sp-args,
    		  "String length is odd.\n");
      }
    
      /* FIXME: In the future add support for decoding of surrogates. */
    
      len = in->len / 2;
    
      out = begin_wide_shared_string(len, 1);
    #if (PIKE_BYTEORDER == 4321)
      /* Big endian
       *
       * FIXME: Future optimization: Perform sufficient magic
       * to do the conversion in place if the ref-count is == 1.
       */
      MEMCPY(out->str, in->str, in->len);
    #else
      /* Little endian */
      {
        int i;
        p_wchar1 *str1 = STR1(out);
    
        for (i = len; i--;) {
          str1[i] = (((unsigned char *)in->str)[i*2]<<8) +
    	((unsigned char *)in->str)[i*2 + 1];
        }
      }
    #endif /* PIKE_BYTEORDER == 4321 */
      out = end_shared_string(out);
      pop_n_elems(args);
      push_string(out);
    }
    
    void f_string_to_utf8(INT32 args)
    {
      int len;
      struct pike_string *in;
      struct pike_string *out;
      int i,j;
      int extended = 0;
    
      get_all_args("string_to_utf8", args, "%W", &in);
    
      if (args > 1) {
        if (sp[1-args].type != T_INT) {
          SIMPLE_BAD_ARG_ERROR("string_to_utf8", 2, "int|void");
        }
        extended = sp[1-args].u.integer;
      }
    
      len = in->len;
    
      for(i=0; i < in->len; i++) {
        unsigned INT32 c = index_shared_string(in, i);
        if (c & ~0x7f) {
          /* 8bit or more. */
          len++;
          if (c & ~0x7ff) {
    	/* 12bit or more. */
    	len++;
    	if (c & ~0xffff) {
    	  /* 17bit or more. */
    	  len++;
    	  if (c & ~0x1fffff) {
    	    /* 22bit or more. */
    	    len++;
    	    if (c & ~0x3ffffff) {
    	      /* 27bit or more. */
    	      len++;
    	      if (c & ~0x7fffffff) {
    		/* 32bit or more. */
    		if (!extended) {
    		  error("string_to_utf8(): "
    			"Value 0x%08x (index %d) is larger than 31 bits.\n",
    			c, i);
    		}
    		len++;
    		/* FIXME: Needs fixing when we get 64bit chars... */
    	      }
    	    }
    	  }
    	}
          }
        }
      }
      if (len == in->len) {
        /* 7bit string -- already valid utf8. */
        pop_n_elems(args - 1);
        return;
      }
      out = begin_shared_string(len);
    
      for(i=j=0; i < in->len; i++) {
        unsigned INT32 c = index_shared_string(in, i);
        if (!(c & ~0x7f)) {
          /* 7bit */
          out->str[j++] = c;
        } else if (!(c & ~0x7ff)) {
          /* 11bit */
          out->str[j++] = 0xc0 | (c >> 6);
          out->str[j++] = 0x80 | (c & 0x3f);
        } else if (!(c & ~0xffff)) {
          /* 16bit */
          out->str[j++] = 0xe0 | (c >> 12);
          out->str[j++] = 0x80 | ((c >> 6) & 0x3f);
          out->str[j++] = 0x80 | (c & 0x3f);
        } else if (!(c & ~0x1fffff)) {
          /* 21bit */
          out->str[j++] = 0xf0 | (c >> 18);
          out->str[j++] = 0x80 | ((c >> 12) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 6) & 0x3f);
          out->str[j++] = 0x80 | (c & 0x3f);
        } else if (!(c & ~0x3ffffff)) {
          /* 26bit */
          out->str[j++] = 0xf8 | (c >> 24);
          out->str[j++] = 0x80 | ((c >> 18) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 12) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 6) & 0x3f);
          out->str[j++] = 0x80 | (c & 0x3f);
        } else if (!(c & ~0x7fffffff)) {
          /* 31bit */
          out->str[j++] = 0xfc | (c >> 30);
          out->str[j++] = 0x80 | ((c >> 24) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 18) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 12) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 6) & 0x3f);
          out->str[j++] = 0x80 | (c & 0x3f);
        } else {
          /* This and onwards is extended UTF-8 encoding. */
          /* 32 - 36bit */
          out->str[j++] = 0xfe;
          out->str[j++] = 0x80 | ((c >> 30) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 24) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 18) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 12) & 0x3f);
          out->str[j++] = 0x80 | ((c >> 6) & 0x3f);
          out->str[j++] = 0x80 | (c & 0x3f);
        }
      }
    #ifdef PIKE_DEBUG
      if (len != j) {
        fatal("string_to_utf8(): Calculated and actual lengths differ: %d != %d\n",
    	  len, j);
      }
    #endif /* PIKE_DEBUG */
      out = end_shared_string(out);
      pop_n_elems(args);
      push_string(out);
    }
    
    void f_utf8_to_string(INT32 args)
    {
      struct pike_string *in;
      struct pike_string *out;
      int len = 0;
      int shift = 0;
      int i,j;
      int extended = 0;
    
      get_all_args("utf8_to_string", args, "%S", &in);
    
      if (args > 1) {
        if (sp[1-args].type != T_INT) {
          SIMPLE_BAD_ARG_ERROR("utf8_to_string()", 2, "int|void");
        }
        extended = sp[1-args].u.integer;
      }
    
      for(i=0; i < in->len; i++) {
        unsigned int c = ((unsigned char *)in->str)[i];
        len++;
        if (c & 0x80) {
          int cont = 0;
          if ((c & 0xc0) == 0x80) {
    	error("utf8_to_string(): "
    	      "Unexpected continuation block 0x%02x at index %d.\n",
    	      c, i);
          }
          if ((c & 0xe0) == 0xc0) {
    	/* 11bit */
    	cont = 1;
    	if (c & 0x1c) {
    	  if (shift < 1) {
    	    shift = 1;
    	  }
    	}
          } else if ((c & 0xf0) == 0xe0) {
    	/* 16bit */
    	cont = 2;
    	if (shift < 1) {
    	  shift = 1;
    	}
          } else {
    	shift = 2;
    	if ((c & 0xf8) == 0xf0) {
    	  /* 21bit */
    	  cont = 3;
    	} else if ((c & 0xfc) == 0xf8) {
    	  /* 26bit */
    	  cont = 4;
    	} else if ((c & 0xfe) == 0xfc) {
    	  /* 31bit */
    	  cont = 5;
    	} else if (c == 0xfe) {
    	  /* 36bit */
    	  if (!extended) {
    	    error("utf8_to_string(): "
    		  "Character 0xfe at index %d when not in extended mode.\n",
    		  i);
    	  }
    	  cont = 6;
    	} else {
    	  error("utf8_to_string(): "
    		"Unexpected character 0xff at index %d.\n",
    		i);
    	}
          }
          while(cont--) {
    	i++;
    	if (i >= in->len) {
    	  error("utf8_to_string(): Truncated UTF8 sequence.\n");
    	}
    	c = ((unsigned char *)(in->str))[i];
    	if ((c & 0xc0) != 0x80) {
    	  error("utf8_to_string(): "
    		"Expected continuation character at index %d (got 0x%02x).\n",
    		i, c);
    	}
          }
        }
      }
      if (len == in->len) {
        /* 7bit in == 7bit out */
        pop_n_elems(args-1);
        return;
      }
    
      out = begin_wide_shared_string(len, shift);
      
      for(j=i=0; i < in->len; i++) {
        unsigned int c = ((unsigned char *)in->str)[i];
    
        if (c & 0x80) {
          int cont = 0;
    
          /* NOTE: The tests aren't as paranoid here, since we've
           * already tested the string above.
           */
          if ((c & 0xe0) == 0xc0) {
    	/* 11bit */
    	cont = 1;
    	c &= 0x1f;
          } else if ((c & 0xf0) == 0xe0) {
    	/* 16bit */
    	cont = 2;
    	c &= 0x0f;
          } else if ((c & 0xf8) == 0xf0) {
    	/* 21bit */
    	cont = 3;
    	c &= 0x07;
          } else if ((c & 0xfc) == 0xf8) {
    	/* 26bit */
    	cont = 4;
    	c &= 0x03;
          } else if ((c & 0xfe) == 0xfc) {
    	/* 31bit */
    	cont = 5;
    	c &= 0x01;
          } else {
    	/* 36bit */
    	cont = 6;
    	c = 0;
          }
          while(cont--) {
    	unsigned INT32 c2 = ((unsigned char *)(in->str))[++i] & 0x3f;
    	c = (c << 6) | c2;
          }
        }
        low_set_index(out, j++, c);
      }
    #ifdef PIKE_DEBUG
      if (j != len) {
        fatal("utf8_to_string(): Calculated and actual lengths differ: %d != %d\n",
    	  len, j);
      }
    #endif /* PIKE_DEBUG */
      out = end_shared_string(out);
      pop_n_elems(args);
      push_string(out);
    }
    
    void f_all_constants(INT32 args)
    {
      pop_n_elems(args);
      ref_push_mapping(get_builtin_constants());
    }
    
    void f_allocate(INT32 args)
    {
      INT32 size;
      struct array *a;
    
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("allocate",1);
    
      if(sp[-args].type!=T_INT)
        SIMPLE_BAD_ARG_ERROR("allocate",1,"int");
    
      size=sp[-args].u.integer;
      if(size < 0)
        PIKE_ERROR("allocate", "Can't allocate array of negative size.\n", sp, args);
      a=allocate_array(size);
      if(args>1)
      {
        INT32 e;
        for(e=0;e<a->size;e++)
          copy_svalues_recursively_no_free(a->item+e, sp-args+1, 1, 0);
      }
      pop_n_elems(args);
      push_array(a);
    }
    
    void f_rusage(INT32 args)
    {
      INT32 *rus,e;
      struct array *v;
      pop_n_elems(args);
      rus=low_rusage();
      if(!rus)
        PIKE_ERROR("rusage", "System rusage information not available.\n", sp, args);
      v=allocate_array_no_init(29,0);
    
      for(e=0;e<29;e++)
      {
        ITEM(v)[e].type=T_INT;
        ITEM(v)[e].subtype=NUMBER_NUMBER;
        ITEM(v)[e].u.integer=rus[e];
      }
    
      sp->u.array=v;
      sp->type=T_ARRAY;
      sp++;
    }
    
    void f_this_object(INT32 args)
    {
      pop_n_elems(args);
      if(fp)
      {
        sp->u.object=fp->current_object;
        sp->type=T_OBJECT;
        add_ref(fp->current_object);
        sp++;
      }else{
        push_int(0);
      }
    }
    
    void f_throw(INT32 args)
    {
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("throw", 1);
      assign_svalue(&throw_value,sp-args);
      pop_n_elems(args);
      throw_severity=0;
      pike_throw();
    }
    
    void f_exit(INT32 args)
    {
      static int in_exit=0;
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY, ("exit: permission denied.\n"));
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("exit", 1);
    
      if(sp[-args].type != T_INT)
        SIMPLE_BAD_ARG_ERROR("exit", 1, "int");
    
      if(in_exit) error("exit already called!\n");
      in_exit=1;
    
      assign_svalue(&throw_value, sp-args);
      throw_severity=THROW_EXIT;
      pike_throw();
    }
    
    void f__exit(INT32 args)
    {
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY, ("_exit: permission denied.\n"));
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("_exit", 1);
    
      if(sp[-args].type != T_INT)
        SIMPLE_BAD_ARG_ERROR("_exit", 1, "int");
    
      exit(sp[-args].u.integer);
    }
    
    void f_time(INT32 args)
    {
      if(!args)
      {
        GETTIMEOFDAY(&current_time);
      }else{
        if(sp[-args].type == T_INT && sp[-args].u.integer > 1)
        {
          struct timeval tmp;
          GETTIMEOFDAY(&current_time);
          tmp.tv_sec=sp[-args].u.integer;
          tmp.tv_usec=0;
          my_subtract_timeval(&tmp,&current_time);
          pop_n_elems(args);
          push_float( - (float)tmp.tv_sec - ((float)tmp.tv_usec)/1000000 );
          return;
        }
      }
      pop_n_elems(args);
      push_int(current_time.tv_sec);
    }
    
    void f_crypt(INT32 args)
    {
      char salt[2];
      char *ret, *saltp;
      char *choise =
        "cbhisjKlm4k65p7qrJfLMNQOPxwzyAaBDFgnoWXYCZ0123tvdHueEGISRTUV89./";
    
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("crypt", 1);
    
      if(sp[-args].type != T_STRING)
        SIMPLE_BAD_ARG_ERROR("crypt", 1, "string");
    
      
      if(args>1)
      {
        if(sp[1-args].type != T_STRING ||
           sp[1-args].u.string->len < 2)
        {
          pop_n_elems(args);
          push_int(0);
          return;
        }
          
        saltp=sp[1-args].u.string->str;
      } else {
        unsigned int foo; /* Sun CC want's this :( */
        foo=my_rand();
        salt[0] = choise[foo % (unsigned int) strlen(choise)];
        foo=my_rand();
        salt[1] = choise[foo % (unsigned int) strlen(choise)];
        saltp=salt;
      }
    #ifdef HAVE_CRYPT
      ret = (char *)crypt(sp[-args].u.string->str, saltp);
    #else
    #ifdef HAVE__CRYPT
      ret = (char *)_crypt(sp[-args].u.string->str, saltp);
    #else
      ret = sp[-args].u.string->str;
    #endif
    #endif
      if(args < 2)
      {
        pop_n_elems(args);
        push_string(make_shared_string(ret));
      }else{
        int i;
        i=!strcmp(ret,sp[1-args].u.string->str);
        pop_n_elems(args);
        push_int(i);
      }
    }
    
    void f_destruct(INT32 args)
    {
      struct object *o;
      if(args)
      {
        if(sp[-args].type != T_OBJECT)
          SIMPLE_BAD_ARG_ERROR("destruct", 1, "object");
        
        o=sp[-args].u.object;
      }else{
        if(!fp)
          PIKE_ERROR("destruct", "Destruct called without argument from callback function.\n", sp, args);
    	   
        o=fp->current_object;
      }
    #ifdef PIKE_SECURITY
      if(!CHECK_DATA_SECURITY(o, SECURITY_BIT_DESTRUCT))
        error("Destruct permission denied.\n");
    #endif
      destruct(o);
      pop_n_elems(args);
    }
    
    void f_indices(INT32 args)
    {
      INT32 size;
      struct array *a;
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("indices", 1);
    
      switch(sp[-args].type)
      {
      case T_STRING:
        size=sp[-args].u.string->len;
        goto qjump;
    
      case T_ARRAY:
        size=sp[-args].u.array->size;
    
      qjump:
        a=allocate_array_no_init(size,0);
        while(--size>=0)
        {
          ITEM(a)[size].type=T_INT;
          ITEM(a)[size].subtype=NUMBER_NUMBER;
          ITEM(a)[size].u.integer=size;
        }
        break;
    
      case T_MAPPING:
        a=mapping_indices(sp[-args].u.mapping);
        break;
    
      case T_MULTISET:
        a=copy_array(sp[-args].u.multiset->ind);
        break;
    
      case T_OBJECT:
        a=object_indices(sp[-args].u.object);
        break;
    
      case T_PROGRAM:
        a = program_indices(sp[-args].u.program);
        break;
    
      case T_FUNCTION:
        {
          struct program *p = program_from_svalue(sp-args);
          if (p) {
    	a = program_indices(p);
    	break;
          }
        }
        /* FALL THROUGH */
    
      default:
        SIMPLE_BAD_ARG_ERROR("indices", 1,
    			 "string|array|mapping|"
    			 "multiset|object|program|function");
        return; /* make apcc happy */
      }
      pop_n_elems(args);
      push_array(a);
    }
    
    void f_values(INT32 args)
    {
      INT32 size;
      struct array *a;
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("values", 1);
    
      switch(sp[-args].type)
      {
      case T_STRING:
        size = sp[-args].u.string->len;
        a = allocate_array_no_init(size,0);
        while(--size >= 0)
        {
          ITEM(a)[size].type = T_INT;
          ITEM(a)[size].subtype = NUMBER_NUMBER;
          ITEM(a)[size].u.integer = index_shared_string(sp[-args].u.string, size);
        }
        break;
    
      case T_ARRAY:
        a=copy_array(sp[-args].u.array);
        break;
    
      case T_MAPPING:
        a=mapping_values(sp[-args].u.mapping);
        break;
    
      case T_MULTISET:
        size=sp[-args].u.multiset->ind->size;
        a=allocate_array_no_init(size,0);
        while(--size>=0)
        {
          ITEM(a)[size].type=T_INT;
          ITEM(a)[size].subtype=NUMBER_NUMBER;
          ITEM(a)[size].u.integer=1;
        }
        break;
    
      case T_OBJECT:
        a=object_values(sp[-args].u.object);
        break;
    
      case T_PROGRAM:
        a = program_values(sp[-args].u.program);
        break;
    
      case T_FUNCTION:
        {
          struct program *p = program_from_svalue(sp - args);
          if (p) {
    	a = program_values(p);
    	break;
          }
        }
        /* FALL THROUGH */
    
      default:
        SIMPLE_BAD_ARG_ERROR("values", 1,
    			 "string|array|mapping|multiset|"
    			 "object|program|function");
        return;  /* make apcc happy */
      }
      pop_n_elems(args);
      push_array(a);
    }
    
    void f_next_object(INT32 args)
    {
      struct object *o;
      if(args < 1)
      {
        o=first_object;
      }else{
        if(sp[-args].type != T_OBJECT)
          SIMPLE_BAD_ARG_ERROR("next_object", 1, "object");
        o=sp[-args].u.object->next;
        while(o && !o->prog) o=o->next;
      }
      pop_n_elems(args);
      if(!o)
      {
        push_int(0);
      }else{
        ref_push_object(o);
      }
    }
    
    void f_object_program(INT32 args)
    {
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("object_program", 1);
    
      if(sp[-args].type == T_OBJECT)
      {
        struct object *o=sp[-args].u.object;
        struct program *p;
        if((p=o->prog))
        {
          if(o->parent && o->parent->prog)
          {
    	INT32 id=o->parent_identifier;
    	o=o->parent;
    	add_ref(o);
    	pop_n_elems(args);
    	push_object(o);
    	sp[-1].subtype=id;
    	sp[-1].type=T_FUNCTION;
    	return;
          }else{
    	add_ref(p);
    	pop_n_elems(args);
    	push_program(p);
    	return;
          }
        }
      }
    
      pop_n_elems(args);
      push_int(0);
    }
    
    void f_reverse(INT32 args)
    {
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("reverse", 1);
    
      switch(sp[-args].type)
      {
      case T_STRING:
      {
        INT32 e;
        struct pike_string *s;
        s=begin_wide_shared_string(sp[-args].u.string->len,
    			  sp[-args].u.string->size_shift);
        switch(sp[-args].u.string->size_shift)
        {
          case 0:
    	for(e=0;e<sp[-args].u.string->len;e++)
    	  STR0(s)[e]=STR0(sp[-args].u.string)[sp[-args].u.string->len-1-e];
    	break;
    
          case 1:
    	for(e=0;e<sp[-args].u.string->len;e++)
    	  STR1(s)[e]=STR1(sp[-args].u.string)[sp[-args].u.string->len-1-e];
    	break;
    
          case 2:
    	for(e=0;e<sp[-args].u.string->len;e++)
    	  STR2(s)[e]=STR2(sp[-args].u.string)[sp[-args].u.string->len-1-e];
    	break;
        }
        s=low_end_shared_string(s);
        pop_n_elems(args);
        push_string(s);
        break;
      }
    
      case T_INT:
      {
        INT32 e;
        e=sp[-args].u.integer;
        e=((e & 0x55555555UL)<<1) + ((e & 0xaaaaaaaaUL)>>1);
        e=((e & 0x33333333UL)<<2) + ((e & 0xccccccccUL)>>2);
        e=((e & 0x0f0f0f0fUL)<<4) + ((e & 0xf0f0f0f0UL)>>4);
        e=((e & 0x00ff00ffUL)<<8) + ((e & 0xff00ff00UL)>>8);
        e=((e & 0x0000ffffUL)<<16)+ ((e & 0xffff0000UL)>>16);
        sp[-args].u.integer=e;
        pop_n_elems(args-1);
        break;
      }
        
      case T_ARRAY:
      {
        struct array *a;
        a=reverse_array(sp[-args].u.array);
        pop_n_elems(args);
        push_array(a);
        break;
      }
    
      default:
        SIMPLE_BAD_ARG_ERROR("reverse", 1, "string|int|array");    
      }
    }
    
    struct tupel
    {
      int prefix;
      struct pike_string *ind;
      struct pike_string *val;
    };
    
    static int replace_sortfun(struct tupel *a,struct tupel *b)
    {
      return my_quick_strcmp(a->ind,b->ind);
    }
    
    /* Magic, magic and more magic */
    static int find_longest_prefix(char *str,
    			       INT32 len,
    			       int size_shift,
    			       struct tupel *v,
    			       INT32 a,
    			       INT32 b)
    {
      INT32 tmp,c,match=-1;
      while(a<b)
      {
        c=(a+b)/2;
        
        tmp=generic_quick_binary_strcmp(v[c].ind->str,
    				    v[c].ind->len,
    				    v[c].ind->size_shift,
    				    str,
    				    MINIMUM(len,v[c].ind->len),
    				    size_shift);
        if(tmp<0)
        {
          INT32 match2=find_longest_prefix(str,
    				       len,
    				       size_shift,
    				       v,
    				       c+1,
    				       b);
          if(match2!=-1) return match2;
    
          while(1)
          {
    	if(v[c].prefix==-2)
    	{
    	  v[c].prefix=find_longest_prefix(v[c].ind->str,
    					  v[c].ind->len,
    					  v[c].ind->size_shift,
    					  v,
    					  0 /* can this be optimized? */,
    					  c);
    	}
    	c=v[c].prefix;
    	if(c<a || c<match) return match;
    
    	if(!generic_quick_binary_strcmp(v[c].ind->str,
    					v[c].ind->len,
    					v[c].ind->size_shift,
    					str,
    					MINIMUM(len,v[c].ind->len),
    					size_shift))
    	   return c;
          }
        }
        else if(tmp>0)
        {
          b=c;
        }
        else
        {
          a=c+1; /* There might still be a better match... */
          match=c;
        }
      }
      return match;
    }
    			       
    
    static struct pike_string * replace_many(struct pike_string *str,
    					struct array *from,
    					struct array *to)
    {
      INT32 s,length,e,num;
      struct string_builder ret;
    
      struct tupel *v;
    
      int set_start[256];
      int set_end[256];
    
      if(from->size != to->size)
        error("Replace must have equal-sized from and to arrays.\n");
    
      if(!from->size)
      {
        reference_shared_string(str);
        return str;
      }
    
      v=(struct tupel *)xalloc(sizeof(struct tupel)*from->size);
    
      for(num=e=0;e<from->size;e++)
      {
        if(ITEM(from)[e].type != T_STRING)
        {
          free((char *)v);
          error("Replace: from array is not array(string)\n");
        }
    
        if(ITEM(to)[e].type != T_STRING)
        {
          free((char *)v);
          error("Replace: to array is not array(string)\n");
        }
    
        if(ITEM(from)[e].u.string->size_shift > str->size_shift)
          continue;
    
        v[num].ind=ITEM(from)[e].u.string;
        v[num].val=ITEM(to)[e].u.string;
        v[num].prefix=-2; /* Uninitialized */
        num++;
      }
    
      fsort((char *)v,num,sizeof(struct tupel),(fsortfun)replace_sortfun);
    
      for(e=0;e<(INT32)NELEM(set_end);e++)
        set_end[e]=set_start[e]=0;
    
      for(e=0;e<num;e++)
      {
        INT32 x;
        x=index_shared_string(v[num-1-e].ind,0);
        if(x<(INT32)NELEM(set_start)) set_start[x]=num-e-1;
        x=index_shared_string(v[e].ind,0);
        if(x<(INT32)NELEM(set_end)) set_end[x]=e+1;
      }
    
      init_string_builder(&ret,str->size_shift);
    
      length=str->len;
    
      for(s=0;length > 0;)
      {
        INT32 a,b,ch;
    
        ch=index_shared_string(str,s);
        if(ch<(INT32)NELEM(set_end)) b=set_end[ch]; else b=num;
    
        if(b)
        {
          if(ch<(INT32)NELEM(set_start)) a=set_start[ch]; else a=0;
    
          a=find_longest_prefix(str->str+(s << str->size_shift),
    			    length,
    			    str->size_shift,
    			    v, a, b);
    
          if(a!=-1)
          {
    	ch=v[a].ind->len;
    	if(!ch) ch=1;
    	s+=ch;
    	length-=ch;
    	string_builder_shared_strcat(&ret,v[a].val);
    	continue;
          }
        }
        string_builder_putchar(&ret, ch);
        s++;
        length--;
      }
    
      free((char *)v);
      return finish_string_builder(&ret);
    }
    
    void f_replace(INT32 args)
    {
      if(args < 3)
        SIMPLE_TOO_FEW_ARGS_ERROR("replace", 3);
    
      switch(sp[-args].type)
      {
      case T_ARRAY:
      {
        array_replace(sp[-args].u.array,sp+1-args,sp+2-args);
        pop_n_elems(args-1);
        break;
      }
    
      case T_MAPPING:
      {
        mapping_replace(sp[-args].u.mapping,sp+1-args,sp+2-args);
        pop_n_elems(args-1);
        break;
      }
    
      case T_STRING:
      {
        struct pike_string *s;
        switch(sp[1-args].type)
        {
        default:
          SIMPLE_BAD_ARG_ERROR("replace", 2, "string|array");
          
        case T_STRING:
          if(sp[2-args].type != T_STRING)
    	SIMPLE_BAD_ARG_ERROR("replace", 3, "string");
    
          s=string_replace(sp[-args].u.string,
    		       sp[1-args].u.string,
    		       sp[2-args].u.string);
          break;
          
        case T_ARRAY:
          if(sp[2-args].type != T_ARRAY)
    	SIMPLE_BAD_ARG_ERROR("replace", 3, "array");
    
          s=replace_many(sp[-args].u.string,
    		     sp[1-args].u.array,
    		     sp[2-args].u.array);
        
        }
        pop_n_elems(args);
        push_string(s);
        break;
      }
    
      default:
        SIMPLE_BAD_ARG_ERROR("replace", 1, "array|mapping|string");
      }
    }
    
    void f_compile(INT32 args)
    {
      struct program *p;
    
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("compile", 1);
    
      if(sp[-args].type != T_STRING)
        SIMPLE_BAD_ARG_ERROR("compile", 1, "string");
    
      p=compile(sp[-args].u.string);
      pop_n_elems(args);
      push_program(p);
    }
    
    void f_mkmapping(INT32 args)
    {
      struct mapping *m;
      struct array *a,*b;
      get_all_args("mkmapping",args,"%a%a",&a,&b);
      if(a->size != b->size)
        bad_arg_error("mkmapping", sp-args, args, 2, "array", sp+1-args,
    		  "mkmapping called on arrays of different sizes (%d != %d)\n",
    		  a->size, b->size);
    
      m=mkmapping(sp[-args].u.array, sp[1-args].u.array);
      pop_n_elems(args);
      push_mapping(m);
    }
    
    #define SETFLAG(FLAGS,FLAG,ONOFF) \
      FLAGS = (FLAGS & ~FLAG) | ( ONOFF ? FLAG : 0 )
    void f_set_weak_flag(INT32 args)
    {
      struct svalue *s;
      INT_TYPE ret;
    
      get_all_args("set_weak_flag",args,"%*%i",&s,&ret);
    
      switch(s->type)
      {
        case T_ARRAY:
          SETFLAG(s->u.array->flags,ARRAY_WEAK_FLAG,ret);
          break;
        case T_MAPPING:
          SETFLAG(s->u.mapping->flags,MAPPING_FLAG_WEAK,ret);
          break;
        default:
          SIMPLE_BAD_ARG_ERROR("set_weak_flag",1,"array|mapping");
      }
      pop_n_elems(args-1);
    }
    
    
    
    void f_objectp(INT32 args)
    {
      if(args<1)
        SIMPLE_TOO_FEW_ARGS_ERROR("objectp", 1);
      if(sp[-args].type != T_OBJECT || !sp[-args].u.object->prog)
      {
        pop_n_elems(args);
        push_int(0);
      }else{
        pop_n_elems(args);
        push_int(1);
      }
    }
    
    void f_functionp(INT32 args)
    {
      if(args<1)
        SIMPLE_TOO_FEW_ARGS_ERROR("functionp", 1);
      if(sp[-args].type != T_FUNCTION ||
         (sp[-args].subtype != FUNCTION_BUILTIN && !sp[-args].u.object->prog))
      {
        pop_n_elems(args);
        push_int(0);
      }else{
        pop_n_elems(args);
        push_int(1);
      }
    }
    
    #ifndef HAVE_AND_USE_POLL
    #undef HAVE_POLL
    #endif
    
    void f_sleep(INT32 args)
    {
      struct timeval t1,t2,t3;
      INT32 a,b;
    
      if(!args)
        SIMPLE_TOO_FEW_ARGS_ERROR("sleep", 1);
    
      switch(sp[-args].type)
      {
      case T_INT:
        t2.tv_sec=sp[-args].u.integer;
        t2.tv_usec=0;
        break;
    
      case T_FLOAT:
      {
        FLOAT_TYPE f;
        f=sp[-args].u.float_number;
        t2.tv_sec=floor(f);
        t2.tv_usec=(long)(1000000.0*(f-floor(f)));
        break;
      }
    
      default:
        SIMPLE_BAD_ARG_ERROR("sleep", 1, "int|float");
      }
      pop_n_elems(args);
    
    
      if( args >1 && !IS_ZERO(sp-args+1))
      {
        THREADS_ALLOW();
    #ifdef __NT__
        Sleep(t2.tv_sec * 1000 + t2.tv_usec / 1000);
    #elif defined(HAVE_POLL)
        poll(NULL, 0, t2.tv_sec * 1000 + t2.tv_usec / 1000);
    #else
        select(0,0,0,0,&t2);
    #endif
        THREADS_DISALLOW();
      }else{
        GETTIMEOFDAY(&t1);
        my_add_timeval(&t1, &t2);
        
        while(1)
        {
          GETTIMEOFDAY(&t2);
          if(my_timercmp(&t1, <= , &t2))
    	return;
          
          t3=t1;
          my_subtract_timeval(&t3, &t2);
          
          THREADS_ALLOW();
    #ifdef __NT__
          Sleep(t3.tv_sec * 1000 + t3.tv_usec / 1000);
    #elif defined(HAVE_POLL)
          poll(NULL, 0, t3.tv_sec * 1000 + t3.tv_usec / 1000);
    #else
          select(0,0,0,0,&t3);
    #endif
          THREADS_DISALLOW();
          check_signals(0,0,0);
        }
      }
    }
    
    void f_gc(INT32 args)
    {
      INT32 tmp;
      pop_n_elems(args);
      tmp=num_objects;
      do_gc();
      push_int(tmp - num_objects);
    }
    
    #ifdef TYPEP
    #undef TYPEP
    #endif
    
    #define TYPEP(ID,NAME,TYPE) \
    void ID(INT32 args) \
    { \
      int t; \
      if(args<1) SIMPLE_TOO_FEW_ARGS_ERROR(NAME, 1); \
      t=sp[-args].type == TYPE; \
      pop_n_elems(args); \
      push_int(t); \
    }
    
    
    void f_programp(INT32 args)
    {
      if(args<1)
        SIMPLE_TOO_FEW_ARGS_ERROR("programp", 1);
      switch(sp[-args].type)
      {
      case T_PROGRAM:
        pop_n_elems(args);
        push_int(1);
        return;
    
      case T_FUNCTION:
        if(program_from_function(sp-args))
        {
          pop_n_elems(args);
          push_int(1);
          return;
        }
    
      default:
        pop_n_elems(args);
        push_int(0);
      }
    }
    
    TYPEP(f_intp, "intpp", T_INT)
    TYPEP(f_mappingp, "mappingp", T_MAPPING)
    TYPEP(f_arrayp, "arrayp", T_ARRAY)
    TYPEP(f_multisetp, "multisetp", T_MULTISET)
    TYPEP(f_stringp, "stringp", T_STRING)
    TYPEP(f_floatp, "floatp", T_FLOAT)
    
    void f_sort(INT32 args)
    {
      INT32 e,*order;
    
      if(args < 1)
        SIMPLE_TOO_FEW_ARGS_ERROR("sort", 1);
    
      for(e=0;e<args;e++)
      {
        if(sp[e-args].type != T_ARRAY)
          SIMPLE_BAD_ARG_ERROR("sort", e+1, "array");
    
        if(sp[e-args].u.array->size != sp[-args].u.array->size)
          bad_arg_error("sort", sp-args, args, e+1, "array", sp+e-args,
    		    "Argument %d has wrong size.\n", (e+1));
      }
    
      if(args > 1)
      {
        order=get_alpha_order(sp[-args].u.array);
        for(e=0;e<args;e++) order_array(sp[e-args].u.array,order);
        free((char *)order);
        pop_n_elems(args-1);
      } else {
        sort_array_destructively(sp[-args].u.array);
      }
    }
    
    void f_rows(INT32 args)
    {
      INT32 e;
      struct array *a,*tmp;
      struct svalue *val;
    
      get_all_args("rows", args, "%*%a", &val, &tmp);
    
      /* Optimization */
      if(tmp->refs == 1)
      {
        struct svalue sval;
        tmp->type_field = BIT_MIXED | BIT_UNFINISHED;
        for(e=0;e<tmp->size;e++)
        {
          index_no_free(&sval, val, ITEM(tmp)+e);
          free_svalue(ITEM(tmp)+e);
          ITEM(tmp)[e]=sval;
        }
        stack_swap();
        pop_stack();
        return;
      }
    
      push_array(a=allocate_array(tmp->size));
      
      for(e=0;e<a->size;e++)
        index_no_free(ITEM(a)+e, val, ITEM(tmp)+e);
      
      sp--;
      pop_n_elems(args);
      push_array(a);
    }
    
    void f_column(INT32 args)
    {
      INT32 e;
      struct array *a,*tmp;
      struct svalue *val;
    
      DECLARE_CYCLIC();
    
      get_all_args("column", args, "%a%*", &tmp, &val);
    
      /* Optimization */
      if(tmp->refs == 1)
      {
        /* An array with one ref cannot possibly be cyclic */
        struct svalue sval;
        tmp->type_field = BIT_MIXED | BIT_UNFINISHED;
        for(e=0;e<tmp->size;e++)
        {
          index_no_free(&sval, ITEM(tmp)+e, val);
          free_svalue(ITEM(tmp)+e);
          ITEM(tmp)[e]=sval;
        }
        pop_stack();
        return;
      }
    
      if((a=(struct array *)BEGIN_CYCLIC(tmp,0)))
      {
        pop_n_elems(args);
        ref_push_array(a);
      }else{
        push_array(a=allocate_array(tmp->size));
        SET_CYCLIC_RET(a);
    
        for(e=0;e<a->size;e++)
          index_no_free(ITEM(a)+e, ITEM(tmp)+e, val);
    
        END_CYCLIC();
        sp--;
        pop_n_elems(args);
        push_array(a);
      }
    }
    
    #ifdef PIKE_DEBUG
    void f__verify_internals(INT32 args)
    {
      INT32 tmp=d_flag;
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY,
    			  ("_verify_internals: permission denied.\n"));
      d_flag=0x7fffffff;
      do_debug();
      d_flag=tmp;
      do_gc();
      pop_n_elems(args);
    }
    
    void f__debug(INT32 args)
    {
      INT_TYPE d;
    
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY,
    			  ("_debug: permission denied.\n"));
    
      get_all_args("_debug", args, "%i", &d);
      pop_n_elems(args);
      push_int(d_flag);
      d_flag = d;
    }
    
    #ifdef YYDEBUG
    
    void f__compiler_trace(INT32 args)
    {
      extern int yydebug;
      INT_TYPE yyd;
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY,
    			  ("_compiler_trace: permission denied.\n"));
      get_all_args("_compiler_trace", args, "%i", &yyd);
      pop_n_elems(args);
      push_int(yydebug);
      yydebug = yyd;
    }
    
    #endif /* YYDEBUG */
    #endif
    
    #if defined(HAVE_LOCALTIME) || defined(HAVE_GMTIME)
    static void encode_struct_tm(struct tm *tm)
    {
      push_string(make_shared_string("sec"));
      push_int(tm->tm_sec);
      push_string(make_shared_string("min"));
      push_int(tm->tm_min);
      push_string(make_shared_string("hour"));
      push_int(tm->tm_hour);
    
      push_string(make_shared_string("mday"));
      push_int(tm->tm_mday);
      push_string(make_shared_string("mon"));
      push_int(tm->tm_mon);
      push_string(make_shared_string("year"));
      push_int(tm->tm_year);
    
      push_string(make_shared_string("wday"));
      push_int(tm->tm_wday);
      push_string(make_shared_string("yday"));
      push_int(tm->tm_yday);
      push_string(make_shared_string("isdst"));
      push_int(tm->tm_isdst);
    }
    #endif
    
    #ifdef HAVE_GMTIME
    void f_gmtime(INT32 args)
    {
      struct tm *tm;
      INT_TYPE tt;
      time_t t;
    
      get_all_args("gmtime", args, "%i", &tt);
    
      t = tt;
      tm = gmtime(&t);
      pop_n_elems(args);
      encode_struct_tm(tm);
    
      push_string(make_shared_string("timezone"));
      push_int(0);
      f_aggregate_mapping(20);
    }
    #endif
    
    #ifdef HAVE_LOCALTIME
    void f_localtime(INT32 args)
    {
      struct tm *tm;
      INT_TYPE tt;
      time_t t;
    
      get_all_args("localtime", args, "%i", &tt);
    
      t = tt;
      tm = localtime(&t);
      pop_n_elems(args);
      encode_struct_tm(tm);
    
    #ifdef HAVE_EXTERNAL_TIMEZONE
      push_string(make_shared_string("timezone"));
      push_int(timezone);
      f_aggregate_mapping(20);
    #else
    #ifdef STRUCT_TM_HAS_GMTOFF
      push_string(make_shared_string("timezone"));
      push_int(tm->tm_gmtoff);
      f_aggregate_mapping(20);
    #else
      f_aggregate_mapping(18);
    #endif
    #endif
    }
    #endif
    
    #ifdef HAVE_MKTIME
    static void f_mktime (INT32 args)
    {
      INT_TYPE sec, min, hour, mday, mon, year, isdst;
      struct tm date;
      struct svalue s;
      struct svalue * r;
      int retval;
      if (args<1)
        SIMPLE_TOO_FEW_ARGS_ERROR("mktime", 1);
    
      if(args == 1)
      {
        MEMSET(&date, 0, sizeof(date));
    
        push_text("sec");
        push_text("min");
        push_text("hour");
        push_text("mday");
        push_text("mon");
        push_text("year");
        push_text("isdst");
        push_text("timezone");
        f_aggregate(8);
        f_rows(2);
        sp--;
        push_array_items(sp->u.array);
    
        args=8;
      }
    
      get_all_args("mktime",args, "%i%i%i%i%i%i",
    	       &sec, &min, &hour, &mday, &mon, &year);
    
      MEMSET(&date, 0, sizeof(date));
      date.tm_sec=sec;
      date.tm_min=min;
      date.tm_hour=hour;
      date.tm_mday=mday;
      date.tm_mon=mon;
      date.tm_year=year;
      if(sp[6-args].subtype == NUMBER_NUMBER)
      {
        date.tm_isdst = sp[6-args].u.integer;
      }else{
        date.tm_isdst = -1;
      }
    
    #if STRUCT_TM_HAS_GMTOFF
      if(sp[7-args].subtype == NUMBER_NUMBER)
      {
        date.tm_gmtoff=sp[7-args].u.intger;
      }else{
        time_t tmp = 0;
        data.tm_gmtoff=localtime(&tmp).tm_gmtoff;
      }
      retval=mktime(&date);
    #else
    #ifdef HAVE_EXTERNAL_TIMEZONE
      if(sp[7-args].subtype == NUMBER_NUMBER)
      {
        retval=mktime(&date) + sp[7-args].u.integer - timezone;
      }else{
        retval=mktime(&date);
      }
    #else
      retval=mktime(&date);
    #endif
    #endif
    
      if (retval == -1)
        PIKE_ERROR("mktime", "Cannot convert.\n", sp, args);
       pop_n_elems(args);
       push_int(retval);
    }
    
    #endif
    
    
    /* Check if the string s[0..len[ matches the glob m[0..mlen[ */
    static int does_match(struct pike_string *s,int j,
    		      struct pike_string *m,int i)
    {
      for (; i<m->len; i++)
      {
        switch (index_shared_string(m,i))
        {
         case '?':
           if(j++>=s->len) return 0;
           break;
    
         case '*': 
          i++;
          if (i==m->len) return 1;	/* slut */
    
          for (;j<s->len;j++)
    	if (does_match(s,j,m,i))
    	  return 1;
    
          return 0;
    
         default: 
           if(j>=s->len ||
    	  index_shared_string(m,i)!=index_shared_string(s,j)) return 0;
           j++;
        }
      }
      return j==s->len;
    }
    
    void f_glob(INT32 args)
    {
      INT32 i,matches;
      struct array *a;
      struct svalue *sval, tmp;
      struct pike_string *glob;
    
      if(args < 2)
        SIMPLE_TOO_FEW_ARGS_ERROR("glob", 2);
    
      if(args > 2)
        pop_n_elems(args-2);
      args=2;
    
      if (sp[-args].type!=T_STRING)
        SIMPLE_BAD_ARG_ERROR("glob", 1, "string");
    
      glob=sp[-args].u.string;
    
      switch(sp[1-args].type)
      {
      case T_STRING:
        i=does_match(sp[1-args].u.string,0,glob,0);
        pop_n_elems(2);
        push_int(i);
        break;
        
      case T_ARRAY:
        a=sp[1-args].u.array;
        matches=0;
        for(i=0;i<a->size;i++)
        {
          if(ITEM(a)[i].type != T_STRING)
    	SIMPLE_BAD_ARG_ERROR("glob", 2, "string|array(string)");
    
          if(does_match(ITEM(a)[i].u.string,0,glob,0))
          {
    	add_ref(ITEM(a)[i].u.string);
    	push_string(ITEM(a)[i].u.string);
    	matches++;
          }
        }
        f_aggregate(matches);
        tmp=sp[-1];
        sp--;
        pop_n_elems(2);
        sp[0]=tmp;
        sp++;
        break;
    
      default:
        SIMPLE_BAD_ARG_ERROR("glob", 1, "string|array(string)");
      }
    }
    
    /* comb_merge */
    
    /* mixed interleave_array(array(mapping(int:mixed)) tab) */
    static void f_interleave_array(INT32 args)
    {
      struct array *arr = NULL;
      struct array *min = NULL;
      struct array *order = NULL;
      int max = 0;
      int ok;
      int nelems = 0;
      int i;
    
      get_all_args("interleave_array", args, "%a", &arr);
    
      /* We're not interrested in any other arguments. */
      pop_n_elems(args-1);
    
      if ((ok = arr->type_field & BIT_MAPPING) &&
          (arr->type_field & ~BIT_MAPPING)) {
        /* Might be ok, but do some more checking... */
        for(i = 0; i < arr->size; i++) {
          if (ITEM(arr)[i].type != T_MAPPING) {
    	ok = 0;
    	break;
          }
        }
      }
      if (!ok) {
        SIMPLE_BAD_ARG_ERROR("interleave_array", 1, "array(mapping(int:mixed))");
      }
    
      /* The order array */
      ref_push_array(arr);
      f_indices(1);
      order = sp[-1].u.array;
    
      /* The min array */
      push_array(min = allocate_array(arr->size));
    
      /* Initialize the min array */
      for (i = 0; i < arr->size; i++) {
        struct mapping *m;
        /* e and k are used by MAPPING_LOOP() */
        INT32 e;
        struct keypair *k;
        INT_TYPE low = 0x7fffffff;
    #ifdef PIKE_DEBUG
        if (ITEM(arr)[i].type != T_MAPPING) {
          error("interleave_array(): Element %d is not a mapping!\n", i);
        }
    #endif /* PIKE_DEBUG */
        m = ITEM(arr)[i].u.mapping;
        MAPPING_LOOP(m) {
          if (k->ind.type != T_INT) {
    	error("interleave_array(): Index not an integer in mapping %d!\n", i);
          }
          if (low > k->ind.u.integer) {
    	low = k->ind.u.integer;
    	if (low < 0) {
    	  error("interleave_array(): Index %d in mapping %d is negative!\n",
    		low, i);
    	}
          }
          if (max < k->ind.u.integer) {
    	max = k->ind.u.integer;
          }
          nelems++;
        }
        /* FIXME: Is this needed? Isn't T_INT default? */
        ITEM(min)[i].u.integer = low;
      }
    
      ref_push_array(order);
      f_sort(2);	/* Sort the order array on the minimum index */
    
      /* State on stack now:
       *
       * array(mapping(int:mixed))	arr
       * array(int)			order
       * array(int)			min (now sorted)
       */
    
      /* Now we can start with the real work... */
      {
        char *tab;
        int size;
        int minfree = 0;
    
        /* Initialize the lookup table */
        max += 1;
        max *= 2;
        /* max will be the padding at the end. */
        size = (nelems + max) * 8;	/* Initial size */
        if (!(tab = malloc(size + max))) {
          SIMPLE_OUT_OF_MEMORY_ERROR("interleave_array", size+max);
        }
        MEMSET(tab, 0, size + max);
    
        for (i = 0; i < order->size; i++) {
          int low = ITEM(min)[i].u.integer;
          int j = ITEM(order)[i].u.integer;
          int offset = 0;
          struct mapping *m;
          INT32 e;
          struct keypair *k;
    
          if (!(m = ITEM(arr)[j].u.mapping)->size) {
    	/* Not available */
    	ITEM(min)[i].u.integer = -1;
    	continue;
          }
    
          if (low < minfree) {
    	offset = minfree - low;
          } else {
    	minfree = offset;
          }
    
          ok = 0;
          while (!ok) {
    	ok = 1;
    	MAPPING_LOOP(m) {
    	  int ind = k->ind.u.integer;
    	  if (tab[offset + ind]) {
    	    ok = 0;
    	    while (tab[++offset + ind])
    	      ;
    	  }
    	}
          }
          MAPPING_LOOP(m) {
    	tab[offset + k->ind.u.integer] = 1;
          }
          while(tab[minfree]) {
    	minfree++;
          }
          ITEM(min)[i].u.integer = offset;
    
          /* Check need for realloc */
          if (offset >= size) {
    	char *newtab = realloc(tab, size*2 + max);
    	if (!newtab) {
    	  free(tab);
    	  error("interleave_array(): Couldn't extend table!\n");
    	}
    	tab = newtab;
    	MEMSET(tab + size + max, 0, size);
    	size = size * 2;
          }
        }
        free(tab);
      }
    
      /* We want these two to survive the stackpopping. */
      add_ref(min);
      add_ref(order);
    
      pop_n_elems(3);
    
      /* Return value */
      ref_push_array(min);
    
      /* Restore the order */
      push_array(order);
      push_array(min);
      f_sort(2);
      pop_stack();
    }
    
    /* longest_ordered_sequence */
    
    static int find_gt(struct array *a, int i, int *stack, int top)
    {
      struct svalue *x = a->item + i;
      int l,h;
    
      if (!top || !is_lt(x, a->item + stack[top - 1])) return top;
    
      l = 0;
      h = top;
    
      while (l < h) {
        int middle = (l + h)/2;
        if (!is_gt(a->item + stack[middle], x)) {
          l = middle+1;
        } else {
          h = middle;
        }
      }
      return l;
    }
    
    static struct array *longest_ordered_sequence(struct array *a)
    {
      int *stack;
      int *links;
      int i,j,top=0,l=0,ltop=-1;
      struct array *res;
      ONERROR tmp;
      ONERROR tmp2;
    
      if(!a->size)
        return allocate_array(0);
    
      stack = malloc(sizeof(int)*a->size);
      links = malloc(sizeof(int)*a->size);
    
      if (!stack || !links)
      {
        if (stack) free(stack);
        if (links) free(links);
        return 0;
      }
    
      /* is_gt(), is_lt() and low_allocate_array() can generate errors. */
    
      SET_ONERROR(tmp, free, stack);
      SET_ONERROR(tmp2, free, links);
    
      for (i=0; i<a->size; i++) {
        int pos;
    
        pos = find_gt(a, i, stack, top);
    
        if (pos == top) {
          top++;
          ltop = i;
        }
        if (pos != 0)
          links[i] = stack[pos-1];
        else
          links[i] = -1;
        stack[pos] = i;
      }
    
      /* FIXME(?) memory unfreed upon error here */
      res = low_allocate_array(top, 0); 
      while (ltop != -1)
      {
        res->item[--top].u.integer = ltop;
        ltop = links[ltop];
      }
    
      UNSET_ONERROR(tmp2);
      UNSET_ONERROR(tmp);
    
      free(stack);
      free(links);
      return res;
    }
    
    static void f_longest_ordered_sequence(INT32 args)
    {
      struct array *a = NULL;
    
      get_all_args("Array.longest_ordered_sequence", args, "%a", &a);
    
      /* THREADS_ALLOW(); */
    
      a = longest_ordered_sequence(a);
    
      /* THREADS_DISALLOW(); */
    
      if (!a) {
        SIMPLE_OUT_OF_MEMORY_ERROR("Array.longest_ordered_sequence",
    			       (int)sizeof(int *)*a->size*2);
      }
    
      pop_n_elems(args);
      push_array(a);
    }
    
    /**** diff ************************************************************/
    
    static struct array* diff_compare_table(struct array *a,struct array *b,int *u)
    {
       struct array *res;
       struct mapping *map;
       struct svalue *pval;
       int i;
    
       if (u) {
         *u = 0;	/* Unique rows in array b */
       }
    
       map=allocate_mapping(256);
       push_mapping(map); /* in case of out of memory */
    
       for (i=0; i<b->size; i++)
       {
          pval=low_mapping_lookup(map,b->item+i);
          if (!pval)
          {
    	 struct svalue val;
    	 val.type=T_ARRAY;
    	 val.u.array=low_allocate_array(1,1);
    	 val.u.array->item[0].type=T_INT;
    	 val.u.array->item[0].subtype=NUMBER_NUMBER;
    	 val.u.array->item[0].u.integer=i;
    	 mapping_insert(map,b->item+i,&val);
    	 free_svalue(&val);
    	 if (u) {
    	   (*u)++;
    	 }
          }
          else
          {
    	 pval->u.array=resize_array(pval->u.array,pval->u.array->size+1);
    	 pval->u.array->item[pval->u.array->size-1].type=T_INT;
    	 pval->u.array->item[pval->u.array->size-1].subtype=NUMBER_NUMBER;
    	 pval->u.array->item[pval->u.array->size-1].u.integer=i;
          }
       }
    
       res=low_allocate_array(a->size,0);
    
       for (i=0; i<a->size; i++)
       {
          pval=low_mapping_lookup(map,a->item+i);
          if (!pval)
          {
    	 res->item[i].type=T_ARRAY;
    	 add_ref(res->item[i].u.array=&empty_array);
          }
          else
          {
    	 assign_svalue(res->item+i,pval);
          }
       }
    
       pop_stack();
       return res;
    }
    
    struct diff_magic_link
    { 
       int x;
       int refs;
       struct diff_magic_link *prev;
    };
    
    struct diff_magic_link_pool
    {
       struct diff_magic_link *firstfree;
       struct diff_magic_link_pool *next;
       int firstfreenum;
       struct diff_magic_link dml[1];
    };
    
    struct diff_magic_link_head
    {
      unsigned int depth;
      struct diff_magic_link *link;
    };
    
    #define DMLPOOLSIZE 16384
    
    static int dmls=0;
    
    static INLINE struct diff_magic_link_pool*
             dml_new_pool(struct diff_magic_link_pool **pools)
    {
       struct diff_magic_link_pool *new;
    
       new=malloc(sizeof(struct diff_magic_link_pool)+
    	      sizeof(struct diff_magic_link)*DMLPOOLSIZE);
       if (!new) return NULL; /* fail */
    
       new->firstfreenum=0;
       new->firstfree=NULL;
       new->next=*pools;
       *pools=new;
       return *pools;
    }
    
    static INLINE struct diff_magic_link* 
           dml_new(struct diff_magic_link_pool **pools)
    {
       struct diff_magic_link *new;
       struct diff_magic_link_pool *pool;
    
       dmls++;
    
       if ( *pools && (new=(*pools)->firstfree) )
       {
          (*pools)->firstfree=new->prev;
          new->prev=NULL;
          return new;
       }
    
       pool=*pools;
       while (pool)
       {
          if (pool->firstfreenum<DMLPOOLSIZE)
    	 return pool->dml+(pool->firstfreenum++);
          pool=pool->next;
       }
    
       if ( (pool=dml_new_pool(pools)) )
       {
          pool->firstfreenum=1;
          return pool->dml;
       }
       
       return NULL;
    }	
    
    static INLINE void dml_free_pools(struct diff_magic_link_pool *pools)
    {
       struct diff_magic_link_pool *pool;
    
       while (pools)
       {
          pool=pools->next;
          free(pools);
          pools=pool;
       }
    }
    
    static INLINE void dml_delete(struct diff_magic_link_pool *pools,
    			      struct diff_magic_link *dml)
    {
       if (dml->prev && !--dml->prev->refs) dml_delete(pools,dml->prev);
       dmls--;
       dml->prev=pools->firstfree;
       pools->firstfree=dml;
    }
    
    static INLINE int diff_ponder_stack(int x,
    				    struct diff_magic_link **dml,
    				    int top)
    {
       int middle,a,b;
       
       a=0; 
       b=top;
       while (b>a)
       {
          middle=(a+b)/2;
          if (dml[middle]->x<x) a=middle+1;
          else if (dml[middle]->x>x) b=middle;
          else return middle;
       }
       if (a<top && dml[a]->x<x) a++;
       return a;
    }
    
    static INLINE int diff_ponder_array(int x,
    				    struct svalue *arr,
    				    int top)
    {
       int middle,a,b;
       
       a=0; 
       b=top;
       while (b>a)
       {
          middle=(a+b)/2;
          if (arr[middle].u.integer<x) a=middle+1;
          else if (arr[middle].u.integer>x) b=middle;
          else return middle;
       }
       if (a<top && arr[a].u.integer<x) a++;
       return a;
    }
    
    /*
     * The Grubba-Mirar Longest Common Sequence algorithm.
     *
     * This algorithm is O((Na * Nb / K)*lg(Na * Nb / K)), where:
     *
     *  Na == sizeof(a)
     *  Nb == sizeof(b)
     *  K  == sizeof(correlation(a,b))
     *
     * For binary data:
     *  K == 256 => O(Na * Nb * lg(Na * Nb)),
     *  Na ~= Nb ~= N => O(N * lg(N))
     *
     * For ascii data:
     *  K ~= C * min(Na, Nb), C constant => O(max(Na, Nb)*lg(max(Na,Nb))),
     *  Na ~= Nb ~= N => O(N * lg(N))
     *
     * diff_longest_sequence() takes two arguments:
     *  cmptbl == diff_compare_table(a, b)
     *  blen == sizeof(b) >= max(@(cmptbl*({})))
     */
    static struct array *diff_longest_sequence(struct array *cmptbl, int blen)
    {
       int i,j,top=0,lsize=0;
       struct array *a;
       struct diff_magic_link_pool *pools=NULL;
       struct diff_magic_link *dml;
       struct diff_magic_link **stack;
       char *marks;
    
       if(!cmptbl->size)
         return allocate_array(0);
    
       stack = malloc(sizeof(struct diff_magic_link*)*cmptbl->size);
    
       if (!stack) {
         int args = 0;
         SIMPLE_OUT_OF_MEMORY_ERROR("diff_longest_sequence",
    				(int)sizeof(struct diff_magic_link*) *
    				cmptbl->size);
       }
    
       /* NB: marks is used for optimization purposes only */
       marks = calloc(blen, 1);
    
       if (!marks && blen) {
         int args = 0;
         free(stack);
         SIMPLE_OUT_OF_MEMORY_ERROR("diff_longest_sequence", blen);
       }
    
    #ifdef DIFF_DEBUG
       fprintf(stderr, "\n\nDIFF: sizeof(cmptbl)=%d, blen=%d\n",
    	   cmptbl->size, blen);
    #endif /* DIFF_DEBUG */
    
       for (i = 0; i<cmptbl->size; i++)
       {
          struct svalue *inner=cmptbl->item[i].u.array->item;
    
    #ifdef DIFF_DEBUG
          fprintf(stderr, "DIFF: i=%d\n", i);
    #endif /* DIFF_DEBUG */
    
          for (j = cmptbl->item[i].u.array->size; j--;)
          {
    	 int x = inner[j].u.integer;
    
    #ifdef DIFF_DEBUG
    	 fprintf(stderr, "DIFF:  j=%d, x=%d\n", j, x);
    #endif /* DIFF_DEBUG */
    #ifdef PIKE_DEBUG
    	 if (x >= blen) {
    	   fatal("diff_longest_sequence(): x:%d >= blen:%d\n", x, blen);
    	 } else if (x < 0) {
    	   fatal("diff_longest_sequence(): x:%d < 0\n", x);
    	 }
    #endif /* PIKE_DEBUG */
    	 if (!marks[x]) {
    	   int pos;
    
    	   if (top && x<=stack[top-1]->x) {
    	     /* Find the insertion point. */
    	     pos = diff_ponder_stack(x, stack, top);
    	     if (pos != top) {
    	       /* Not on the stack anymore. */
    	       marks[stack[pos]->x] = 0;
    	     }
    	   } else
    	     pos=top;
    
    #ifdef DIFF_DEBUG
    	   fprintf(stderr, "DIFF:  pos=%d\n", pos);
    #endif /* DIFF_DEBUG */
    
    	   /* This part is only optimization (j accelleration). */
    	   if (pos && j)
    	   {
    	     if (!marks[inner[j-1].u.integer])
    	     {
    	       /* Find the element to insert. */
    	       j = diff_ponder_array(stack[pos-1]->x+1, inner, j);
    	       x = inner[j].u.integer;
    	     }
    	   }
    	   else
    	   {
    	     j = 0;
    	     x = inner->u.integer;
    	   }
    
    #ifdef DIFF_DEBUG
    	   fprintf(stderr, "DIFF: New j=%d, x=%d\n", j, x);
    #endif /* DIFF_DEBUG */
    #ifdef PIKE_DEBUG
    	   if (x >= blen) {
    	     fatal("diff_longest_sequence(): x:%d >= blen:%d\n", x, blen);
    	   } else if (x < 0) {
    	     fatal("diff_longest_sequence(): x:%d < 0\n", x);
    	   }
    #endif /* PIKE_DEBUG */
    
    	   /* Put x on the stack. */
    	   marks[x] = 1;
    	   if (pos == top)
    	   {
    #ifdef DIFF_DEBUG
    	     fprintf(stderr, "DIFF:  New top element\n");
    #endif /* DIFF_DEBUG */
    
    	     if (! (dml=dml_new(&pools)) )
    	     {
    	       int args = 0;
    	       dml_free_pools(pools);
    	       free(stack);
    	       SIMPLE_OUT_OF_MEMORY_ERROR("diff_longest_sequence",
    					  sizeof(struct diff_magic_link_pool) +
    					  sizeof(struct diff_magic_link) *
    					  DMLPOOLSIZE);
    	     }
    
    	     dml->x = x;
    	     dml->refs = 1;
    
    	     if (pos)
    	       (dml->prev = stack[pos-1])->refs++;
    	     else
    	       dml->prev = NULL;
    
    	     top++;
    	    
    	     stack[pos] = dml;
    	   } else if (pos && 
    		      stack[pos]->refs == 1 &&
    		      stack[pos-1] == stack[pos]->prev)
    	   {
    #ifdef DIFF_DEBUG
    	     fprintf(stderr, "DIFF:  Optimized case\n");
    #endif /* DIFF_DEBUG */
    
    	     /* Optimization. */
    	     stack[pos]->x = x;
    	   } else {
    #ifdef DIFF_DEBUG
    	     fprintf(stderr, "DIFF:  Generic case\n");
    #endif /* DIFF_DEBUG */
    
    	     if (! (dml=dml_new(&pools)) )
    	     {
    	       int args = 0;
    	       dml_free_pools(pools);
    	       free(stack);
    	       SIMPLE_OUT_OF_MEMORY_ERROR("diff_longest_sequence",
    					  sizeof(struct diff_magic_link_pool) +
    					  sizeof(struct diff_magic_link) *
    					  DMLPOOLSIZE);
    	     }
    
    	     dml->x = x;
    	     dml->refs = 1;
    
    	     if (pos)
    	       (dml->prev = stack[pos-1])->refs++;
    	     else
    	       dml->prev = NULL;
    
    	     if (!--stack[pos]->refs)
    	       dml_delete(pools, stack[pos]);
    	    
    	     stack[pos] = dml;
    	   }
    #ifdef DIFF_DEBUG
    	 } else {
    	   fprintf(stderr, "DIFF:  Already marked (%d)!\n", marks[x]);
    #endif /* DIFF_DEBUG */
    	 }
          }
    #ifdef DIFF_DEBUG
          for(j=0; j < top; j++) {
    	fprintf(stderr, "DIFF:  stack:%d, mark:%d\n",
    		stack[j]->x, marks[stack[j]->x]);
          }
    #endif /* DIFF_DEBUG */
       }
    
       /* No need for marks anymore. */
    
       free(marks);
    
       /* FIXME(?) memory unfreed upon error here. */
       a=low_allocate_array(top,0); 
       if (top)
       {
           dml=stack[top-1];
           while (dml)
           {
    	  a->item[--top].u.integer=dml->x;
    	  dml=dml->prev;
           }
       }
    
       free(stack);
       dml_free_pools(pools);
       return a;
    }
    
    /*
     * The dynamic programming Longest Common Sequence algorithm.
     *
     * This algorithm is O(Na * Nb), where:
     *
     *  Na == sizeof(a)
     *  Nb == sizeof(b)
     *
     * This makes it faster than the G-M algorithm on binary data,
     * but slower on ascii data.
     *
     * NOT true! The G-M algorithm seems to be faster on most data anyway.
     *	/grubba 1998-05-19
     */
    static struct array *diff_dyn_longest_sequence(struct array *cmptbl, int blen)
    {
      struct array *res = NULL;
      struct diff_magic_link_head *table = NULL;
      struct diff_magic_link_pool *dml_pool = NULL;
      struct diff_magic_link *dml;
      unsigned int sz = (unsigned int)cmptbl->size;
      unsigned int i;
      unsigned int off1 = 0;
      unsigned int off2 = blen + 1;
      unsigned int l1 = 0;
      unsigned int l2 = 0;
    
      table = calloc(sizeof(struct diff_magic_link_head)*2, off2);
      if (!table) {
        int args = 0;
        SIMPLE_OUT_OF_MEMORY_ERROR("diff_dyn_longest_sequence",
    			       sizeof(struct diff_magic_link_head) * 2 * off2);
      }
    
      /* FIXME: Assumes NULL is represented with all zeroes */
      /* NOTE: Scan strings backwards to get the same result as the G-M
       * algorithm.
       */
      for (i = sz; i--;) {
        struct array *boff = cmptbl->item[i].u.array;
    
    #ifdef DIFF_DEBUG
        fprintf(stderr, "  i:%d\n", i);
    #endif /* DIFF_DEBUG */
    
        if (boff->size) {
          unsigned int bi;
          unsigned int base = blen;
          unsigned int tmp = off1;
          off1 = off2;
          off2 = tmp;
    
          for (bi = boff->size; bi--;) {
    	unsigned int ib = boff->item[bi].u.integer;
    
    #ifdef DIFF_DEBUG
    	fprintf(stderr, "    Range [%d - %d] differ\n", base - 1, ib + 1);
    #endif /* DIFF_DEBUG */
    	while ((--base) > ib) {
    	  /* Differ */
    	  if (table[off1 + base].link) {
    	    if (!--(table[off1 + base].link->refs)) {
    	      dml_delete(dml_pool, table[off1 + base].link);
    	    }
    	  }
    	  /* FIXME: Should it be > or >= here to get the same result
    	   * as with the G-M algorithm?
    	   */
    	  if (table[off2 + base].depth > table[off1 + base + 1].depth) {
    	    table[off1 + base].depth = table[off2 + base].depth;
    	    dml = (table[off1 + base].link = table[off2 + base].link);
    	  } else {
    	    table[off1 + base].depth = table[off1 + base + 1].depth;
    	    dml = (table[off1 + base].link = table[off1 + base + 1].link);
    	  }
    	  if (dml) {
    	    dml->refs++;
    	  }
    	}
    	/* Equal */
    #ifdef DIFF_DEBUG
    	fprintf(stderr, "    Equal\n");
    #endif /* DIFF_DEBUG */
    
    	if (table[off1 + ib].link) {
    	  if (!--(table[off1 + ib].link->refs)) {
    	    dml_delete(dml_pool, table[off1 + ib].link);
    	  }
    	}
    	table[off1 + ib].depth = table[off2 + ib + 1].depth + 1;
    	dml = (table[off1 + ib].link = dml_new(&dml_pool));
    	if (!dml) {
    	  int args = 0;
    	  dml_free_pools(dml_pool);
    	  free(table);
    	  SIMPLE_OUT_OF_MEMORY_ERROR("diff_dyn_longest_sequence",
    				     sizeof(struct diff_magic_link_pool) +
    				     sizeof(struct diff_magic_link) *
    				     DMLPOOLSIZE);
    	}
    	dml->refs = 1;
    	dml->prev = table[off2 + ib + 1].link;
    	if (dml->prev) {
    	  dml->prev->refs++;
    	}
    	dml->x = ib;
          }
    #ifdef DIFF_DEBUG
          fprintf(stderr, "    Range [0 - %d] differ\n", base-1);
    #endif /* DIFF_DEBUG */
          while (base--) {
    	/* Differ */
    	if (table[off1 + base].link) {
    	  if (!--(table[off1 + base].link->refs)) {
    	    dml_delete(dml_pool, table[off1 + base].link);
    	  }
    	}
    	/* FIXME: Should it be > or >= here to get the same result
    	 * as with the G-M algorithm?
    	 */
    	if (table[off2 + base].depth > table[off1 + base + 1].depth) {
    	  table[off1 + base].depth = table[off2 + base].depth;
    	  dml = (table[off1 + base].link = table[off2 + base].link);
    	} else {
    	  table[off1 + base].depth = table[off1 + base + 1].depth;
    	  dml = (table[off1 + base].link = table[off1 + base + 1].link);
    	}
    	if (dml) {
    	  dml->refs++;
    	}
          }
        }
      }
    
      /* Convert table into res */
      sz = table[off1].depth;
      dml = table[off1].link;
      free(table);
    #ifdef DIFF_DEBUG
      fprintf(stderr, "Result array size:%d\n", sz);
    #endif /* DIFF_DEBUG */
    
      res = allocate_array(sz);
      if (!res) {
        int args = 0;
        if (dml_pool) {
          dml_free_pools(dml_pool);
        }
        SIMPLE_OUT_OF_MEMORY_ERROR("diff_dyn_longest_sequence",
    			       sizeof(struct array) +
    			       sz*sizeof(struct svalue));
      }
    
      i = 0;
      while(dml) {
    #ifdef PIKE_DEBUG
        if (i >= sz) {
          fatal("Consistency error in diff_dyn_longest_sequence()\n");
        }
    #endif /* PIKE_DEBUG */
    #ifdef DIFF_DEBUG
        fprintf(stderr, "  %02d: %d\n", i, dml->x);
    #endif /* DIFF_DEBUG */
        res->item[i].type = T_INT;
        res->item[i].subtype = 0;
        res->item[i].u.integer = dml->x;
        dml = dml->prev;
        i++;
      }
    #ifdef PIKE_DEBUG
      if (i != sz) {
        fatal("Consistency error in diff_dyn_longest_sequence()\n");
      }
    #endif /* PIKE_DEBUG */
    
      dml_free_pools(dml_pool);
      return(res);
    }
    
    static struct array* diff_build(struct array *a,
    				struct array *b,
    				struct array *seq)
    {
       struct array *ad,*bd;
       int bi,ai,lbi,lai,i,eqstart;
    
       /* FIXME(?) memory unfreed upon error here (and later) */
       ad=low_allocate_array(0,32);
       bd=low_allocate_array(0,32);
       
       eqstart=0;
       lbi=bi=ai=-1;
       for (i=0; i<seq->size; i++)
       {
          bi=seq->item[i].u.integer;
    
          if (bi!=lbi+1 || !is_equal(a->item+ai+1,b->item+bi))
          {
    	 /* insert the equality */
    	 if (lbi>=eqstart)
    	 {
    	    push_array(friendly_slice_array(b,eqstart,lbi+1));
    	    ad=append_array(ad,sp-1);
    	    bd=append_array(bd,sp-1);
    	    pop_stack();
    	 }
    	 /* insert the difference */
    	 lai=ai;
    	 ai=array_search(a,b->item+bi,ai+1)-1;
    
    	 push_array(friendly_slice_array(b,lbi+1,bi));
    	 bd=append_array(bd, sp-1);
    	 pop_stack();
    
    	 push_array(friendly_slice_array(a,lai+1,ai+1));
    	 ad=append_array(ad,sp-1);
    	 pop_stack();
    
    	 eqstart=bi;
          }
          ai++;
          lbi=bi;
       }
    
       if (lbi>=eqstart)
       {
          push_array(friendly_slice_array(b,eqstart,lbi+1));
          ad=append_array(ad,sp-1);
          bd=append_array(bd,sp-1);
          pop_stack();
       }
    
       if (b->size>bi+1 || a->size>ai+1)
       {
          push_array(friendly_slice_array(b,lbi+1,b->size));
          bd=append_array(bd, sp-1);
          pop_stack();
          
          push_array(friendly_slice_array(a,ai+1,a->size));
          ad=append_array(ad,sp-1);
          pop_stack();
       }
    
       push_array(ad);
       push_array(bd);
       return aggregate_array(2);
    }
    
    void f_diff(INT32 args)
    {
       struct array *seq;
       struct array *cmptbl;
       struct array *diff;
       int uniq;
    
       /* FIXME: Ought to use get_all_args() */
    
       if (args<2)
          SIMPLE_TOO_FEW_ARGS_ERROR("diff", 2);
    
       if (sp[-args].type != T_ARRAY)
         SIMPLE_BAD_ARG_ERROR("diff", 1, "array");
       if (sp[1-args].type != T_ARRAY)
         SIMPLE_BAD_ARG_ERROR("diff", 2, "array");
    
       cmptbl = diff_compare_table(sp[-args].u.array, sp[1-args].u.array, &uniq);
    
       push_array(cmptbl);
    #ifdef ENABLE_DYN_DIFF
       if (uniq * 100 > sp[1-args].u.array->size) {
    #endif /* ENABLE_DYN_DIFF */
    #ifdef DIFF_DEBUG
         fprintf(stderr, "diff: Using G-M algorithm, u:%d, s:%d\n",
    	     uniq, sp[1-args].u.array->size);
    #endif /* DIFF_DEBUG */
         seq = diff_longest_sequence(cmptbl, sp[1-1-args].u.array->size);
    #ifdef ENABLE_DYN_DIFF
       } else {
    #ifdef DIFF_DEBUG
         fprintf(stderr, "diff: Using dyn algorithm, u:%d, s:%d\n",
    	     uniq, sp[1-args].u.array->size);
    #endif /* DIFF_DEBUG */
         seq = diff_dyn_longest_sequence(cmptbl, sp[1-1-args].u.array->size);
       }     
    #endif /* ENABLE_DYN_DIFF */
       push_array(seq);
       
       diff=diff_build(sp[-2-args].u.array,sp[1-2-args].u.array,seq);
    
       pop_n_elems(2+args);
       push_array(diff);
    }
    
    void f_diff_compare_table(INT32 args)
    {
      struct array *a;
      struct array *b;
      struct array *cmptbl;
    
      get_all_args("diff_compare_table", args, "%a%a", &a, &b);
    
      cmptbl = diff_compare_table(a, b, NULL);
    
      pop_n_elems(args);
      push_array(cmptbl);
    }
    
    void f_diff_longest_sequence(INT32 args)
    {
      struct array *a;
      struct array *b;
      struct array *seq;
      struct array *cmptbl;
    
      get_all_args("diff_longest_sequence", args, "%a%a", &a, &b);
    
      cmptbl = diff_compare_table(a, b, NULL);
    
      push_array(cmptbl);
    
      seq = diff_longest_sequence(cmptbl, b->size);
    
      pop_n_elems(args+1);
      push_array(seq); 
    }
    
    void f_diff_dyn_longest_sequence(INT32 args)
    {
      struct array *a;
      struct array *b;
      struct array *seq;
      struct array *cmptbl;
    
      get_all_args("diff_dyn_longest_sequence", args, "%a%a", &a, &b);
    
      cmptbl=diff_compare_table(a, b, NULL);
    
      push_array(cmptbl);
    
      seq = diff_dyn_longest_sequence(cmptbl, b->size);
    
      pop_n_elems(args+1);
      push_array(seq); 
    }
    
    /**********************************************************************/
    
    static struct callback_list memory_usage_callback;
    
    struct callback *add_memory_usage_callback(callback_func call,
    					  void *arg,
    					  callback_func free_func)
    {
      return add_to_callback(&memory_usage_callback, call, arg, free_func);
    }
    
    
    void f__memory_usage(INT32 args)
    {
      INT32 num,size;
      struct svalue *ss;
      pop_n_elems(args);
      ss=sp;
    
      count_memory_in_mappings(&num, &size);
      push_text("num_mappings");
      push_int(num);
      push_text("mapping_bytes");
      push_int(size);
    
      count_memory_in_strings(&num, &size);
      push_text("num_strings");
      push_int(num);
      push_text("string_bytes");
      push_int(size);
    
      count_memory_in_arrays(&num, &size);
      push_text("num_arrays");
      push_int(num);
      push_text("array_bytes");
      push_int(size);
    
      count_memory_in_programs(&num,&size);
      push_text("num_programs");
      push_int(num);
      push_text("program_bytes");
      push_int(size);
    
      count_memory_in_multisets(&num, &size);
      push_text("num_multisets");
      push_int(num);
      push_text("multiset_bytes");
      push_int(size);
    
      count_memory_in_objects(&num, &size);
      push_text("num_objects");
      push_int(num);
      push_text("object_bytes");
      push_int(size);
    
      count_memory_in_callbacks(&num, &size);
      push_text("num_callbacks");
      push_int(num);
      push_text("callback_bytes");
      push_int(size);
    
      count_memory_in_callables(&num, &size);
      push_text("num_callables");
      push_int(num);
      push_text("callable_bytes");
      push_int(size);
    
      count_memory_in_pike_frames(&num, &size);
      push_text("num_frames");
      push_int(num);
      push_text("frame_bytes");
      push_int(size);
    
      call_callback(&memory_usage_callback, (void *)0);
    
      f_aggregate_mapping(sp-ss);
    }
    
    void f__next(INT32 args)
    {
      struct svalue tmp;
    
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY, ("_next: permission denied.\n"));
    
      if(!args)
        SIMPLE_TOO_FEW_ARGS_ERROR("_next", 1);
      
      pop_n_elems(args-1);
      args = 1;
      tmp=sp[-1];
      switch(tmp.type)
      {
      case T_OBJECT:  tmp.u.object=tmp.u.object->next; break;
      case T_ARRAY:   tmp.u.array=tmp.u.array->next; break;
      case T_MAPPING: tmp.u.mapping=tmp.u.mapping->next; break;
      case T_MULTISET:tmp.u.multiset=tmp.u.multiset->next; break;
      case T_PROGRAM: tmp.u.program=tmp.u.program->next; break;
      case T_STRING:  tmp.u.string=tmp.u.string->next; break;
      default:
        SIMPLE_BAD_ARG_ERROR("_next", 1,
    			 "object|array|mapping|multiset|program|string");
      }
      if(tmp.u.refs)
      {
        assign_svalue(sp-1,&tmp);
      }else{
        pop_stack();
        push_int(0);
      }
    }
    
    void f__prev(INT32 args)
    {
      struct svalue tmp;
    
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY, ("_prev: permission denied.\n"));
    
      if(!args)
        SIMPLE_TOO_FEW_ARGS_ERROR("_prev", 1);
      
      pop_n_elems(args-1);
      args = 1;
      tmp=sp[-1];
      switch(tmp.type)
      {
      case T_OBJECT:  tmp.u.object=tmp.u.object->prev; break;
      case T_ARRAY:   tmp.u.array=tmp.u.array->prev; break;
      case T_MAPPING: tmp.u.mapping=tmp.u.mapping->prev; break;
      case T_MULTISET:tmp.u.multiset=tmp.u.multiset->prev; break;
      case T_PROGRAM: tmp.u.program=tmp.u.program->prev; break;
      default:
        SIMPLE_BAD_ARG_ERROR("_prev", 1, "object|array|mapping|multiset|program");
      }
      if(tmp.u.refs)
      {
        assign_svalue(sp-1,&tmp);
      }else{
        pop_stack();
        push_int(0);
      }
    }
    
    void f__refs(INT32 args)
    {
      INT32 i;
    
      if(!args)
        SIMPLE_TOO_FEW_ARGS_ERROR("_refs", 1);
    
      if(sp[-args].type > MAX_REF_TYPE)
        SIMPLE_BAD_ARG_ERROR("refs", 1,
    			 "array|mapping|multiset|object|"
    			 "function|program|string");
    
      i=sp[-args].u.refs[0];
      pop_n_elems(args);
      push_int(i);
    }
    
    void f__typeof(INT32 args)
    {
      INT32 i;
      struct pike_string *s,*t;
      if(!args)
        SIMPLE_TOO_FEW_ARGS_ERROR("_typeof", 1);
    
      low_init_threads_disable();
      s=get_type_of_svalue(sp-args);
      t=describe_type(s);
      exit_threads_disable(NULL);
    
      free_string(s);
      pop_n_elems(args);
      push_string(t);
    }
    
    void f_replace_master(INT32 args)
    {
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY,
    			  ("replace_master: permission denied.\n"));
    
      if(!args)
        SIMPLE_TOO_FEW_ARGS_ERROR("replace_master", 1);
      if(sp[-args].type != T_OBJECT)
        SIMPLE_BAD_ARG_ERROR("replace_master", 1, "object");
     if(!sp[-args].u.object->prog)
        bad_arg_error("replace_master", sp-args, args, 1, "object", sp-args,
    		  "Called with destructed object.\n");
        
      free_object(master_object);
      master_object=sp[-args].u.object;
      add_ref(master_object);
    
      free_program(master_program);
      master_program=master_object->prog;
      add_ref(master_program);
    
      pop_n_elems(args);
    }
    
    void f_master(INT32 args)
    {
      pop_n_elems(args);
      ref_push_object(master());
    }
    
    #ifdef HAVE_GETHRVTIME
    #include <sys/time.h>
    
    void f_gethrvtime(INT32 args)
    {
      pop_n_elems(args);
      push_int((INT32)(gethrvtime()/1000));
    }
    
    void f_gethrtime(INT32 args)
    {
      pop_n_elems(args);
      if(args)
        push_int((INT32)(gethrtime())); 
      else
        push_int((INT32)(gethrtime()/1000)); 
    }
    #else
    void f_gethrtime(INT32 args)
    {
      struct timeval tv;
      pop_n_elems(args);
      GETTIMEOFDAY(&tv);
      if(args)
        push_int((INT32)((tv.tv_sec *1000000) + tv.tv_usec)*1000);
      else
        push_int((INT32)((tv.tv_sec *1000000) + tv.tv_usec));
    }
    #endif /* HAVE_GETHRVTIME */
    
    #ifdef PROFILING
    static void f_get_prof_info(INT32 args)
    {
      struct program *prog = 0;
      int num_functions;
      int i;
    
      if (!args) {
        SIMPLE_TOO_FEW_ARGS_ERROR("get_profiling_info", 1);
      }
      prog = program_from_svalue(sp-args);
      if(!prog)
        SIMPLE_BAD_ARG_ERROR("get_profiling_info", 1, "program|function|object");
    
      add_ref(prog);
    
      pop_n_elems(args);
    
      /* ({ num_clones, ([ "fun_name":({ num_calls }) ]) }) */
    
      push_int(prog->num_clones);
    
      for(num_functions=i=0; i<(int)prog->num_identifiers; i++) {
        if (prog->identifiers[i].num_calls)
        {
          num_functions++;
          add_ref(prog->identifiers[i].name);
          push_string(prog->identifiers[i].name);
    
          push_int(prog->identifiers[i].num_calls);
          push_int(prog->identifiers[i].total_time);
          push_int(prog->identifiers[i].self_time);
          f_aggregate(3);
        }
      }
      f_aggregate_mapping(num_functions * 2);
      f_aggregate(2);
    }
    #endif /* PROFILING */
    
    void f_object_variablep(INT32 args)
    {
      struct object *o;
      struct pike_string *s;
      int ret;
    
      get_all_args("variablep",args,"%o%S",&o, &s);
    
      if(!o->prog)
        bad_arg_error("variablep", sp-args, args, 1, "object", sp-args,
    		  "Called on destructed object.\n");
    
      ret=find_shared_string_identifier(s,o->prog);
      if(ret!=-1)
      {
        ret=IDENTIFIER_IS_VARIABLE(ID_FROM_INT(o->prog, ret)->identifier_flags);
      }else{
        ret=0;
      }
      pop_n_elems(args);
      push_int(!!ret);
    }
    
    
    
    void f_splice(INT32 args)
    {
      struct array *out;
      INT32 size=0x7fffffff;
      INT32 i,j,k;
    
    #ifdef PIKE_DEBUG
      if(args < 0) fatal("Negative args to f_splice()\n");
    #endif
    
      for(i=0;i<args;i++)
        if (sp[i-args].type!=T_ARRAY) 
          SIMPLE_BAD_ARG_ERROR("splice", i+1, "array");
        else
          if (sp[i-args].u.array->size < size)
    	size=sp[i-args].u.array->size;
    
      out=allocate_array(args * size);
      if (!args)
      {
        push_array(out);
        return;
      }
    
      out->type_field=0;
      for(i=-args; i<0; i++) out->type_field|=sp[i].u.array->type_field;
    
      for(k=j=0; j<size; j++)
        for(i=-args; i<0; i++)
          assign_svalue_no_free(out->item+(k++), sp[i].u.array->item+j);
    
      pop_n_elems(args);
      push_array(out);
      return;
    }
    
    void f_everynth(INT32 args)
    {
      INT32 k,n=2;
      INT32 start=0;
      struct array *a;
      struct array *ina;
      INT32 size=0;
    #ifdef PIKE_DEBUG
      if(args < 0) fatal("Negative args to f_everynth()\n");
    #endif
    
      check_all_args("everynth", args,
    		 BIT_ARRAY, BIT_INT | BIT_VOID, BIT_INT | BIT_VOID , 0);
    
      switch(args)
      {
        default:
        case 3:
         start=sp[2-args].u.integer;
         if(start<0)
           bad_arg_error("everynth", sp-args, args, 3, "int", sp+2-args,
    		     "Argument negative.\n");
        case 2:
          n=sp[1-args].u.integer;
          if(n<1)
    	bad_arg_error("everynth", sp-args, args, 2, "int", sp+1-args,
    		      "Argument negative.\n");
        case 1:
          ina=sp[-args].u.array;
      }
    
      a=allocate_array(((size=ina->size)-start+n-1)/n);
      for(k=0; start<size; start+=n)
        assign_svalue_no_free(a->item+(k++), ina->item+start);
    
      a->type_field=ina->type_field;
      pop_n_elems(args);
      push_array(a);
      return;
    }
    
    
    void f_transpose(INT32 args)
    {
      struct array *out;
      struct array *in;
      struct array *outinner;
      struct array *ininner;
      INT32 sizeininner=0,sizein=0;
      INT32 inner=0;
      INT32 j,i;
      TYPE_FIELD type=0;
    #ifdef PIKE_DEBUG
      if(args < 0) fatal("Negative args to f_transpose()\n");
    #endif
      
      if (args<1)
        SIMPLE_TOO_FEW_ARGS_ERROR("transpose", 1);
    
      if (sp[-args].type!=T_ARRAY) 
        SIMPLE_BAD_ARG_ERROR("transpose", 1, "array(array)");
    
      in=sp[-args].u.array;
      sizein=in->size;
    
      if(!sizein)
      {
        pop_n_elems(args);
        out=allocate_array(0);
        push_array(out);
        return; 
      }
    
      if(in->type_field != BIT_ARRAY)
      {
        array_fix_type_field(in);
        if(!in->type_field || in->type_field & ~BIT_ARRAY)
          error("The array given as argument 1 to transpose must contain arrays only.\n");
      }
    
      sizeininner=in->item->u.array->size;
    
      for(i=1 ; i<sizein; i++)
        if (sizeininner!=(in->item+i)->u.array->size)
          error("The array given as argument 1 to transpose must contain arrays of the same size.\n");
    
      out=allocate_array(sizeininner);
    
      for(i=0; i<sizein; i++)
        type|=in->item[i].u.array->type_field;
      
      for(j=0; j<sizeininner; j++)
      {
        struct svalue * ett;
        struct svalue * tva;
    
        outinner=allocate_array(sizein);
        ett=outinner->item;
        tva=in->item;
        for(i=0; i<sizein; i++)
          assign_svalue_no_free(ett+i, tva[i].u.array->item+j);
    
        outinner->type_field=type;
        out->item[j].u.array=outinner;
        out->item[j].type=T_ARRAY;
      }
    
      out->type_field=BIT_ARRAY;
      pop_n_elems(args);
      push_array(out);
      return;
    }
    
    #ifdef DEBUG_MALLOC
    void f__reset_dmalloc(INT32 args)
    {
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY,
    			  ("_reset_dmalloc: permission denied.\n"));
      pop_n_elems(args);
      reset_debug_malloc();
    }
    
    void f__list_open_fds(INT32 args)
    {
      extern void list_open_fds(void);
      list_open_fds();
    }
    #endif
    
    #ifdef PIKE_DEBUG
    void f__locate_references(INT32 args)
    {
      CHECK_SECURITY_OR_ERROR(SECURITY_BIT_SECURITY,
    			  ("_locate_references: permission denied.\n"));
      if(args)
        locate_references(sp[-args].u.refs);
      pop_n_elems(args-1);
    }
    
    void f__describe(INT32 args)
    {
      debug_describe_svalue(sp-1);
    }
    
    #endif
    
    void f_map_array(INT32 args)
    {
      ONERROR tmp;
      INT32 e;
      struct svalue *fun;
      struct array *ret,*foo;
    
      if (args < 2)
        SIMPLE_TOO_FEW_ARGS_ERROR("map_array", 2);
    
      if(sp[-args].type != T_ARRAY)
        SIMPLE_BAD_ARG_ERROR("map_array", 1, "array");
      
      foo=sp[-args].u.array;
      fun=sp-args+1;
    
      ret=allocate_array(foo->size);
      SET_ONERROR(tmp, do_free_array, ret);
      for(e=0;e<foo->size;e++)
      {
        push_svalue(foo->item+e);
        assign_svalues_no_free(sp,fun+1,args-2,-1);
        sp+=args-2;
        apply_svalue(fun,args-1);
        ret->item[e]=*(--sp);
      }
      pop_n_elems(args);
      UNSET_ONERROR(tmp);
      push_array(ret);
    }
    
    void f_map(INT32 args)
    {
       /*
         map(arr,fun,extra) => ret
    
         arr	fun		goes
         array	function |	array ret; ret[i]=fun(arr[i],@extra);
           		program |	
               	object | 
                    array
    
         array	multiset |	ret = rows(fun,arr)
         		mapping 
    
         array	string  	array ret; ret[i]=arr[i][fun](@extra);
    
         array      void|int(0)     array ret; ret=arr(@extra)
    
         mapping |	*               mapping ret = 
         program |                     mkmapping(indices(arr),
         function			             map(values(arr),fun,@extra));
    
         multiset	*               multiset ret = 
                                       (multiset)(map(indices(arr),fun,@extra));
    
         string     *               string ret = 
                                       (string)map((array)arr,fun,@extra);
    
         object	*               if arr->cast :              
                                       try map((array)arr,fun,@extra);
                                       try map((mapping)arr,fun,@extra);
    				   try map((multiset)arr,fun,@extra);
                                    if arr->_sizeof && arr->`[] 
                                       array ret; ret[i]=arr[i];
    				   ret=map(ret,fun,@extra);
                                    error
    
         *          *               error (ie arr or fun = float or bad int )
    
        */
    
       struct svalue *mysp;
       struct array *a,*d,*f;
       int splice,i,n;
    
       if (args<1)
          SIMPLE_TOO_FEW_ARGS_ERROR("map", 1);
       else if (args<2)
          { push_int(0); args++; }
    
       switch (sp[-args].type)
       {
          case T_ARRAY:
    	 break;
    
          case T_MAPPING:
          case T_PROGRAM:
          case T_FUNCTION:
    	 /* mapping ret =                             
    	       mkmapping(indices(arr),                
    	                 map(values(arr),fun,@extra)); */
    	 f_aggregate(args-2);
    	 mysp=sp;
    	 splice=mysp[-1].u.array->size;
    
    	 push_svalue(mysp-3); /* arr */
    	 f_values(1);
    	 push_svalue(mysp-2); /* fun */
    	 *sp=mysp[-1];        /* extra */
    	 mysp[-1].type=T_INT;
    	 push_array_items(sp->u.array);
    	 f_map(splice+2);     /* ... arr fun extra -> ... retval */
    	 stack_pop_n_elems_keep_top(2); /* arr fun extra ret -> arr retval */
    	 stack_swap();        /* retval arr */
    	 f_indices(1);        /* retval retind */
    	 stack_swap();        /* retind retval */
    	 f_mkmapping(2);      /* ret :-) */
    	 return;
    
          case T_MULTISET:
    	 /* multiset ret =                             
    	       (multiset)(map(indices(arr),fun,@extra)); */
    	 push_svalue(sp-args);      /* take indices from arr */
    	 free_svalue(sp-args-1);    /* move it to top of stack */
    	 sp[-args-1].type=T_INT;    
    	 f_indices(1);              /* call f_indices */
    	 sp--;                       
    	 sp[-args]=sp[0];           /* move it back */
    	 f_map(args);               
    	 sp--;                      /* allocate_multiset is destructive */
    	 push_multiset(allocate_multiset(sp->u.array));
    	 return;
    
          case T_STRING:
    	 /* multiset ret =                             
    	       (string)(map((array)arr,fun,@extra)); */
    	 push_svalue(sp-args);      /* take indices from arr */
    	 free_svalue(sp-args-1);    /* move it to top of stack */
    	 sp[-args-1].type=T_INT;    
    	 o_cast(NULL,T_ARRAY);      /* cast the string to an array */
    	 sp--;                       
    	 sp[-args]=sp[0];           /* move it back */
    	 f_map(args);               
    	 o_cast(NULL,T_STRING);     /* cast the array to a string */
    	 return;
    
          case T_OBJECT:
    	 /* if arr->cast :              
                   try map((array)arr,fun,@extra);
                   try map((mapping)arr,fun,@extra);
                   try map((multiset)arr,fun,@extra); */
    
    	 mysp=sp+3-args;
    
    	 push_svalue(mysp-3);
    	 push_constant_text("cast");
    	 f_arrow(2);
    	 if (!IS_ZERO(sp-1))
    	 {
    	    pop_stack();
    
    	    push_constant_text("array");
    	    safe_apply(mysp[-3].u.object,"cast",1);
    	    if (sp[-1].type==T_ARRAY)
    	    {
    	       free_svalue(mysp-3);
    	       mysp[-3]=*(--sp);
    	       f_map(args);
    	       return;
    	    }
    	    pop_stack();
    
    	    push_constant_text("mapping");
    	    safe_apply(mysp[-3].u.object,"cast",1);
    	    if (sp[-1].type==T_MAPPING)
    	    {
    	       free_svalue(mysp-3);
    	       mysp[-3]=*(--sp);
    	       f_map(args);
    	       return;
    	    }
    	    pop_stack();
    
    	    push_constant_text("multiset");
    	    safe_apply(mysp[-3].u.object,"cast",1);
    	    if (sp[-1].type==T_MULTISET)
    	    {
    	       free_svalue(mysp-3);
    	       mysp[-3]=*(--sp);
    	       f_map(args);
    	       return;
    	    }
    	    pop_stack();
    	 }
    	 pop_stack();
    
             /* if arr->_sizeof && arr->`[] 
                   array ret; ret[i]=arr[i];
                   ret=map(ret,fun,@extra); */
    
    	 /* class myarray { int a0=1,a1=2; int `[](int what) { return ::`[]("a"+what); } int _sizeof() { return 2; } } 
    	    map(myarray(),lambda(int in){ werror("in=%d\n",in); }); */
    
    	 push_svalue(mysp-3);
    	 push_constant_text("`[]");
    	 f_arrow(2);
    	 push_svalue(mysp-3);
    	 push_constant_text("_sizeof");
    	 f_arrow(2);
    	 if (!IS_ZERO(sp-2)&&!IS_ZERO(sp-1))
    	 {
    	    f_call_function(1);
    	    if (sp[-1].type!=T_INT)
    	       SIMPLE_BAD_ARG_ERROR("map", 1, 
    				    "object sizeof() returning integer");
    	    n=sp[-1].u.integer;
    	    pop_stack();
    	    push_array(d=allocate_array(n));
    	    stack_swap();
    	    for (i=0; i<n; i++)
    	    {
    	       stack_dup(); /* `[] */
    	       push_int(i);
    	       f_call_function(2);
    	       d->item[i]=*(--sp);
    	    }
    	    pop_stack();
    	    free_svalue(mysp-3);
    	    mysp[-3]=*(--sp);
    	    f_map(args);
    	    return;
    	 }
    	 pop_stack();
    	 pop_stack();
    
    	 SIMPLE_BAD_ARG_ERROR("map",1,
    			      "object that works in map");
    
          default:
    	 SIMPLE_BAD_ARG_ERROR("map",1,
    			      "array|mapping|program|function|"
    			      "multiset|string|object");
       }
    
       f_aggregate(args-2);
       mysp=sp;
       splice=mysp[-1].u.array->size;
    
       a=mysp[-3].u.array;
       n=a->size;
    
       switch (mysp[-2].type)
       {
          case T_FUNCTION:
          case T_PROGRAM:
          case T_OBJECT:
          case T_ARRAY:
    	 /* ret[i]=fun(arr[i],@extra); */
             push_array(d=allocate_array(n));
    	 d=sp[-1].u.array;
    	 for (i=0; i<n; i++)
    	 {
    	    push_svalue(a->item+i);
    	    if (splice) 
    	    {
    	       add_ref_svalue(mysp-1);
    	       push_array_items(mysp[-1].u.array);
    	       apply_svalue(mysp-2,1+splice);
    	    }
    	    else
    	    {
    	       apply_svalue(mysp-2,1);
    	    }
    	    d->item[i]=*--sp;
    	 }
    	 stack_pop_n_elems_keep_top(3); /* fun arr extra d -> d */
    	 return;
    
          case T_MAPPING:
          case T_MULTISET:
    	 /* ret[i]=fun[arr[i]]; */
    	 pop_stack();
    	 stack_swap();
    	 f_rows(2);
    	 return; 
    
          case T_STRING:
    	 /* ret[i]=arr[i][fun](@extra); */
             push_array(d=allocate_array(n));
    	 d=sp[-1].u.array;
    	 for (i=0; i<n; i++)
    	 {
    	    push_svalue(a->item+i);
    	    push_svalue(mysp-2);
    	    f_arrow(2);
    	    if(IS_ZERO(sp-1))
    	    {
    	      pop_stack();
    	      continue;
    	    }
    	    add_ref_svalue(mysp-1);
    	    push_array_items(mysp[-1].u.array);
    	    f_call_function(splice+1);
    	    d->item[i]=*--sp;
    	 }
    	 stack_pop_n_elems_keep_top(3); /* fun arr extra d -> d */
    	 return;
    
          case T_INT:
    	 if (mysp[-2].u.integer==0)
    	 {
    	    /* ret=arr(@extra); */
    	    stack_swap(); /* arr fun extra -> arr extra fun */
    	    pop_stack();  /* arr extra */
    	    sp--;
    	    push_array_items(sp->u.array);
    	    f_call_function(1+splice);
    	    return;
    	 }	    
    	 /* no break here */
          default:
    	 SIMPLE_BAD_ARG_ERROR("map",2,
    			      "function|program|object|"
    			      "string|int(0)|multiset");
       }      
    }
    
    void f_filter(INT32 args)
    {
       /* filter(mixed arr,mixed fun,mixed ... extra) ->
    
            _arr__________goes________
    
            array		if fun is an array:
    			   for (i=0; i<sizeof(arr); i++) 
    				   if (fun[i]) res+=({arr[i]});
    			otherwise:
    			   keep=map(arr,fun,@extra);
    			   for (i=0; i<sizeof(arr); i++) 
    				   if (keep[i]) res+=({arr[i]});
            
            multiset	(multiset)filter((array)arr,fun,@extra);
            
            mapping |	ind=indices(arr),val=values(arr)
            program |       keep=map(val,fun,@extra);
            function     	for (i=0; i<sizeof(keep); i++) 
    				if (keep[i]) res[ind[i]]=val[i];
            
            string		(string)filter( (array)arr,fun,@extra );
       
            object	        if arr->cast :              
                               try filter((array)arr,fun,@extra);
                               try filter((mapping)arr,fun,@extra);
    			   try filter((multiset)arr,fun,@extra);
       */
    
       int n,i,m,k;
       struct array *a,*y,*f;
       struct svalue *mysp;
    
       if (args<1)
          SIMPLE_TOO_FEW_ARGS_ERROR("filter", 1);
       
       switch (sp[-args].type)
       {
          case T_ARRAY:
    	 if (args >= 2 && sp[1-args].type == T_ARRAY) {
    	   if (sp[1-args].u.array->size != sp[-args].u.array->size)
    	     SIMPLE_BAD_ARG_ERROR("filter", 2, "array of same size as the first");
    	   pop_n_elems(args-2);
    	 }
    	 else {
    	   MEMMOVE(sp-args+1,sp-args,args*sizeof(*sp));
    	   sp++;
    	   add_ref_svalue(sp-args);
    	   f_map(args);
    	 }
    
    	 f=sp[-1].u.array;
    	 a=sp[-2].u.array;
    	 n=a->size;
    	 for (k=m=i=0; i<n; i++)
    	    if (!IS_ZERO(f->item+i))
    	    {
    	       push_svalue(a->item+i);
    	       if (m++>32) 
    	       {
    		  f_aggregate(m);
    		  m=0;
    		  if (++k>32) {
    		    f_add(k);
    		    k=1;
    		  }
    	       }
    	    }
    	 if (m || !k) {
    	   f_aggregate(m);
    	   k++;
    	 }
    	 if (k > 1) f_add(k);
    	 stack_pop_n_elems_keep_top(2);
    	 return;
    
          case T_MAPPING:
          case T_PROGRAM:
          case T_FUNCTION:
    	 /* mapping ret =                             
    	       mkmapping(indices(arr),                
    	                 map(values(arr),fun,@extra)); */
    	 MEMMOVE(sp-args+2,sp-args,args*sizeof(*sp));
    	 sp+=2;
    	 sp[-args-2].type=T_INT;
    	 sp[-args-1].type=T_INT;
    
    	 push_svalue(sp-args);
    	 f_indices(1);
    	 sp[-args-3]=*--sp;
    	 push_svalue(sp-args);
    	 f_values(1);
    	 sp[-args-2]=*--sp;
    
    	 assign_svalue(sp-args,sp-args-1); /* loop values only */
    	 f_map(args);
    
    	 y=sp[-3].u.array;
    	 a=sp[-2].u.array;
    	 f=sp[-1].u.array;
    	 n=a->size;
    
    	 for (m=i=0; i<n; i++)
    	    if (!IS_ZERO(f->item+i)) m++;
    
    	 push_mapping(allocate_mapping(MAXIMUM(m,4)));
    
    	 for (i=0; i<n; i++)
    	    if (!IS_ZERO(f->item+i))
    	       mapping_insert(sp[-1].u.mapping,y->item+i,a->item+i);
    
    	 stack_pop_n_elems_keep_top(3);
    	 return;
    
          case T_MULTISET:
    	 push_svalue(sp-args);      /* take indices from arr */
    	 free_svalue(sp-args-1);    /* move it to top of stack */
    	 sp[-args-1].type=T_INT;    
    	 f_indices(1);              /* call f_indices */
    	 sp--;                       
    	 sp[-args]=sp[0];           /* move it back */
    	 f_filter(args);               
    	 sp--;                      /* allocate_multiset is destructive */
    	 push_multiset(allocate_multiset(sp->u.array));
    	 return;
    
          case T_STRING:
    	 push_svalue(sp-args);      /* take indices from arr */
    	 free_svalue(sp-args-1);    /* move it to top of stack */
    	 sp[-args-1].type=T_INT;    
    	 o_cast(NULL,T_ARRAY);      /* cast the string to an array */
    	 sp--;                       
    	 sp[-args]=sp[0];           /* move it back */
    	 f_filter(args);               
    	 o_cast(NULL,T_STRING);     /* cast the array to a string */
    	 return;
    
          case T_OBJECT:
    	 mysp=sp+3-args;
    
    	 push_svalue(mysp-3);
    	 push_constant_text("cast");
    	 f_arrow(2);
    	 if (!IS_ZERO(sp-1))
    	 {
    	    pop_stack();
    
    	    push_constant_text("array");
    	    safe_apply(mysp[-3].u.object,"cast",1);
    	    if (sp[-1].type==T_ARRAY)
    	    {
    	       free_svalue(mysp-3);
    	       mysp[-3]=*(--sp);
    	       f_filter(args);
    	       return;
    	    }
    	    pop_stack();
    
    	    push_constant_text("mapping");
    	    safe_apply(mysp[-3].u.object,"cast",1);
    	    if (sp[-1].type==T_MAPPING)
    	    {
    	       free_svalue(mysp-3);
    	       mysp[-3]=*(--sp);
    	       f_filter(args);
    	       return;
    	    }
    	    pop_stack();
    
    	    push_constant_text("multiset");
    	    safe_apply(mysp[-3].u.object,"cast",1);
    	    if (sp[-1].type==T_MULTISET)
    	    {
    	       free_svalue(mysp-3);
    	       mysp[-3]=*(--sp);
    	       f_filter(args);
    	       return;
    	    }
    	    pop_stack();
    	 }
    	 pop_stack();
    
    	 SIMPLE_BAD_ARG_ERROR("filter",1,
    			      "...|object that can be cast to array, multiset or mapping");
    
          default:
    	 SIMPLE_BAD_ARG_ERROR("filter",1,
    			      "array|mapping|program|function|"
    			      "multiset|string|object");
       }
    }
    
    void f_enumerate(INT32 args)
    {
       struct array *d;
       int i;
       INT_TYPE n;
    
       if (args<1)
          SIMPLE_TOO_FEW_ARGS_ERROR("enumarate", 1);
       if (args<2) 
       {
          push_int(1);
          args++;
       }
       if (args<3)
       {
          push_int(0);
          args++;
       }
    
       if (args<=3 &&
           (sp[1-args].type==T_INT &&
    	sp[2-args].type==T_INT))
       {
          INT_TYPE step,start;
    
          get_all_args("enumerate", args, "%i%i%i", &n, &step, &start);
          if (n<0) 
    	 SIMPLE_BAD_ARG_ERROR("enumerate",1,"int(0..)");
    
          pop_n_elems(args);
          push_array(d=allocate_array(n));
          for (i=0; i<n; i++)
          {
    	 d->item[i].u.integer=start;
    	 d->item[i].type=T_INT;
    	 d->item[i].subtype=NUMBER_NUMBER;
    	 start+=step;
          }
       }
       else if (args<=3 &&
    	    ((sp[1-args].type==T_INT ||
    	      sp[1-args].type==T_FLOAT) &&
    	     (sp[2-args].type==T_INT ||
    	      sp[2-args].type==T_FLOAT) ) )
       {
          FLOAT_TYPE step, start;
    
          get_all_args("enumerate", args, "%i%F%F", &n, &step, &start);
          if (n<0) 
    	 SIMPLE_BAD_ARG_ERROR("enumerate",1,"int(0..)");
    
          pop_n_elems(args);
    
          push_array(d=allocate_array(n));
          for (i=0; i<n; i++)
          {
    	 d->item[i].u.float_number=start;
    	 d->item[i].type=T_FLOAT;
    	 start+=step;
          }
       }
       else
       {
          get_all_args("enumerate", args, "%i", &n);
          if (n<0) SIMPLE_BAD_ARG_ERROR("enumerate",1,"int(0..)");
          if (args>4) pop_n_elems(args-4);
          if (args<4)
          {
    	 push_array(d=allocate_array(n));
    	 push_svalue(sp-2); /* start */
    	 for (i=0; i<n; i++)
    	 {
    	    assign_svalue_no_free(d->item+i,sp-1);
    	    if (i<n-1)
    	    {
    	       push_svalue(sp-4); /* step */
    	       f_add(2);
    	    }
    	 }
          }
          else
          {
    	 push_array(d=allocate_array(n));
    	 push_svalue(sp-3); /* start */
    	 for (i=0; i<n; i++)
    	 {
    	    assign_svalue_no_free(d->item+i,sp-1);
    	    if (i<n-1)
    	    {
    	       push_svalue(sp-3); /* function */
    	       stack_swap();
    	       push_svalue(sp-6); /* step */
    	       f_call_function(3);
    	    }
    	 }
          }
          pop_stack();
          stack_pop_n_elems_keep_top(4);
       }
    }
    
    void f_string_count(INT32 args)
    {
       struct pike_string * haystack=NULL;
       struct pike_string * needle=NULL;
       int c=0;
       int i,j;
    
       get_all_args("String.count",args,"%W%W",&haystack,&needle);
    
       switch (needle->len)
       {
          case 0:
    	 switch (haystack->len)
    	 {
    	    case 0: c=1; break; /* "" appears one time in "" */
    	    case 1: c=0; break; /* "" doesn't appear in "x" */
    	    default: c=haystack->len-1; /* one time between each character */
    	 }
    	 break;
          case 1:
    	 /* maybe optimize? */
          default:
    	 for (i=0; i<haystack->len; i++)
    	 {
    	    j=string_search(haystack,needle,i);
    	    if (j==-1) break;
    	    i=j+needle->len-1;
    	    c++;
    	 }
    	 break;
       }
       pop_n_elems(args);
       push_int(c);
    }
    
    void f_inherit_list(INT32 args)
    {
      struct program *p;
      struct svalue *arg;
      struct object *par;
      int parid,e,q=0;
    
      get_all_args("inherit_list",args,"%*",&arg);
      if(sp[-args].type == T_OBJECT)
        f_object_program(1);
      
      p=program_from_svalue(arg);
      if(!p) 
        SIMPLE_BAD_ARG_ERROR("inherit_list", 1, "program");
    
      if(arg->type == T_FUNCTION)
      {
        par=arg->u.object;
        parid=arg->subtype;
      }else{
        par=0;
        parid=-1;
      }
    
      check_stack(p->num_inherits);
      for(e=0;e<p->num_inherits;e++)
      {
        struct inherit *in=p->inherits+e;
        if(in->inherit_level==1)
        {
          if(in->parent_offset)
          {
    	struct inherit *inherit=in;
    	int accumulator;
    	struct object *o=par;
    	int i;
    
    	o=par;
    	i=in->parent_identifier;
    	accumulator=inherit->parent_offset - 1;
    	while(accumulator--)
    	{
    	  struct program *p;
    	  if(inherit->parent_offset)
    	  {
    	    i=o->parent_identifier;
    	    o=o->parent;
    	    accumulator+=inherit->parent_offset-1;
    	  }else{
    	    i=inherit->parent_identifier;
    	    o=inherit->parent;
    	  }
    	  if(!o || !o->prog || i<0) break;
    	  inherit=INHERIT_FROM_INT(o->prog, i);
    	}
    	if(o && o->prog && i>=0)
    	{
    	  ref_push_object(o);
    	  sp[-1].subtype=i;
    	  sp[-1].type=T_FUNCTION;
    #ifdef PIKE_DEBUG
    	  if(program_from_svalue(sp-1) != in->prog)
    	    fatal("Programming error in inherit_list!\n");
    #endif
    	  q++;
    	  continue;
    	}
          }
    
          if(in->parent && in->parent->prog)
          {
    	ref_push_object(in->parent);
    	sp[-1].subtype=in->parent_identifier;
    	sp[-1].type=T_FUNCTION;
    #ifdef PIKE_DEBUG
    	if(program_from_svalue(sp-1) != in->prog)
    	  fatal("Programming error in inherit_list!\n");
    #endif
          }else{
    	ref_push_program(in->prog);
          }
          q++;
        }
      }
      f_aggregate(q);
    }
    
    
    void f_program_implements(INT32 args)
    {
      struct program *p,*p2;
      int ret;
      get_all_args("Program.implements",args,"%p%p",&p,&p2);
    
      ret=implements(p,p2);
      pop_n_elems(args);
      push_int(ret);
    }
    
    void f_program_inherits(INT32 args)
    {
      struct program *p,*p2;
      struct svalue *arg,*arg2;
      int ret;
      get_all_args("Program.inherits",args,"%p%p",&p,&p2);
    
      ret=!!low_get_storage(p2,p);
      pop_n_elems(args);
      push_int(ret);
    }
    
    void f_program_defined(INT32 args)
    {
      struct program *p;
      get_all_args("Program.defined",args,"%p",&p);
    
      if(p && p->num_linenumbers)
      {
        char *tmp;
        INT32 line;
        if((tmp=get_line(p->program, p, &line)))
        {
          struct pike_string *tmp2;
          tmp2=make_shared_string(tmp);
          pop_n_elems(args);
    
          push_string(tmp2);
          if(line > 1)
          {
    	push_constant_text(":");
    	push_int(line);
    	f_add(3);
          }
          return;
        }
      }
    
      pop_n_elems(args);
      push_int(0);
    }
    
    void f_function_defined(INT32 args)
    {
      check_all_args("Function.defined",args,BIT_FUNCTION, 0);
    
      if(sp[-args].subtype != FUNCTION_BUILTIN &&
         sp[-args].u.object->prog)
      {
        char *tmp;
        INT32 line;
        struct identifier *id=ID_FROM_INT(sp[-args].u.object->prog,
    				      sp[-args].subtype);
        if(IDENTIFIER_IS_PIKE_FUNCTION( id->identifier_flags ) &&
          id->func.offset != -1)
        {
          if((tmp=get_line(sp[-args].u.object->prog->program + id->func.offset,
    		       sp[-args].u.object->prog,
    		       &line)))
          {
    	struct pike_string *tmp2;
    	tmp2=make_shared_string(tmp);
    	pop_n_elems(args);
    	
    	push_string(tmp2);
    	push_constant_text(":");
    	push_int(line);
    	f_add(3);
    	return;
          }
        }
      }
    
      pop_n_elems(args);
      push_int(0);
      
    }
    
    void f_string_width(INT32 args)
    {
      struct pike_string *s;
      int ret;
      get_all_args("String.width",args,"%S",&s);
      ret=s->size_shift;
      pop_n_elems(args);
      push_int(8 * (1<<ret));
    }
    
    void init_builtin_efuns(void)
    {
      ADD_EFUN("gethrtime", f_gethrtime,tFunc(tOr(tInt,tVoid),tInt), OPT_EXTERNAL_DEPEND);
    
    #ifdef HAVE_GETHRVTIME
      ADD_EFUN("gethrvtime",f_gethrvtime,tFunc(tVoid,tInt),OPT_EXTERNAL_DEPEND);
    #endif
      
    #ifdef PROFILING
      ADD_EFUN("get_profiling_info", f_get_prof_info,
    	   tFunc(tPrg,tArray), OPT_EXTERNAL_DEPEND);
    #endif /* PROFILING */
    
      ADD_EFUN("_refs",f__refs,tFunc(tRef,tInt),OPT_EXTERNAL_DEPEND);
      ADD_EFUN("_typeof",f__typeof,tFunc(tMix,tStr),0);
      ADD_EFUN("replace_master",f_replace_master,tFunc(tObj,tVoid),OPT_SIDE_EFFECT);
      
    /* function(:object) */
      ADD_EFUN("master",f_master,tFunc(tNone,tObj),OPT_EXTERNAL_DEPEND);
      
    /* function(string,void|mixed:void) */
      ADD_EFUN("add_constant",f_add_constant,tFunc(tStr tOr(tVoid,tMix),tVoid),OPT_SIDE_EFFECT);
      
    /* function(0=mixed ...:array(0)) */
    #ifdef DEBUG_MALLOC
      ADD_EFUN("aggregate",_f_aggregate,tFuncV(tNone,tSetvar(0,tMix),tArr(tVar(0))),OPT_TRY_OPTIMIZE);
    #else
      ADD_EFUN("aggregate",f_aggregate,tFuncV(tNone,tSetvar(0,tMix),tArr(tVar(0))),OPT_TRY_OPTIMIZE);
    #endif
      
    /* function(0=mixed ...:multiset(0)) */
      ADD_EFUN("aggregate_multiset",f_aggregate_multiset,tFuncV(tNone,tSetvar(0,tMix),tSet(tVar(0))),OPT_TRY_OPTIMIZE);
      
    /* function(0=mixed ...:mapping(0:0)) */
      ADD_EFUN("aggregate_mapping",f_aggregate_mapping,tFuncV(tNone,tSetvar(0,tMix),tMap(tVar(0),tVar(0))),OPT_TRY_OPTIMIZE);
      
    /* function(:mapping(string:mixed)) */
      ADD_EFUN("all_constants",f_all_constants,tFunc(tNone,tMap(tStr,tMix)),OPT_EXTERNAL_DEPEND);
      
    /* function(int,void|0=mixed:array(0)) */
      ADD_EFUN("allocate", f_allocate,tFunc(tInt tOr(tVoid,tSetvar(0,tMix)),tArr(tVar(0))), 0);
      
    /* function(mixed:int) */
      ADD_EFUN("arrayp",  f_arrayp,tFunc(tMix,tInt),0);
      
    /* function(:array(array(function|int|string))) */
      ADD_EFUN("backtrace",f_backtrace,tFunc(tNone,tArr(tArr(tOr3(tFunction,tInt,tStr)))),OPT_EXTERNAL_DEPEND);
    
      
    /* function(array,mixed:array) */
      ADD_EFUN("column",f_column,tFunc(tArray tMix,tArray),0);
      
    /* function(string...:string) */
      ADD_EFUN("combine_path",f_combine_path,tFuncV(tNone,tStr,tStr),0);
      
    /* function(string,mixed...:program) */
      ADD_EFUN("compile",f_compile,tFuncV(tStr,tMix,tPrg),OPT_EXTERNAL_DEPEND);
      
    /* function(1=mixed:1) */
      ADD_EFUN("copy_value",f_copy_value,tFunc(tSetvar(1,tMix),tVar(1)),0);
      
    /* function(string:string)|function(string,string:int) */
      ADD_EFUN("crypt",f_crypt,tOr(tFunc(tStr,tStr),tFunc(tStr tStr,tInt)),OPT_EXTERNAL_DEPEND);
      
    /* function(int:string) */
      ADD_EFUN("ctime",f_ctime,tFunc(tInt,tStr),OPT_TRY_OPTIMIZE);
      
    /* function(object|void:void) */
      ADD_EFUN("destruct",f_destruct,tFunc(tOr(tObj,tVoid),tVoid),OPT_SIDE_EFFECT);
      
    /* function(mixed,mixed:int) */
      ADD_EFUN("equal",f_equal,tFunc(tMix tMix,tInt),OPT_TRY_OPTIMIZE);
      /* function(array(0=mixed),int|void,int|void:array(0)) */
      ADD_FUNCTION("everynth",f_everynth,tFunc(tArr(tSetvar(0,tMix)) tOr(tInt,tVoid) tOr(tInt,tVoid),tArr(tVar(0))), 0);
      
    /* function(int:void) */
      ADD_EFUN("exit",f_exit,tFunc(tInt,tVoid),OPT_SIDE_EFFECT);
      
    /* function(int:void) */
      ADD_EFUN("_exit",f__exit,tFunc(tInt,tVoid),OPT_SIDE_EFFECT);
      
    /* function(mixed:int) */
      ADD_EFUN("floatp",  f_floatp,tFunc(tMix,tInt),OPT_TRY_OPTIMIZE);
      
    /* function(function:string) */
      ADD_EFUN("function_name",f_function_name,tFunc(tFunction,tStr),OPT_TRY_OPTIMIZE);
      
    /* function(function:object) */
      ADD_EFUN("function_object",f_function_object,tFunc(tFunction,tObj),OPT_TRY_OPTIMIZE);
      
    /* function(mixed:int) */
      ADD_EFUN("functionp",  f_functionp,tFunc(tMix,tInt),OPT_TRY_OPTIMIZE);
      
    /* function(string,string:int)|function(string,string*:array(string)) */
      ADD_EFUN("glob",f_glob,tOr(tFunc(tStr tStr,tInt),tFunc(tStr tArr(tStr),tArr(tStr))),OPT_TRY_OPTIMIZE);
      
    /* function(string,int|void:int) */
      ADD_EFUN("hash",f_hash,tFunc(tStr tOr(tInt,tVoid),tInt),OPT_TRY_OPTIMIZE);
      
    /* function(string|array:int*)|function(mapping(1=mixed:mixed)|multiset(1=mixed):array(1))|function(object|program:string*) */
      ADD_EFUN("indices",f_indices,tOr3(tFunc(tOr(tStr,tArray),tArr(tInt)),tFunc(tOr(tMap(tSetvar(1,tMix),tMix),tSet(tSetvar(1,tMix))),tArr(tVar(1))),tFunc(tOr(tObj,tPrg),tArr(tStr))),0);
      
    /* function(mixed:int) */
      ADD_EFUN("intp",   f_intp,tFunc(tMix,tInt),OPT_TRY_OPTIMIZE);
      
    /* function(mixed:int) */
      ADD_EFUN("multisetp",   f_multisetp,tFunc(tMix,tInt),OPT_TRY_OPTIMIZE);
      
    /* function(string:string) */
      ADD_EFUN("lower_case",f_lower_case,tFunc(tStr,tStr),OPT_TRY_OPTIMIZE);
      
    /* function(0=mapping,mixed:0) */
      ADD_EFUN("m_delete",f_m_delete,tFunc(tSetvar(0,tMapping) tMix,tVar(0)),0);
      
    /* function(mixed:int) */
      ADD_EFUN("mappingp",f_mappingp,tFunc(tMix,tInt),OPT_TRY_OPTIMIZE);
      
    /* function(array(1=mixed),array(2=mixed):mapping(1:2)) */
      ADD_EFUN("mkmapping",f_mkmapping,tFunc(tArr(tSetvar(1,tMix)) tArr(tSetvar(2,tMix)),tMap(tVar(1),tVar(2))),OPT_TRY_OPTIMIZE);
      
    /* function(mapping,int:int) */
      ADD_EFUN("set_weak_flag",f_set_weak_flag,tFunc(tSetvar(1,tMix) tInt,tVar(1)),OPT_SIDE_EFFECT);
      
    /* function(void|object:object) */
      ADD_EFUN("next_object",f_next_object,tFunc(tOr(tVoid,tObj),tObj),OPT_EXTERNAL_DEPEND);
      
    /* function(string:string)|function(object:object)|function(mapping:mapping)|function(multiset:multiset)|function(program:program)|function(array:array) */
      ADD_EFUN("_next",f__next,tOr6(tFunc(tStr,tStr),tFunc(tObj,tObj),tFunc(tMapping,tMapping),tFunc(tMultiset,tMultiset),tFunc(tPrg,tPrg),tFunc(tArray,tArray)),OPT_EXTERNAL_DEPEND);
      
    /* function(object:object)|function(mapping:mapping)|function(multiset:multiset)|function(program:program)|function(array:array) */
      ADD_EFUN("_prev",f__prev,tOr5(tFunc(tObj,tObj),tFunc(tMapping,tMapping),tFunc(tMultiset,tMultiset),tFunc(tPrg,tPrg),tFunc(tArray,tArray)),OPT_EXTERNAL_DEPEND);
      
    /* function(mixed:program) */
      ADD_EFUN("object_program",f_object_program,tFunc(tMix,tPrg),0);
      
    /* function(mixed:int) */
      ADD_EFUN("objectp", f_objectp,tFunc(tMix,tInt),0);
      
    /* function(mixed:int) */
      ADD_EFUN("programp",f_programp,tFunc(tMix,tInt),0);
      
    /* function(:int) */
      ADD_EFUN("query_num_arg",f_query_num_arg,tFunc(tNone,tInt),OPT_EXTERNAL_DEPEND);
      
    /* function(int:int) */
      ADD_EFUN("random",f_random,tFunc(tInt,tInt),OPT_EXTERNAL_DEPEND);
      
    /* function(int:void) */
      ADD_EFUN("random_seed",f_random_seed,tFunc(tInt,tVoid),OPT_SIDE_EFFECT);
      
    /* function(string,string,string:string)|function(string,string*,string*:string)|function(0=array,mixed,mixed:0)|function(1=mapping,mixed,mixed:1) */
      ADD_EFUN("replace",f_replace,tOr4(tFunc(tStr tStr tStr,tStr),tFunc(tStr tArr(tStr) tArr(tStr),tStr),tFunc(tSetvar(0,tArray) tMix tMix,tVar(0)),tFunc(tSetvar(1,tMapping) tMix tMix,tVar(1))),0);
      
    /* function(int:int)|function(string:string)|function(array:array) */
      ADD_EFUN("reverse",f_reverse,tOr3(tFunc(tInt,tInt),tFunc(tStr,tStr),tFunc(tArray,tArray)),0);
      
    /* function(mixed,array:array) */
      ADD_EFUN("rows",f_rows,tFunc(tMix tArray,tArray),0);
      
    /* function(:int *) */
      ADD_EFUN("rusage", f_rusage,tFunc(tNone,tArr(tInt)),OPT_EXTERNAL_DEPEND);
      
    /* function(string,string,void|int:int)|function(array,mixed,void|int:int)|function(mapping,mixed:mixed) */
      ADD_EFUN("search",f_search,tOr3(tFunc(tStr tStr tOr(tVoid,tInt),tInt),tFunc(tArray tMix tOr(tVoid,tInt),tInt),tFunc(tMapping tMix,tMix)),0);
      
    /* function(float|int,int|void:void) */
      ADD_EFUN("sleep", f_sleep,tFunc(tOr(tFlt,tInt) tOr(tInt,tVoid),tVoid),OPT_SIDE_EFFECT);
      
    /* function(array(0=mixed),array(mixed)...:array(0)) */
      ADD_EFUN("sort",f_sort,tFuncV(tArr(tSetvar(0,tMix)),tArr(tMix),tArr(tVar(0))),OPT_SIDE_EFFECT);
      /* function(array(0=mixed)...:array(0)) */
      ADD_FUNCTION("splice",f_splice,tFuncV(tNone,tArr(tSetvar(0,tMix)),tArr(tVar(0))), 0);
      
    /* function(mixed:int) */
      ADD_EFUN("stringp", f_stringp,tFunc(tMix,tInt),0);
      
    /* function(:object) */
      ADD_EFUN("this_object", f_this_object,tFunc(tNone,tObj),OPT_EXTERNAL_DEPEND);
      
    /* function(mixed:void) */
      ADD_EFUN("throw",f_throw,tFunc(tMix,tVoid),OPT_SIDE_EFFECT);
      
    /* function(void|int:int|float) */
      ADD_EFUN("time",f_time,tFunc(tOr(tVoid,tInt),tOr(tInt,tFlt)),OPT_EXTERNAL_DEPEND);
      
    /* function(int:int) */
      ADD_EFUN("trace",f_trace,tFunc(tInt,tInt),OPT_SIDE_EFFECT);
      /* function(array(0=mixed):array(0)) */
      ADD_FUNCTION("transpose",f_transpose,tFunc(tArr(tSetvar(0,tMix)),tArr(tVar(0))), 0);
      
    /* function(string:string) */
      ADD_EFUN("upper_case",f_upper_case,tFunc(tStr,tStr),0);
      
    /* function(string|multiset:array(int))|function(array(0=mixed)|mapping(mixed:0=mixed)|object|program:array(0)) */
      ADD_EFUN("values",f_values,tOr(tFunc(tOr(tStr,tMultiset),tArr(tInt)),tFunc(tOr4(tArr(tSetvar(0,tMix)),tMap(tMix,tSetvar(0,tMix)),tObj,tPrg),tArr(tVar(0)))),0);
      
    /* function(mixed:int) */
      ADD_EFUN("zero_type",f_zero_type,tFunc(tMix,tInt),0);
      
    /* function(string,string:array) */
      ADD_EFUN("array_sscanf",f_sscanf,tFunc(tStr tStr,tArray),0);
    
      /* Some Wide-string stuff */
      
    /* function(string:string) */
      ADD_EFUN("string_to_unicode", f_string_to_unicode,tFunc(tStr,tStr), OPT_TRY_OPTIMIZE);
      
    /* function(string:string) */
      ADD_EFUN("unicode_to_string", f_unicode_to_string,tFunc(tStr,tStr), OPT_TRY_OPTIMIZE);
      
    /* function(string,int|void:string) */
      ADD_EFUN("string_to_utf8", f_string_to_utf8,tFunc(tStr tOr(tInt,tVoid),tStr), OPT_TRY_OPTIMIZE);
      
    /* function(string,int|void:string) */
      ADD_EFUN("utf8_to_string", f_utf8_to_string,tFunc(tStr tOr(tInt,tVoid),tStr), OPT_TRY_OPTIMIZE);
    
    #ifdef HAVE_LOCALTIME
      
    /* function(int:mapping(string:int)) */
      ADD_EFUN("localtime",f_localtime,tFunc(tInt,tMap(tStr,tInt)),OPT_EXTERNAL_DEPEND);
    #endif
    #ifdef HAVE_GMTIME
      
    /* function(int:mapping(string:int)) */
      ADD_EFUN("gmtime",f_gmtime,tFunc(tInt,tMap(tStr,tInt)),OPT_EXTERNAL_DEPEND);
    #endif
    
    #ifdef HAVE_MKTIME
      
    /* function(int,int,int,int,int,int,int,void|int:int)|function(object|mapping:int) */
      ADD_EFUN("mktime",f_mktime,tOr(tFunc(tInt tInt tInt tInt tInt tInt tInt tOr(tVoid,tInt),tInt),tFunc(tOr(tObj,tMapping),tInt)),OPT_TRY_OPTIMIZE);
    #endif
    
    #ifdef PIKE_DEBUG
      
    /* function(:void) */
      ADD_EFUN("_verify_internals",f__verify_internals,tFunc(tNone,tVoid),OPT_SIDE_EFFECT|OPT_EXTERNAL_DEPEND);
      
    /* function(int:int) */
      ADD_EFUN("_debug",f__debug,tFunc(tInt,tInt),OPT_SIDE_EFFECT|OPT_EXTERNAL_DEPEND);
    #ifdef YYDEBUG
      
    /* function(int:int) */
      ADD_EFUN("_compiler_trace",f__compiler_trace,tFunc(tInt,tInt),OPT_SIDE_EFFECT|OPT_EXTERNAL_DEPEND);
    #endif /* YYDEBUG */
    #endif
      
    /* function(:mapping(string:int)) */
      ADD_EFUN("_memory_usage",f__memory_usage,tFunc(tNone,tMap(tStr,tInt)),OPT_EXTERNAL_DEPEND);
    
      
    /* function(:int) */
      ADD_EFUN("gc",f_gc,tFunc(tNone,tInt),OPT_SIDE_EFFECT);
      
    /* function(:string) */
      ADD_EFUN("version", f_version,tFunc(tNone,tStr), OPT_TRY_OPTIMIZE);
    
      
    /* function(mixed,void|object:string) */
      ADD_EFUN("encode_value", f_encode_value,tFunc(tMix tOr(tVoid,tObj),tStr), OPT_TRY_OPTIMIZE);
      
    /* function(string,void|object:mixed) */
      ADD_EFUN("decode_value", f_decode_value,tFunc(tStr tOr(tVoid,tObj),tMix), OPT_TRY_OPTIMIZE);
      
    /* function(object,string:int) */
      ADD_EFUN("object_variablep", f_object_variablep,tFunc(tObj tStr,tInt), OPT_EXTERNAL_DEPEND);
    
      /* function(array(mapping(int:mixed)):array(int)) */
      ADD_FUNCTION("interleave_array",f_interleave_array,tFunc(tArr(tMap(tInt,tMix)),tArr(tInt)),OPT_TRY_OPTIMIZE);
      /* function(array,array:array(array)) */
      ADD_FUNCTION("diff",f_diff,tFunc(tArray tArray,tArr(tArray)),OPT_TRY_OPTIMIZE);
      /* function(array,array:array(int)) */
      ADD_FUNCTION("diff_longest_sequence",f_diff_longest_sequence,tFunc(tArray tArray,tArr(tInt)),OPT_TRY_OPTIMIZE);
      /* function(array,array:array(int)) */
      ADD_FUNCTION("diff_dyn_longest_sequence",f_diff_dyn_longest_sequence,tFunc(tArray tArray,tArr(tInt)),OPT_TRY_OPTIMIZE);
      /* function(array,array:array(array)) */
      ADD_FUNCTION("diff_compare_table",f_diff_compare_table,tFunc(tArray tArray,tArr(tArray)),OPT_TRY_OPTIMIZE);
      /* function(array:array(int)) */
      ADD_FUNCTION("longest_ordered_sequence",f_longest_ordered_sequence,tFunc(tArray,tArr(tInt)),0);
      /* function(array(mixed),array(mixed)...:array(mixed)) */
      ADD_FUNCTION("sort",f_sort,tFuncV(tArr(tMix),tArr(tMix),tArr(tMix)),OPT_SIDE_EFFECT);
      ADD_FUNCTION("string_count",f_string_count,tFunc(tString tString,tInt),OPT_TRY_OPTIMIZE);
    
    #define tMapStuff(IN,SUB,OUTFUN,OUTSET,OUTPROG,OUTMIX,OUTARR,OUTMAP) \
      tOr7( tFuncV(IN tFuncV(SUB,tMix,tSetvar(2,tMix)),tMix,OUTFUN), \
            tIfnot(tFuncV(IN tFunction,tMix,tMix), \
    	       tOr(tFuncV(IN tProgram, tMix, OUTPROG), \
    	       tFuncV(IN tObj, tMix, OUTMIX))), \
    	tFuncV(IN tSet(tMix),tMix,OUTSET), \
    	tFuncV(IN tMap(tMix, tSetvar(2,tMix)), tMix, OUTMAP), \
            tFuncV(IN tArray, tMix, OUTARR), \
            tFuncV(IN tInt0, tMix, OUTMIX), \
    	tFuncV(IN, tVoid, OUTMIX) )
    
      ADD_EFUN("map",f_map,
    	   tOr7( tMapStuff(tArr(tSetvar(1,tMix)),tVar(1),
    			   tArr(tVar(2)),
    			   tArr(tInt01),
    			   tArr(tObj),
    			   tArr(tMix),
    			   tArr(tArr(tMix)),
    			   tArr(tOr(tInt0,tVar(2)))),
    
    		 tMapStuff(tMap(tSetvar(3,tMix),tSetvar(1,tMix)),tVar(1),
    			   tMap(tVar(3),tVar(2)),
    			   tMap(tVar(3),tInt01),
    			   tMap(tVar(3),tObj),
    			   tMap(tVar(3),tMix),
    			   tMap(tVar(3),tArr(tMix)),
    			   tMap(tVar(3),tOr(tInt0,tVar(2)))),
    		
     		 tMapStuff(tSet(tSetvar(1,tMix)),tVar(1),
    			   tSet(tVar(2)),
    			   tSet(tInt01),
    			   tSet(tObj),
    			   tSet(tMix),
    			   tSet(tArr(tMix)),
    			   tSet(tOr(tInt0,tVar(2)))),
    
    		 tMapStuff(tOr(tProgram,tFunction),tMix,
    			   tMap(tStr,tVar(2)),
    			   tMap(tStr,tInt01),
    			   tMap(tStr,tObj),
    			   tMap(tStr,tMix),
    			   tMap(tStr,tArr(tMix)),
    			   tMap(tStr,tOr(tInt0,tVar(2)))),
    
    		 tOr4( tFuncV(tString tFuncV(tInt,tMix,tInt),tMix,tString), 
    		       tFuncV(tString tFuncV(tInt,tMix,tInt),tMix,tString),
    		       tFuncV(tString tSet(tMix),tMix,tString),
    		       tFuncV(tString tMap(tMix,tInt), tMix, tString) ),
    		 
    		 tFuncV(tArr(tStringIndicable) tString,tMix,tMix),
    
    		 tFuncV(tObj,tMix,tMix) ),
    	   OPT_TRY_OPTIMIZE);
      
      ADD_EFUN("filter",f_filter,
    	   tOr3( tFuncV(tSetvar(1,tOr4(tArray,tMapping,tMultiset,tString)),
    			tMixed,tVar(1)),
    		 tFuncV(tOr(tProgram,tFunction),tMixed,tMap(tString,tMix)),
    		 tFuncV(tObj,tMix,tMix) ) ,
    	   OPT_TRY_OPTIMIZE);
    
      ADD_EFUN("enumerate",f_enumerate,
    	   tOr8(tFunc(tIntPos,tArr(tInt)),
    		tFunc(tIntPos tInt,tArr(tInt)),
    		tFunc(tIntPos tInt tOr(tVoid,tInt),tArr(tInt)),
    		tFunc(tIntPos tFloat tOr3(tVoid,tInt,tFloat),tArr(tFloat)),
    		tFunc(tIntPos tOr(tInt,tFloat) tFloat,tArr(tFloat)),
    		tFunc(tIntPos tMix tObj,tArr(tVar(1))),
    		tFunc(tIntPos tObj tOr(tVoid,tMix),tArr(tVar(1))),
    		tFunc(tIntPos tMix tMix 
    		      tFuncV(,tMix,tSetvar(1,tMix)),tArr(tVar(1)))),
    	   OPT_TRY_OPTIMIZE);
    		
      ADD_FUNCTION("inherit_list",f_inherit_list,tFunc(tProgram,tArr(tProgram)),0);
      ADD_FUNCTION("program_implements",f_program_implements,tFunc(tProgram tProgram,tInt),0);
      ADD_FUNCTION("program_inherits",f_program_inherits,tFunc(tProgram tProgram,tInt),0);
      ADD_FUNCTION("program_defined",f_program_defined,tFunc(tProgram,tString),0);
      ADD_FUNCTION("function_defined",f_function_defined,tFunc(tFunction,tString),0);
      ADD_FUNCTION("string_width",f_string_width,tFunc(tString,tInt),0);
    
    #ifdef DEBUG_MALLOC
      
    /* function(void:void) */
      ADD_EFUN("_reset_dmalloc",f__reset_dmalloc,tFunc(tVoid,tVoid),OPT_SIDE_EFFECT);
      ADD_EFUN("_list_open_fds",f__list_open_fds,tFunc(tVoid,tVoid),OPT_SIDE_EFFECT);
    #endif
    #ifdef PIKE_DEBUG
      
    /* function(1=mixed:1) */
      ADD_EFUN("_locate_references",f__locate_references,tFunc(tSetvar(1,tMix),tVar(1)),OPT_SIDE_EFFECT);
      ADD_EFUN("_describe",f__describe,tFunc(tSetvar(1,tMix),tVar(1)),OPT_SIDE_EFFECT);
    #endif
    }