From a80a9c117f1bc4f26f067c6253170c14c63694ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fredrik=20H=C3=BCbinette=20=28Hubbe=29?= <hubbe@hubbe.net>
Date: Tue, 11 Feb 1997 00:08:54 -0800
Subject: [PATCH] files moved for new module system

Rev: src/modules/Gdbm/.cvsignore:1.1
Rev: src/modules/Gdbm/Makefile.in:1.1
Rev: src/modules/Gdbm/configure.in:1.1
Rev: src/modules/Gdbm/doc/gdbm:1.1
Rev: src/modules/Gdbm/gdbm_machine.h.in:1.1
Rev: src/modules/Gdbm/gdbmmod.c:1.1
Rev: src/modules/Gdbm/testsuite.in:1.1
Rev: src/modules/Gmp/.cvsignore:1.1
Rev: src/modules/Gmp/Makefile.in:1.1
Rev: src/modules/Gmp/configure.in:1.1
Rev: src/modules/Gmp/doc/mpz:1.1
Rev: src/modules/Gmp/gmp_machine.h.in:1.1
Rev: src/modules/Gmp/mpz_glue.c:1.1
Rev: src/modules/Gmp/testsuite.in:1.1
---
 .gitattributes                     |   2 +
 src/modules/Gdbm/.cvsignore        |  10 +
 src/modules/Gdbm/.gitignore        |  10 +
 src/modules/Gdbm/Makefile.in       |   7 +
 src/modules/Gdbm/configure.in      |  15 +
 src/modules/Gdbm/doc/gdbm          | 150 +++++++
 src/modules/Gdbm/gdbm_machine.h.in |  10 +
 src/modules/Gdbm/gdbmmod.c         | 358 ++++++++++++++++
 src/modules/Gdbm/testsuite.in      |  52 +++
 src/modules/Gmp/.cvsignore         |   9 +
 src/modules/Gmp/.gitignore         |   9 +
 src/modules/Gmp/Makefile.in        |   7 +
 src/modules/Gmp/configure.in       |  16 +
 src/modules/Gmp/doc/mpz            | 136 ++++++
 src/modules/Gmp/gmp_machine.h.in   |  10 +
 src/modules/Gmp/mpz_glue.c         | 666 +++++++++++++++++++++++++++++
 src/modules/Gmp/testsuite.in       |  57 +++
 17 files changed, 1524 insertions(+)
 create mode 100644 src/modules/Gdbm/.cvsignore
 create mode 100644 src/modules/Gdbm/.gitignore
 create mode 100644 src/modules/Gdbm/Makefile.in
 create mode 100644 src/modules/Gdbm/configure.in
 create mode 100644 src/modules/Gdbm/doc/gdbm
 create mode 100644 src/modules/Gdbm/gdbm_machine.h.in
 create mode 100644 src/modules/Gdbm/gdbmmod.c
 create mode 100644 src/modules/Gdbm/testsuite.in
 create mode 100644 src/modules/Gmp/.cvsignore
 create mode 100644 src/modules/Gmp/.gitignore
 create mode 100644 src/modules/Gmp/Makefile.in
 create mode 100644 src/modules/Gmp/configure.in
 create mode 100644 src/modules/Gmp/doc/mpz
 create mode 100644 src/modules/Gmp/gmp_machine.h.in
 create mode 100644 src/modules/Gmp/mpz_glue.c
 create mode 100644 src/modules/Gmp/testsuite.in

diff --git a/.gitattributes b/.gitattributes
index 5317cf7304..ad0703b1d1 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -20,6 +20,8 @@ testfont binary
 /src/lex.c foreign_ident
 /src/main.c foreign_ident
 /src/mapping.c foreign_ident
+/src/modules/Gdbm/gdbmmod.c foreign_ident
+/src/modules/Gmp/mpz_glue.c foreign_ident
 /src/modules/_Crypto/cbc.c foreign_ident
 /src/modules/_Crypto/crypto.c foreign_ident
 /src/modules/_Crypto/crypto.h foreign_ident
diff --git a/src/modules/Gdbm/.cvsignore b/src/modules/Gdbm/.cvsignore
new file mode 100644
index 0000000000..aad00c7c3e
--- /dev/null
+++ b/src/modules/Gdbm/.cvsignore
@@ -0,0 +1,10 @@
+
+.pure
+Makefile
+config.log
+config.status
+configure
+dependencies
+gdbm_machine.h
+linker_options
+stamp-h
diff --git a/src/modules/Gdbm/.gitignore b/src/modules/Gdbm/.gitignore
new file mode 100644
index 0000000000..0a44e55840
--- /dev/null
+++ b/src/modules/Gdbm/.gitignore
@@ -0,0 +1,10 @@
+
+/.pure
+/Makefile
+/config.log
+/config.status
+/configure
+/dependencies
+/gdbm_machine.h
+/linker_options
+/stamp-h
diff --git a/src/modules/Gdbm/Makefile.in b/src/modules/Gdbm/Makefile.in
new file mode 100644
index 0000000000..75379bf80d
--- /dev/null
+++ b/src/modules/Gdbm/Makefile.in
@@ -0,0 +1,7 @@
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+MODULE_LDFLAGS=@LIBS@
+OBJS=gdbmmod.o
+
+@static_module_makefile@
+@dependencies@
diff --git a/src/modules/Gdbm/configure.in b/src/modules/Gdbm/configure.in
new file mode 100644
index 0000000000..1ae3ef7fa6
--- /dev/null
+++ b/src/modules/Gdbm/configure.in
@@ -0,0 +1,15 @@
+AC_INIT(gdbmmod.c)
+AC_CONFIG_HEADER(gdbm_machine.h)
+
+AC_ARG_WITH(gdbm,    [  --with(out)-gdbm       GNU database manager support ],[],[with_gdbm=yes])
+
+sinclude(../module_configure.in)
+
+if test x$with_gdbm = xyes; then
+  AC_CHECK_HEADERS(gdbm.h)
+  AC_CHECK_LIB(gdbm, gdbm_open)
+fi
+
+AC_OUTPUT(Makefile,echo FOO >stamp-h )
+
+
diff --git a/src/modules/Gdbm/doc/gdbm b/src/modules/Gdbm/doc/gdbm
new file mode 100644
index 0000000000..ddf660a793
--- /dev/null
+++ b/src/modules/Gdbm/doc/gdbm
@@ -0,0 +1,150 @@
+NAME
+	/precompiled/gdbm - database interface
+
+DESCRIPTION
+	This is the an interface to the gdbm library. This module might or
+	might not be available in your Pike depending on weather gdbm was
+	available when Pike was compiled.
+
+	A gdbm database has essentially the same functionality as a mapping,
+	except the syntax is different, and it is located on disk, not in
+	memory. Each gdbm database is one file which contains a set of
+	key-value pairs. Both keys and values are strings and all keys are
+	unique.
+
+============================================================================
+NAME
+	create - open database
+
+SYNTAX
+	int gdbm->create();
+	or
+	int gdbm->create(string file);
+	or
+	int gdbm->create(string file, string mode);
+
+DESCRIPTION
+	Without arguments, this function does nothing. With one argument it
+	opens the given file as a gdbm database, if this fails for some
+	reason, an error will be generated. If a second argument is present,
+	it specifies how to open the database using one or more of the follow
+	flags in a string:
+
+	r	open database for reading
+	w	open database for writing
+	c	create database if it does not exist
+	t	overwrite existing database
+	f	fast mode
+
+	The fast mode prevents the database from syncronizing each change
+	in the database immediately. This is dangerous because the database
+	can be left in an unusable state if Pike is terminated abnormally.
+
+	The default mode is "rwc".
+
+NOTA BENE
+	The gdbm manual states that it is important that the database is
+	closed properly. Unfortunately this will not be the case if Pike
+	calls exit() or returns from main(). You should therefore make sure
+	you call close or destruct your gdbm objects when exiting your
+	program. This will probably be done automatically in the future.
+
+============================================================================
+NAME
+	close - close database
+
+SYNTAX
+	void gdbm->close();
+
+DESCRIPTION
+	This closes the database.
+
+============================================================================
+NAME
+	store - store a value in the database
+
+SYNTAX
+	int gdbm->store(string key, string data);
+
+DESCRIPTION
+	Associate the contents of 'data' with the key 'key'. If the key 'key'
+	already exists in the database the data for that key will be replaced.
+	If it does not exist it will be added. An error will be generated if
+	the database was not open for writing.
+
+============================================================================
+NAME
+	fetch - fetch a value from the databse
+
+SYNTAX
+	string gdbm->fetch(string key);
+
+DESCRIPTION
+	Return the data associated with the key 'key' in the database.
+	If there was no such key in the database, zero is returned.
+
+============================================================================
+NAME
+	delete - delete a value from the database
+
+SYNTAX
+	int gdbm->delete(string key);
+
+DESCRIPTION
+	Remove a key from the database. Note that no error will be generated
+	if the key does not exist.
+
+============================================================================
+NAME
+	firstkey - get first key in database
+
+SYNTAX
+	string gdbm->firstkey();
+
+DESCRIPTION
+	Return the first key in the database, this can be any key in the
+	database.
+
+============================================================================
+NAME
+	nextkey - get next key in database
+
+SYNTAX
+	string gdbm->nextkey(string key);
+
+DESCRIPTION
+	This returns the key in database that follows the key 'key' key.
+	This is of course used to iterate over all keys in the database.
+
+EXAMPLE
+	/* Write the contents of the database */
+	for(key=gdbm->firstkey(); k; k=gdbm->nextkey(k))
+	  write(k+":"+gdbm->fetch(k)+"\n");
+
+============================================================================
+NAME
+	reorganize - reorganize database
+
+SYNTAX
+	int gdbm->reorganize();
+
+DESCRIPTION
+	Deletions and insertions into the database can cause fragmentation
+	which will make the database bigger. This routine reorganizes the
+	contents to get rid of fragmentation. Note however that this function
+	can take a LOT of time to run.
+
+============================================================================
+NAME
+	sync - synchronize database
+
+SYNTAX
+	void gdbm->sync();
+
+DESCRIPTION
+	When opening the database with the 'f' flag writings to the database
+	can be cached in memory for a long time. Calling sync will write
+	all such caches to disk and not return until everything is stored
+	on the disk.
+
+============================================================================
diff --git a/src/modules/Gdbm/gdbm_machine.h.in b/src/modules/Gdbm/gdbm_machine.h.in
new file mode 100644
index 0000000000..e0768c6db7
--- /dev/null
+++ b/src/modules/Gdbm/gdbm_machine.h.in
@@ -0,0 +1,10 @@
+#ifndef GDBM_MACHINE_H
+#define GDBM_MACHINE_H
+
+/* Define this if you have <gdbm.h> */
+#undef HAVE_GDBM_H
+
+/* Define this if you have libgdbm */
+#undef HAVE_LIBGDBM
+
+#endif
diff --git a/src/modules/Gdbm/gdbmmod.c b/src/modules/Gdbm/gdbmmod.c
new file mode 100644
index 0000000000..ee01ec75c0
--- /dev/null
+++ b/src/modules/Gdbm/gdbmmod.c
@@ -0,0 +1,358 @@
+/*\
+||| 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: gdbmmod.c,v 1.1 1997/02/11 08:00:50 hubbe Exp $");
+#include "gdbm_machine.h"
+#include "types.h"
+#include "threads.h"
+
+/* Todo: make sure only one thread accesses the same gdbmmod */
+
+#if defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM)
+
+#include "interpret.h"
+#include "svalue.h"
+#include "stralloc.h"
+#include "array.h"
+#include "object.h"
+#include "macros.h"
+
+#include <gdbm.h>
+
+#ifdef _REENTRANT
+static MUTEX_T gdbm_lock;
+#endif  
+
+struct gdbm_glue
+{
+  GDBM_FILE dbf;
+};
+
+#define THIS ((struct gdbm_glue *)(fp->current_storage))
+
+static void do_free()
+{
+  if(THIS->dbf)
+  {
+    GDBM_FILE dbf;
+    dbf=THIS->dbf;
+    THIS->dbf=0;
+
+    THREADS_ALLOW();
+    mt_lock(& gdbm_lock);
+    gdbm_close(dbf);
+    mt_unlock(& gdbm_lock);
+    THREADS_DISALLOW();
+  }
+}
+
+static int fixmods(char *mods)
+{
+  int mode;
+  mode=0;
+  while(1)
+  {
+    switch(*(mods++))
+    {
+    case 0:
+      switch(mode & 15)
+      {
+      default: error("No mode given for gdbm->open()\n"); 
+      case 1|16:
+      case 1: mode=GDBM_READER; break;
+      case 3: mode=GDBM_WRITER; break;
+      case 3|16: mode=GDBM_WRITER | GDBM_FAST; break;
+      case 7: mode=GDBM_WRCREAT; break;
+      case 7|16: mode=GDBM_WRCREAT | GDBM_FAST; break;
+      case 15: mode=GDBM_NEWDB; break;
+      case 15|16: mode=GDBM_NEWDB | GDBM_FAST; break;
+      }
+      return mode;
+
+    case 'r': case 'R': mode|=1;  break;
+    case 'w': case 'W': mode|=3;  break;
+    case 'c': case 'C': mode|=7;  break;
+    case 't': case 'T': mode|=15; break;
+    case 'f': case 'F': mode|=16; break;
+
+    default:
+      error("Bad mode flag in gdbm->open.\n");
+    }
+  }
+}
+
+void gdbmmod_fatal(char *err)
+{
+  error("GDBM: %s\n",err);
+}
+
+static void gdbmmod_create(INT32 args)
+{
+  struct gdbm_glue *this=THIS;
+  do_free();
+  if(args)
+  {
+    GDBM_FILE tmp;
+    struct pike_string *tmp2;
+    int rwmode = GDBM_WRCREAT;
+
+    if(sp[-args].type != T_STRING)
+      error("Bad argument 1 to gdbm->create()\n");
+
+    if(args>1)
+    {
+      if(sp[1-args].type != T_STRING)
+	error("Bad argument 2 to gdbm->create()\n");
+
+      rwmode=fixmods(sp[1-args].u.string->str);
+    }
+
+    tmp2=sp[-args].u.string;
+
+    THREADS_ALLOW();
+    mt_lock(& gdbm_lock);
+    tmp=gdbm_open(tmp2->str, 512, rwmode, 00666, gdbmmod_fatal);
+    mt_unlock(& gdbm_lock);
+    THREADS_DISALLOW();
+
+    if(!fp->current_object->prog)
+    {
+      if(tmp) gdbm_close(tmp);
+      error("Object destructed in gdbm->open()n");
+    }
+    THIS->dbf=tmp;
+
+    pop_n_elems(args);
+    if(!THIS->dbf)
+      error("Failed to open GDBM database.\n");
+  }
+}
+
+#define STRING_TO_DATUM(dat, st) dat.dptr=st->str,dat.dsize=st->len;
+#define DATUM_TO_STRING(dat) make_shared_binary_string(dat.dptr, dat.dsize)
+
+static void gdbmmod_fetch(INT32 args)
+{
+  struct gdbm_glue *this=THIS;
+  datum key,ret;
+
+  if(!args)
+    error("Too few arguments to gdbm->fetch()\n");
+
+  if(sp[-args].type != T_STRING)
+    error("Bad argument 1 to gdbm->fetch()\n");
+
+  if(!THIS->dbf)
+    error("GDBM database not open.\n");
+
+  STRING_TO_DATUM(key, sp[-args].u.string);
+
+  THREADS_ALLOW();
+  mt_lock(& gdbm_lock);
+  ret=gdbm_fetch(this->dbf, key);
+  mt_unlock(& gdbm_lock);
+  THREADS_DISALLOW();
+
+  pop_n_elems(args);
+  if(ret.dptr)
+  {
+    push_string(DATUM_TO_STRING(ret));
+    free(ret.dptr);
+  }else{
+    push_int(0);
+  }
+}
+
+static void gdbmmod_delete(INT32 args)
+{
+  struct gdbm_glue *this=THIS;
+  datum key;
+  int ret;
+  if(!args)
+    error("Too few arguments to gdbm->delete()\n");
+
+  if(sp[-args].type != T_STRING)
+    error("Bad argument 1 to gdbm->delete()\n");
+
+  if(!this->dbf)
+    error("GDBM database not open.\n");
+
+  STRING_TO_DATUM(key, sp[-args].u.string);
+
+  THREADS_ALLOW();
+  mt_lock(& gdbm_lock);
+  ret=gdbm_delete(this->dbf, key);
+  mt_unlock(& gdbm_lock);
+  THREADS_DISALLOW();
+  
+  pop_n_elems(args);
+  push_int(0);
+}
+
+static void gdbmmod_firstkey(INT32 args)
+{
+  struct gdbm_glue *this=THIS;
+  datum ret;
+  pop_n_elems(args);
+
+  if(!this->dbf) error("GDBM database not open.\n");
+
+  THREADS_ALLOW();
+  mt_lock(& gdbm_lock);
+  ret=gdbm_firstkey(this->dbf);
+  mt_unlock(& gdbm_lock);
+  THREADS_DISALLOW();
+
+  if(ret.dptr)
+  {
+    push_string(DATUM_TO_STRING(ret));
+    free(ret.dptr);
+  }else{
+    push_int(0);
+  }
+}
+
+static void gdbmmod_nextkey(INT32 args)
+{
+  struct gdbm_glue *this=THIS;
+  datum key,ret;
+  if(!args)
+    error("Too few arguments to gdbm->nextkey()\n");
+
+  if(sp[-args].type != T_STRING)
+    error("Bad argument 1 to gdbm->nextkey()\n");
+
+  if(!THIS->dbf)
+    error("GDBM database not open.\n");
+
+  STRING_TO_DATUM(key, sp[-args].u.string);
+
+  THREADS_ALLOW();
+  mt_lock(& gdbm_lock);
+  ret=gdbm_nextkey(this->dbf, key);
+  mt_unlock(& gdbm_lock);
+  THREADS_DISALLOW();
+
+  pop_n_elems(args);
+  if(ret.dptr)
+  {
+    push_string(DATUM_TO_STRING(ret));
+    free(ret.dptr);
+  }else{
+    push_int(0);
+  }
+}
+
+static void gdbmmod_store(INT32 args)
+{
+  struct gdbm_glue *this=THIS;
+  datum key,data;
+  int ret;
+  if(args<2)
+    error("Too few arguments to gdbm->store()\n");
+
+  if(sp[-args].type != T_STRING)
+    error("Bad argument 1 to gdbm->store()\n");
+
+  if(sp[1-args].type != T_STRING)
+    error("Bad argument 2 to gdbm->store()\n");
+
+  if(!THIS->dbf)
+    error("GDBM database not open.\n");
+
+  STRING_TO_DATUM(key, sp[-args].u.string);
+  STRING_TO_DATUM(data, sp[1-args].u.string);
+
+  THREADS_ALLOW();
+  mt_lock(& gdbm_lock);
+  ret=gdbm_store(this->dbf, key, data, GDBM_REPLACE);
+  mt_unlock(& gdbm_lock);
+  THREADS_DISALLOW();
+
+  if(ret == -1)
+    error("GDBM database not open for writing.\n");
+
+  pop_n_elems(args);
+  push_int(ret == 0);
+}
+
+static void gdbmmod_reorganize(INT32 args)
+{
+  struct gdbm_glue *this=THIS;
+  int ret;
+  pop_n_elems(args);
+
+  if(!THIS->dbf) error("GDBM database not open.\n");
+  THREADS_ALLOW();
+  mt_lock(& gdbm_lock);
+  ret=gdbm_reorganize(this->dbf);
+  mt_unlock(& gdbm_lock);
+  THREADS_DISALLOW();
+  pop_n_elems(args);
+  push_int(ret);
+}
+
+static void gdbmmod_sync(INT32 args)
+{
+  struct gdbm_glue *this=THIS;
+  pop_n_elems(args);
+
+  if(!THIS->dbf) error("GDBM database not open.\n");
+  THREADS_ALLOW();
+  mt_lock(& gdbm_lock);
+  gdbm_sync(this->dbf);
+  mt_unlock(& gdbm_lock);
+  THREADS_DISALLOW();
+  push_int(0);
+}
+
+static void gdbmmod_close(INT32 args)
+{
+  pop_n_elems(args);
+
+  do_free(THIS->dbf);
+  push_int(0);
+}
+
+static void init_gdbm_glue(struct object *o)
+{
+  THIS->dbf=0;
+}
+
+static void exit_gdbm_glue(struct object *o)
+{
+  do_free();
+}
+
+#endif
+
+void pike_module_exit(void) {}
+
+void pike_module_init(void)
+{
+#if defined(HAVE_GDBM_H) && defined(HAVE_LIBGDBM)
+  start_new_program();
+  add_storage(sizeof(struct gdbm_glue));
+  
+  add_function("create",gdbmmod_create,"function(void|string,void|string:void)",0);
+
+  add_function("close",gdbmmod_close,"function(:void)",0);
+  add_function("store",gdbmmod_store,"function(string,string:int)",0);
+  add_function("`[]=",gdbmmod_store,"function(string,string:int)",0);
+  add_function("fetch",gdbmmod_fetch,"function(string:string)",0);
+  add_function("`[]",gdbmmod_fetch,"function(string:string)",0);
+  add_function("delete",gdbmmod_delete,"function(string:int)",0);
+  add_function("firstkey",gdbmmod_firstkey,"function(:string)",0);
+  add_function("nextkey",gdbmmod_nextkey,"function(string:string)",0);
+  add_function("reorganize",gdbmmod_reorganize,"function(:int)",0);
+  add_function("sync",gdbmmod_sync,"function(:void)",0);
+
+  set_init_callback(init_gdbm_glue);
+  set_exit_callback(exit_gdbm_glue);
+  end_class("gdbm",0);
+#endif
+}
+
diff --git a/src/modules/Gdbm/testsuite.in b/src/modules/Gdbm/testsuite.in
new file mode 100644
index 0000000000..e1ce5f879b
--- /dev/null
+++ b/src/modules/Gdbm/testsuite.in
@@ -0,0 +1,52 @@
+// gdbm
+cond( [[ master()->programs["/precompiled/gdbm"] ]],
+[[
+  define([[GDBM]],[[ (program)"/precompiled/gdbm" ]])
+  test_true(programp(GDBM))
+  test_do(destruct(clone(GDBM)))
+
+  define([[GDBMTESTS]],
+  [[
+    test_do(rm("test.gdbm"))
+    test_do(add_efun("GDBMBASE",clone(GDBM,"test.gdbm")))
+    test_true(file_stat("test.gdbm"))
+
+    GDBMNULLTEST
+    test_true(GDBMBASE->store("foo","bar"))
+    GDBMNULLTEST
+    test_equal(GDBMBASE->fetch("foo"),"bar")
+    test_do([[int e; for(e=0;e<100;e++) GDBMBASE->store("x"+e,"y"+e)]])
+    GDBMNULLTEST
+    test_any(int e; for(e=0;e<100;e++) if(GDBMBASE->fetch("x"+e)!="y"+e) return e; return -1,-1)
+    GDBMNULLTEST
+    test_true(GDBMBASE->store(sprintf("%'23'100000s","") , sprintf("%'32'100000s","")))
+    test_true(GDBMBASE->fetch(sprintf("%'23'100000s",""))==sprintf("%'32'100000s",""))
+    GDBMNULLTEST
+    test_equal(GDBMBASE->fetch("foo"),"bar")
+    test_any(int e; for(e=0;e<100;e++) if(GDBMBASE->fetch("x"+e)!="y"+e) return e; return -1,-1)
+    test_true(GDBMBASE->fetch(sprintf("%'23'100000s",""))==sprintf("%'32'100000s",""))
+    test_any(int e; string k; for(k=GDBMBASE->firstkey();k;k=GDBMBASE->nextkey(k)) e++; return e,102)
+
+    test_do(GDBMBASE->sync())
+    test_do(GDBMBASE->reorganize())
+    test_do(GDBMBASE->close())
+  ]])
+
+  define([[GDBMNULLTEST]],[[
+    test_false(GDBMBASE->fetch("slakjdfasdf"))
+  ]])
+
+  GDBMTESTS
+
+  define([[GDBMNULLTEST]],[[
+    test_false(GDBMBASE->fetch("slakjdfasdf"))
+    test_do(GDBMBASE->reorganize())
+    test_do(GDBMBASE->sync())
+    test_do(GDBMBASE->close())
+    test_do(GDBMBASE->create("test.gdbm"))
+  ]])
+
+  GDBMTESTS
+
+  test_do(rm("test.gdbm"))
+]])
diff --git a/src/modules/Gmp/.cvsignore b/src/modules/Gmp/.cvsignore
new file mode 100644
index 0000000000..86d2c454d7
--- /dev/null
+++ b/src/modules/Gmp/.cvsignore
@@ -0,0 +1,9 @@
+.pure
+Makefile
+config.log
+config.status
+configure
+dependencies
+gmp_machine.h
+linker_options
+stamp-h
diff --git a/src/modules/Gmp/.gitignore b/src/modules/Gmp/.gitignore
new file mode 100644
index 0000000000..f201fb660d
--- /dev/null
+++ b/src/modules/Gmp/.gitignore
@@ -0,0 +1,9 @@
+/.pure
+/Makefile
+/config.log
+/config.status
+/configure
+/dependencies
+/gmp_machine.h
+/linker_options
+/stamp-h
diff --git a/src/modules/Gmp/Makefile.in b/src/modules/Gmp/Makefile.in
new file mode 100644
index 0000000000..a7c8bf3359
--- /dev/null
+++ b/src/modules/Gmp/Makefile.in
@@ -0,0 +1,7 @@
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+MODULE_LDFLAGS=@LIBS@
+OBJS=mpz_glue.o
+
+@static_module_makefile@
+@dependencies@
diff --git a/src/modules/Gmp/configure.in b/src/modules/Gmp/configure.in
new file mode 100644
index 0000000000..0df3eec924
--- /dev/null
+++ b/src/modules/Gmp/configure.in
@@ -0,0 +1,16 @@
+AC_INIT(mpz_glue.c)
+AC_CONFIG_HEADER(gmp_machine.h)
+AC_ARG_WITH(gmp,     [  --with(out)-gmp        Support bignums],[],[with_gmp=yes])
+
+sinclude(../module_configure.in)
+
+if test x$with_gmp = xyes ; then
+  AC_CHECK_HEADERS(gmp.h)
+  if test $ac_cv_header_gmp_h = yes ; then
+    AC_CHECK_LIB(gmp, mpz_set_d)
+  fi
+fi
+
+AC_OUTPUT(Makefile,echo FOO >stamp-h )
+
+
diff --git a/src/modules/Gmp/doc/mpz b/src/modules/Gmp/doc/mpz
new file mode 100644
index 0000000000..2b4c0bc67e
--- /dev/null
+++ b/src/modules/Gmp/doc/mpz
@@ -0,0 +1,136 @@
+NAME
+	/precompiled/mpz - bignum program
+
+DESCRIPTION
+	/precompiled/mpz is a builtin program written in C. It implements
+	large, very large integers. In fact, the only limitation on these
+	integers is the available memory.
+
+	The mpz object implements all the normal integer operations.
+	(except xor) There are also some extra operators:
+
+NOTA BENE
+	This module is only available if libgmp.a was available and
+	found when Pike was compiled.
+
+============================================================================
+NAME
+	create - initialize a bignum
+
+SYNTAX
+	object Mpz();
+	or
+	object Mpz(int|object|float i);
+	or
+	object Mpz(string digits, int base);
+	
+
+DESCRIPTION
+	When cloning an mpz it is by default initalized to zero. However,
+	you can give a second argument to clone to initialize the new
+	object to that value. The argument can be an int, float another
+	mpz object, or a string containing an ascii number. You can also
+	give the number in the string in another base by specifying the
+	base as a second argument. Valid bases are 2-36 and 256.
+
+SEE ALSO
+	builtin/clone
+
+============================================================================
+NAME
+	powm - raise and modulo
+
+SYNTAX
+	object mpz->powm(int|string|float|object a,int|string|float|object b);
+
+DESCRIPTION
+	This function returns ( mpz ** a ) % b
+
+============================================================================
+NAME
+	sqrt - square root
+
+SYNTAX
+	object mpz->sqrt();
+
+DESCRIPTION
+	This function return the the truncated integer part of the square
+	root of the value of mpz. 
+
+============================================================================
+NAME
+	probably_prime_p - is this number a prime?
+
+SYNTAX
+	int mpz->probably_prime_p();
+
+DESCRIPTION
+	This function returns 1 if mpz is a prime, and 0 most of the time
+	if it is not.
+
+============================================================================
+NAME
+	gcd - greatest common divisor
+
+SYNTAX
+	object mpz->gcd(object|int|float|string arg)
+
+DESCRIPTION
+	This function returns the greatest common divisor for arg and mpz.
+
+============================================================================
+NAME
+	cast - cast to other type
+
+SYNTAX
+	object mpz->gcd( "string" | "int" | "float" );
+	or
+	(string) mpz
+	or
+	(int) mpz
+	or
+	(float) mpz
+
+
+DESCRIPTION
+	This function converts an mpz to a string, int or float. This is
+	nessesary when you want to view, store or use the result of an mpz
+	calculation.
+
+SEE ALSO
+	cast
+
+============================================================================
+NAME
+	digits - convert mpz to a string
+
+SYNTAX
+	string mpz->digits();
+	or
+	string mpz->digits(int base);
+
+DESCRIPTION
+	This function converts an mpz to a string. If a base is given the
+	number will be represented in that base. Valid bases are 2-36 and
+	256. The default base is 10.
+
+SEE ALSO
+	mpz->cast
+
+============================================================================
+NAME
+	size - how long is a number
+
+SYNTAX
+	string mpz->size();
+	or
+	string mpz->size(int base);
+
+DESCRIPTION
+	This function returns how long the mpz would be represented in the
+	specified base. The default base is 2.
+
+SEE ALSO
+	mpz->digits
+
+============================================================================
diff --git a/src/modules/Gmp/gmp_machine.h.in b/src/modules/Gmp/gmp_machine.h.in
new file mode 100644
index 0000000000..f304d6b576
--- /dev/null
+++ b/src/modules/Gmp/gmp_machine.h.in
@@ -0,0 +1,10 @@
+#ifndef GMP_MACHINE_H
+#define GMP_MACHINE_H
+
+/* Define this if you have <gmp.h> */
+#undef HAVE_GMP_H
+
+/* Define this if you have -lgmp */
+#undef HAVE_LIBGMP
+
+#endif
diff --git a/src/modules/Gmp/mpz_glue.c b/src/modules/Gmp/mpz_glue.c
new file mode 100644
index 0000000000..0c77e0e96f
--- /dev/null
+++ b/src/modules/Gmp/mpz_glue.c
@@ -0,0 +1,666 @@
+/*\
+||| 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: mpz_glue.c,v 1.1 1997/02/11 08:08:40 hubbe Exp $");
+#include "gmp_machine.h"
+#include "types.h"
+
+#if !defined(HAVE_LIBGMP)
+#undef HAVE_GMP_H
+#endif
+
+#ifdef HAVE_GMP_H
+
+#include "interpret.h"
+#include "svalue.h"
+#include "stralloc.h"
+#include "array.h"
+#include "macros.h"
+#include "program.h"
+#include "stralloc.h"
+#include "object.h"
+#include "pike_types.h"
+
+#include <gmp.h>
+#include <assert.h>
+
+#define THIS ((MP_INT *)(fp->current_storage))
+#define OBTOMPZ(o) ((MP_INT *)(o->storage))
+
+static struct program *mpzmod_program;
+
+static void get_mpz_from_digits(MP_INT *tmp,
+				struct pike_string *digits,
+				int base)
+{
+  if(!base || ((base >= 2) && (base <= 36)))
+  {
+    if (mpz_set_str(tmp, digits->str, base))
+      error("invalid digits, cannot convert to mpz");
+  }
+  else if(base == 256)
+  {
+    
+    INT8 i;
+    mpz_t digit;
+    
+    mpz_init(digit);
+    mpz_set_ui(tmp, 0);
+    for (i = 0; i < digits->len; i++)
+    {
+      mpz_set_ui(digit, EXTRACT_UCHAR(digits->str + i));
+      mpz_mul_2exp(digit, digit, (digits->len - i - 1) * 8);
+      mpz_ior(tmp, tmp, digit);
+    }
+  }
+  else
+  {
+    error("invalid base.\n");
+  }
+}
+
+static void get_new_mpz(MP_INT *tmp, struct svalue *s)
+{
+  switch(s->type)
+    {
+    case T_INT:
+      mpz_set_si(tmp, (signed long int) s->u.integer);
+      break;
+    
+    case T_FLOAT:
+      mpz_set_d(tmp, (double) s->u.float_number);
+      break;
+
+    case T_OBJECT:
+      if(s->u.object->prog != mpzmod_program)
+	error("Wrong type of object, cannot convert to mpz.\n");
+
+      mpz_set(tmp, OBTOMPZ(s->u.object));
+      break;
+#if 0    
+    case T_STRING:
+      mpz_set_str(tmp, s->u.string->str, 0);
+      break;
+
+    case T_ARRAY:   /* Experimental */
+      if ( (s->u.array->size != 2)
+	   || (ITEM(s->u.array)[0].type != T_STRING)
+	   || (ITEM(s->u.array)[1].type != T_INT))
+	error("cannot convert array to mpz.\n");
+      get_mpz_from_digits(tmp, ITEM(s->u.array)[0].u.string,
+			  ITEM(s->u.array)[1]);
+      break;
+#endif
+    default:
+      error("cannot convert argument to mpz.\n");
+    }
+}
+
+static void mpzmod_create(INT32 args)
+{
+  switch(args)
+  {
+  case 1:
+    if(sp[-args].type == T_STRING)
+      get_mpz_from_digits(THIS, sp[-args].u.string, 0);
+    else
+      get_new_mpz(THIS, sp-args);
+    break;
+
+  case 2: /* Args are string of digits and integer base */
+    if(sp[-args].type != T_STRING)
+      error("bad argument 1 for Mpz->create()");
+
+    if (sp[1-args].type != T_INT)
+      error("wrong type for base in Mpz->create()");
+
+    get_mpz_from_digits(THIS, sp[-args].u.string, sp[1-args].u.integer);
+    break;
+
+  default:
+    error("Too many arguments to Mpz->create()\n");
+
+  case 0:
+    break;	/* Needed by AIX cc */
+  }
+  pop_n_elems(args);
+}
+
+static void mpzmod_get_int(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(mpz_get_si(THIS));
+}
+
+static void mpzmod_get_float(INT32 args)
+{
+  pop_n_elems(args);
+  push_float((float)mpz_get_d(THIS));
+}
+
+static struct pike_string *low_get_digits(MP_INT *mpz, int base)
+{
+  struct pike_string *s;
+  INT32 len;
+  
+  if ( (base >= 2) && (base <= 36))
+  {
+    len = mpz_sizeinbase(mpz, base) + 2;
+    s = begin_shared_string(len);
+    mpz_get_str(s->str, base, mpz);
+    /* Find NULL character */
+    len-=4;
+    if (len < 0) len = 0;
+    while(s->str[len]) len++;
+    s->len=len;
+    s=end_shared_string(s);
+  }
+  else if (base == 256)
+  {
+    INT32 i;
+    mpz_t tmp;
+    
+    if (mpz_sgn(mpz) < 0)
+      error("only non-negative numbers can be converted to base 256.\n");
+    len = (mpz_sizeinbase(mpz, 2) + 7) / 8;
+    s = begin_shared_string(len);
+    mpz_init_set(tmp, mpz);
+    i = len;
+    while(i--)
+    {
+      s->str[i] = mpz_get_ui(tmp) & 0xff;
+      mpz_tdiv_q_2exp(tmp, tmp, 8);
+    }
+    assert(mpz_sgn(tmp) == 0);
+    mpz_clear(tmp);
+    s = end_shared_string(s);
+  }
+  else
+  {
+    error("invalid base.\n");
+    return 0; /* Make GCC happy */
+  }
+
+  return s;
+}
+
+static void mpzmod_get_string(INT32 args)
+{
+  pop_n_elems(args);
+  push_string(low_get_digits(THIS, 10));
+}
+
+static void mpzmod_digits(INT32 args)
+{
+  INT32 base;
+  if (!args)
+  {
+    base = 10;
+  }
+  else
+  {
+    if (sp[-args].type != T_INT)
+      error("Bad argument 1 for Mpz->digits().\n");
+    base = sp[-args].u.integer;
+  }
+  pop_n_elems(args);
+
+  push_string(low_get_digits(THIS, base));
+}
+
+static void mpzmod_size(INT32 args)
+{
+  int base;
+  if (!args)
+  {
+    /* Default is number of bits */
+    base = 2;
+  }
+  else
+  {
+    if (sp[-args].type != T_INT)
+      error("bad argument 1 for Mpz->size()\n");
+    base = sp[-args].u.integer;
+    if ((base != 256) && ((base < 2) || (base > 36)))
+      error("invalid base\n");
+  }
+  pop_n_elems(args);
+
+  if (base == 256)
+    push_int((mpz_sizeinbase(THIS, 2) + 7) / 8);
+  else
+    push_int(mpz_sizeinbase(THIS, base));
+}
+
+static void mpzmod_cast(INT32 args)
+{
+  if(args < 1)
+    error("mpz->cast() called without arguments.\n");
+  if(sp[-args].type != T_STRING)
+    error("Bad argument 1 to mpz->cast().\n");
+
+  switch(sp[-args].u.string->str[0])
+  {
+  case 'i':
+    if(!strcmp(sp[-args].u.string->str, "int"))
+    {
+      mpzmod_get_int(args);
+      return;
+    }
+    break;
+
+  case 's':
+    if(!strcmp(sp[-args].u.string->str, "string"))
+    {
+      mpzmod_get_string(args);
+      return;
+    }
+    break;
+
+  case 'f':
+    if(!strcmp(sp[-args].u.string->str, "float"))
+    {
+      mpzmod_get_float(args);
+      return;
+    }
+    break;
+
+  case 'o':
+    if(!strcmp(sp[-args].u.string->str, "object"))
+    {
+      pop_n_elems(args);
+      push_object(this_object());
+    }
+    break;
+
+  case 'm':
+    if(!strcmp(sp[-args].u.string->str, "mixed"))
+    {
+      pop_n_elems(args);
+      push_object(this_object());
+    }
+    break;
+    
+  }
+
+  error("mpz->cast() to other type than string, int or float.\n");
+}
+
+static MP_INT *get_mpz(struct svalue *s)
+{
+  struct object *o;
+  switch(s->type)
+  {
+  default:
+    error("Wrong type of object, cannot convert to mpz.\n");
+    return 0;
+
+  case T_INT:
+  case T_FLOAT:
+#if 0
+  case T_STRING:
+  case T_ARRAY:
+#endif
+    o=clone(mpzmod_program,0);
+    get_new_mpz(OBTOMPZ(o), s);
+    free_svalue(s);
+    s->u.object=o;
+    s->type=T_OBJECT;
+    return (MP_INT *)o->storage;
+    
+  case T_OBJECT:
+    if(s->u.object->prog != mpzmod_program)
+      error("Wrong type of object, cannot convert to mpz.\n");
+
+    return (MP_INT *)s->u.object->storage;
+  }
+}
+
+/* These two functions are here so we can allocate temporary
+ * objects without having to worry about them leaking in
+ * case of errors..
+ */
+static struct object *temporary;
+MP_INT *get_tmp()
+{
+  if(!temporary)
+    temporary=clone(mpzmod_program,0);
+
+  return (MP_INT *)temporary->storage;
+}
+
+static void return_temporary(INT32 args)
+{
+  pop_n_elems(args);
+  push_object(temporary);
+  temporary=0;
+}
+
+#define BINFUN(name, fun)				\
+static void name(INT32 args)				\
+{							\
+  INT32 e;						\
+  MP_INT *tmp=get_tmp();				\
+  mpz_set(tmp, THIS);					\
+  for(e=0;e<args;e++)					\
+    fun(tmp, tmp, get_mpz(sp+e-args));			\
+  return_temporary(args);				\
+}
+
+BINFUN(mpzmod_add,mpz_add)
+BINFUN(mpzmod_mul,mpz_mul)
+BINFUN(mpzmod_gcd,mpz_gcd)
+
+static void mpzmod_sub(INT32 args)
+{
+  INT32 e;
+  MP_INT *tmp=get_tmp();
+  mpz_set(tmp, THIS);
+
+  if(args)
+  {
+    for(e=0;e<args;e++)
+      mpz_sub(tmp, tmp, get_mpz(sp+e-args));
+  }else{
+    mpz_neg(tmp, tmp);
+  }
+
+  return_temporary(args);
+}
+
+static void mpzmod_div(INT32 args)
+{
+  INT32 e;
+  MP_INT *tmp=get_tmp();
+  mpz_set(tmp, THIS);
+
+  for(e=0;e<args;e++)
+  {
+    MP_INT *tmp2;
+    tmp2=get_mpz(sp+e-args);
+    if(!mpz_sgn(tmp2))
+      error("Division by zero.\n");
+    mpz_tdiv_q(tmp, tmp, tmp2);
+  }
+  return_temporary(args);
+}
+
+static void mpzmod_mod(INT32 args)
+{
+  INT32 e;
+  MP_INT *tmp=get_tmp();
+  mpz_set(tmp, THIS);
+
+  for(e=0;e<args;e++)
+  {
+    MP_INT *tmp2;
+    tmp2=get_mpz(sp+e-args);
+    if(!mpz_sgn(tmp2))
+      error("Modulo by zero.\n");
+    mpz_tdiv_r(tmp, tmp, tmp2);
+  }
+  return_temporary(args);
+}
+
+static void mpzmod_gcdext(INT32 args)
+{
+  struct object *g, *s, *t;
+  MP_INT *a;
+
+  a = get_mpz(sp-args);
+  
+  g = clone(mpzmod_program, 0);
+  s = clone(mpzmod_program, 0);
+  t = clone(mpzmod_program, 0);
+
+  mpz_gcdext(OBTOMPZ(g), OBTOMPZ(s), OBTOMPZ(t), THIS, a);
+  pop_n_elems(args);
+  push_object(g); push_object(s); push_object(t);
+  f_aggregate(3);
+}
+
+static void mpzmod_gcdext2(INT32 args)
+{
+  struct object *g, *s;
+  MP_INT *a;
+
+  a = get_mpz(sp-args);
+  
+  g = clone(mpzmod_program, 0);
+  s = clone(mpzmod_program, 0);
+
+  mpz_gcdext(OBTOMPZ(g), OBTOMPZ(s), NULL, THIS, a);
+  pop_n_elems(args);
+  push_object(g); push_object(s); 
+  f_aggregate(2);
+}
+
+static void mpzmod_invert(INT32 args)
+{
+  MP_INT *modulo;
+  MP_INT *tmp;
+  
+  modulo = get_mpz(sp-args);
+  if (!mpz_sgn(modulo))
+    error("divide by zero");
+  tmp = get_tmp();
+  if (mpz_invert(tmp, THIS, modulo) == 0)
+    error("not invertible");
+  return_temporary(args);
+}
+
+BINFUN(mpzmod_and,mpz_and)
+BINFUN(mpzmod_or,mpz_ior)
+
+static void mpzmod_compl(INT32 args)
+{
+  struct object *o;
+  pop_n_elems(args);
+  o=clone(mpzmod_program,0);
+  push_object(o);
+  mpz_com(OBTOMPZ(o), THIS);
+}
+
+
+#define CMPFUN(name,cmp)				\
+static void name(INT32 args)				\
+{							\
+  INT32 i;						\
+  if(!args) error("Comparison with one argument?\n");	\
+  i=mpz_cmp(THIS, get_mpz(sp-args)) cmp 0;		\
+  pop_n_elems(args);					\
+  push_int(i);						\
+}
+
+CMPFUN(mpzmod_gt, >)
+CMPFUN(mpzmod_lt, <)
+CMPFUN(mpzmod_ge, >=)
+CMPFUN(mpzmod_le, <=)
+CMPFUN(mpzmod_eq, ==)
+CMPFUN(mpzmod_nq, !=)
+
+static void mpzmod_probably_prime_p(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(mpz_probab_prime_p(THIS, 25));
+}
+
+static void mpzmod_sgn(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(mpz_sgn(THIS));
+}
+
+
+static void mpzmod_sqrt(INT32 args)
+{
+  struct object *o;
+  pop_n_elems(args);
+  if(mpz_sgn(THIS)<0)
+    error("mpz->sqrt() on negative number.\n");
+
+  o=clone(mpzmod_program,0);
+  push_object(o);
+  mpz_sqrt(OBTOMPZ(o), THIS);
+}
+
+static void mpzmod_sqrtrem(INT32 args)
+{
+  struct object *root, *rem;
+  
+  pop_n_elems(args);
+  if(mpz_sgn(THIS)<0)
+    error("mpz->sqrtrem() on negative number.\n");
+
+  root = clone(mpzmod_program,0);
+  rem = clone(mpzmod_program,0);
+  mpz_sqrtrem(OBTOMPZ(root), OBTOMPZ(rem), THIS);
+  push_object(root); push_object(rem);
+  f_aggregate(2);
+}
+
+static void mpzmod_lsh(INT32 args)
+{
+  MP_INT *tmp;
+  pop_n_elems(args-1);
+  push_string(int_type_string);
+  int_type_string->refs++;
+  f_cast(2);
+  tmp=get_tmp();
+  if(sp[-1].u.integer < 0)
+    error("mpz->lsh on negative number.\n");
+  mpz_mul_2exp(tmp, THIS, sp[-1].u.integer);
+  return_temporary(1);
+}
+
+static void mpzmod_rsh(INT32 args)
+{
+  MP_INT *tmp;
+  pop_n_elems(args-1);
+  push_string(int_type_string);
+  int_type_string->refs++;
+  f_cast(2);
+  tmp=get_tmp();
+  mpz_set_ui(tmp,1);
+  mpz_mul_2exp(tmp, tmp, sp[-1].u.integer);
+  mpz_tdiv_q(tmp, THIS, tmp);
+  return_temporary(1);
+}
+
+static void mpzmod_powm(INT32 args)
+{
+  MP_INT *tmp;
+  if(args < 2)
+    error("Too few arguments to mpzmod->powm()\n");
+
+  tmp=get_tmp();
+  mpz_powm(tmp, THIS, get_mpz(sp-args), get_mpz(sp+1-args));
+  return_temporary(args);
+}
+
+static void mpzmod_not(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(!mpz_sgn(THIS));
+}
+
+static void init_mpz_glue(struct object *o)
+{
+  mpz_init(THIS);
+}
+
+static void exit_mpz_glue(struct object *o)
+{
+  mpz_clear(THIS);
+}
+#endif
+
+void pike_module_exit(void)
+{
+#ifdef HAVE_GMP_H
+  if(temporary) free_object(temporary);
+  free_program(mpzmod_program);
+#endif
+}
+
+void pike_module_init(void)
+{
+#ifdef HAVE_GMP_H
+  start_new_program();
+  add_storage(sizeof(MP_INT));
+  
+  add_function("create", mpzmod_create,
+  "function(void|string|int|float|object:void)"
+  "|function(string,int:void)", 0);
+
+#define MPZ_ARG_TYPE "int|float|object"
+#define MPZ_BINOP_TYPE ("function(" MPZ_ARG_TYPE "...:object)")
+
+  add_function("`+",mpzmod_add,MPZ_BINOP_TYPE,0);
+  add_function("`-",mpzmod_sub,MPZ_BINOP_TYPE,0);
+  add_function("`*",mpzmod_mul,MPZ_BINOP_TYPE,0);
+  add_function("`/",mpzmod_div,MPZ_BINOP_TYPE,0);
+  add_function("`%",mpzmod_mod,MPZ_BINOP_TYPE,0);
+  add_function("`&",mpzmod_and,MPZ_BINOP_TYPE,0);
+  add_function("`|",mpzmod_or,MPZ_BINOP_TYPE,0);
+
+#define MPZ_SHIFT_TYPE "function(int|float|object:object)"
+  add_function("`<<",mpzmod_lsh,MPZ_SHIFT_TYPE,0);
+  add_function("`>>",mpzmod_rsh,MPZ_SHIFT_TYPE,0);
+
+#define MPZ_CMPOP_TYPE ("function(" MPZ_ARG_TYPE ":int)")
+
+  add_function("`>", mpzmod_gt,MPZ_CMPOP_TYPE,0);
+  add_function("`<", mpzmod_lt,MPZ_CMPOP_TYPE,0);
+  add_function("`>=",mpzmod_ge,MPZ_CMPOP_TYPE,0);
+  add_function("`<=",mpzmod_le,MPZ_CMPOP_TYPE,0);
+
+  add_function("`==",mpzmod_le,MPZ_CMPOP_TYPE,0);
+  add_function("`!=",mpzmod_le,MPZ_CMPOP_TYPE,0);
+
+  add_function("`!",mpzmod_not,"function(:int)",0);
+
+  add_function("__hash",mpzmod_get_int,"function(:int)",0);
+  add_function("cast",mpzmod_cast,"function(string:mixed)",0);
+
+  add_function("digits", mpzmod_digits, "function(void|int:string)", 0);
+  add_function("size", mpzmod_size, "function(void|int:int)", 0);
+
+  add_function("cast_to_int",mpzmod_get_int,"function(:int)",0);
+  add_function("cast_to_string",mpzmod_get_string,"function(:string)",0);
+  add_function("cast_to_float",mpzmod_get_float,"function(:float)",0);
+
+  add_function("probably_prime_p",mpzmod_probably_prime_p,"function(:int)",0);
+  add_function("gcd",mpzmod_gcd, MPZ_BINOP_TYPE, 0);
+  add_function("gcdext", mpzmod_gcdext,
+  "function(" MPZ_ARG_TYPE "," MPZ_ARG_TYPE ":array(object))", 0);
+  add_function("gcdext2", mpzmod_gcdext2,
+  "function(" MPZ_ARG_TYPE "," MPZ_ARG_TYPE ":array(object))", 0);
+  add_function("invert", mpzmod_invert,
+  "function(" MPZ_ARG_TYPE ":object)", 0);
+
+  add_function("sqrt",mpzmod_gcd,"function(:object)",0);
+  add_function("sqrtrem", mpzmod_sqrtrem, "function(:array(object))", 0);
+  add_function("`~",mpzmod_gcd,"function(:object)",0);
+  add_function("powm",mpzmod_powm,
+  "function(" MPZ_ARG_TYPE "," MPZ_ARG_TYPE ":object)", 0);
+#if 0
+  /* These are not implemented yet */
+  add_function("squarep", mpzmod_squarep, "function(:int)", 0);
+  add_function("divmod", mpzmod_divmod, "function(" MPZ_ARG_TYPE ":array(object))", 0);
+  add_function("pow", mpzmod_pow, "function(int:object)", 0);
+  add_function("divm", mpzmod_divm, "function(string|int|float|object, string|int|float|object:object)", 0);
+  add_efun("mpz_pow", mpz_pow, "function(int, int)", 0);
+  add_efun("mpz_fac", mpz_fac, "function(int|object:object)", 0);
+#endif
+  set_init_callback(init_mpz_glue);
+  set_exit_callback(exit_mpz_glue);
+
+  mpzmod_program=end_program();
+  add_program_constant("mpz",mpzmod_program,0);
+#endif
+}
+
diff --git a/src/modules/Gmp/testsuite.in b/src/modules/Gmp/testsuite.in
new file mode 100644
index 0000000000..39fd5d4ac9
--- /dev/null
+++ b/src/modules/Gmp/testsuite.in
@@ -0,0 +1,57 @@
+// mpz
+cond( [[ master()->programs["/precompiled/mpz"] ]],
+[[
+  define([[MPZ]],[[(program)"/precompiled/mpz"]])
+  test_true(programp(MPZ))
+  test_false(clone(MPZ))
+  test_do(destruct(clone(MPZ)))
+
+  test_eq(Mpz(10),10)
+  test_eq(Mpz("10"),10)
+  test_eq(Mpz(10.0),10)
+  test_eq(Mpz("10",8),8)
+  test_eq(Mpz("0",256),'0')
+
+  test_eq(clone(MPZ,99)+1,100)
+  test_eq(clone(MPZ,100)*10,1000)
+  test_eq(clone(MPZ,"100")*10.0,1000)
+  test_eq(clone(MPZ,100.0)*clone(MPZ,3),300)
+  test_eq(clone(MPZ,100.0)/4,25)
+  test_eq(clone(MPZ,42)%10,2)
+  test_eq(clone(MPZ,10)<<1,20)
+  test_eq(clone(MPZ,10)>>1,5)
+  test_eq(clone(MPZ,66)+11,77)
+  test_eq(clone(MPZ,66)-11,55)
+  test_eq(clone(MPZ,17)&18,16)
+  test_eq(clone(MPZ,17)|7,31)
+  test_eq(-clone(MPZ,17),17)
+  test_eq((~clone(MPZ,17)) & 255,0xf0)
+  test_true(stringp((string)clone(MPZ,17)))
+  test_eq((string)clone(MPZ,17),"17")
+  test_true(intp((int)clone(MPZ,17)))
+  test_eq((int)clone(MPZ,17),17)
+  test_false(clone(MPZ,0))
+  test_true(clone(MPZ,1))
+  test_eq(clone(MPZ,17)->powm(2,4711),290)
+
+  define([[mpz_test_cmp]],[[
+    test_cmp(clone(MPZ,$1), $2)
+    test_cmp($1, clone(MPZ,$2))
+    test_cmp(clone(MPZ, $1), clone(MPZ, $2))
+  ]])
+
+  define([[mpz_test_type1]],[[
+    mpz_test_cmp($1,$2)
+    mpz_test_cmp($1.0,$2)
+  ]])
+
+  define([[mpz_test_type2]],[[
+    mpz_test_type1($1,$2)
+    mpz_test_type1($1,$2.0)
+  ]])
+
+  mpz_test_type2(1,2)
+  mpz_test_type2(-2,1)
+
+]]);
+
-- 
GitLab