[Python-checkins] python/nondist/sandbox/twister MersenneTwister.c,1.4,1.5 random.py,1.5,1.6 test_twister.py,1.3,1.4 todo.txt,1.1,1.2

rhettinger@users.sourceforge.net rhettinger@users.sourceforge.net
Sun, 22 Dec 2002 20:31:38 -0800


Update of /cvsroot/python/python/nondist/sandbox/twister
In directory sc8-pr-cvs1:/tmp/cvs-serv24008

Modified Files:
	MersenneTwister.c random.py test_twister.py todo.txt 
Log Message:
Random.seed() now takes PyLong as well as PyInt.  Keeps interface consistent
with the old random.py.

Put in a new .jumpahead(n) method.  The interface is consistent with
the old random.py, but there are new semantics.  Since a specific n jump 
cannot be computed, takes a useful and compatible alternative by altering
the internal state to another state far from the original.  The new
semantics are faithful to the use case of supporting multiple threads
with non-overlapping sequences.

The MersenneTwister C code is now subclassable.  Accordingly, the random.Random
class now inherits from MersenneTwister.Random.



Index: MersenneTwister.c
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/twister/MersenneTwister.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -C2 -d -r1.4 -r1.5
*** MersenneTwister.c	18 Dec 2002 20:36:01 -0000	1.4
--- MersenneTwister.c	23 Dec 2002 04:31:35 -0000	1.5
***************
*** 4,28 ****
  /* ------------------------------------------------------------------
     The code in this module was based on a download from:
! 	  http://www.math.keio.ac.jp/~matumoto/MT2002/emt19937ar.html
  
     It was modified in 2002 by Raymond Hettinger as follows:
  
! 	* the principal computational lines untouched except for tabbing.
  
! 	* renamed genrand_res53() to random_random() and wrapped
! 	  in python calling/return code.
  
! 	* created random_randbelow() for direct access to genrand_int32
! 	  without first creating a float.
  
! 	* genrand_int32() and the helper functions, init_genrand() 
! 	  and init_by_array(), were declared static, wrapped in
! 	  Python calling/return code.  also, their global data 
! 	  references were replaced with structure references.
  
- 	* unused functions from the original were deleted.
- 	  new, original C python code was added to implement Random()
- 	  interface.
-    
     The following are the verbatim comments from the original code:
  
--- 4,25 ----
  /* ------------------------------------------------------------------
     The code in this module was based on a download from:
!           http://www.math.keio.ac.jp/~matumoto/MT2002/emt19937ar.html
  
     It was modified in 2002 by Raymond Hettinger as follows:
  
!         * the principal computational lines untouched except for tabbing.
  
!         * renamed genrand_res53() to random_random() and wrapped
!           in python calling/return code.
  
!         * genrand_int32() and the helper functions, init_genrand()
!           and init_by_array(), were declared static, wrapped in
!           Python calling/return code.  also, their global data
!           references were replaced with structure references.
  
!         * unused functions from the original were deleted.
!           new, original C python code was added to implement Random()
!           interface.
  
     The following are the verbatim comments from the original code:
  
***************
*** 30,38 ****
     Coded by Takuji Nishimura and Makoto Matsumoto.
  
!    Before using, initialize the state by using init_genrand(seed)  
     or init_by_array(init_key, key_length).
  
     Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
!    All rights reserved.                          
  
     Redistribution and use in source and binary forms, with or without
--- 27,35 ----
     Coded by Takuji Nishimura and Makoto Matsumoto.
  
!    Before using, initialize the state by using init_genrand(seed)
     or init_by_array(init_key, key_length).
  
     Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
!    All rights reserved.
  
     Redistribution and use in source and binary forms, with or without
***************
*** 41,53 ****
  
       1. Redistributions of source code must retain the above copyright
! 	notice, this list of conditions and the following disclaimer.
  
       2. Redistributions in binary form must reproduce the above copyright
! 	notice, this list of conditions and the following disclaimer in the
! 	documentation and/or other materials provided with the distribution.
  
!      3. The names of its contributors may not be used to endorse or promote 
! 	products derived from this software without specific prior written 
! 	permission.
  
     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
--- 38,50 ----
  
       1. Redistributions of source code must retain the above copyright
!         notice, this list of conditions and the following disclaimer.
  
       2. Redistributions in binary form must reproduce the above copyright
!         notice, this list of conditions and the following disclaimer in the
!         documentation and/or other materials provided with the distribution.
  
!      3. The names of its contributors may not be used to endorse or promote
!         products derived from this software without specific prior written
!         permission.
  
     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
***************
*** 74,78 ****
  #include <time.h>               // for seeding to current time
  
! /* Period parameters -- These are all magic.  Don't change. */  
  #define N 624
  #define M 397
--- 71,75 ----
  #include <time.h>               // for seeding to current time
  
! /* Period parameters -- These are all magic.  Don't change. */
  #define N 624
  #define M 397
***************
*** 81,90 ****
  #define LOWER_MASK 0x7fffffffUL /* least significant r bits */
  
- static PyObject *ErrorObject;
- 
  typedef struct {
! 	PyObject_HEAD
! 	unsigned long state[N]; 
! 	int index;
  } RandomObject;
  
--- 78,85 ----
  #define LOWER_MASK 0x7fffffffUL /* least significant r bits */
  
  typedef struct {
!         PyObject_HEAD
!         unsigned long state[N];
!         int index;
  } RandomObject;
  
***************
*** 98,127 ****
  
  /* generates a random number on [0,0xffffffff]-interval */
! static unsigned long 
  genrand_int32(RandomObject *self)
  {
! 	unsigned long y;
! 	static unsigned long mag01[2]={0x0UL, MATRIX_A};
! 	/* mag01[x] = x * MATRIX_A  for x=0,1 */
! 	unsigned long *mt;
  
! 	mt = self->state;
! 	if (self->index >= N) { /* generate N words at one time */
! 		int kk;
  
! 		for (kk=0;kk<N-M;kk++) {
! 			y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
! 			mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
! 		}
! 		for (;kk<N-1;kk++) {
! 			y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
! 			mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
! 	}
! 		y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
! 		mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
  
- 		self->index = 0;
- 	}
-   
      y = mt[self->index++];
      y ^= (y >> 11);
--- 93,122 ----
  
  /* generates a random number on [0,0xffffffff]-interval */
! static unsigned long
  genrand_int32(RandomObject *self)
  {
!         unsigned long y;
!         static unsigned long mag01[2]={0x0UL, MATRIX_A};
!         /* mag01[x] = x * MATRIX_A  for x=0,1 */
!         unsigned long *mt;
  
!         mt = self->state;
!         if (self->index >= N) { /* generate N words at one time */
!                 int kk;
  
!                 for (kk=0;kk<N-M;kk++) {
!                         y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
!                         mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
!                 }
!                 for (;kk<N-1;kk++) {
!                         y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
!                         mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
!         }
!                 y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
!                 mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
! 
!                 self->index = 0;
!         }
  
      y = mt[self->index++];
      y ^= (y >> 11);
***************
*** 135,181 ****
  random_random(RandomObject *self)
  {
! 	unsigned long y, z;
! 	double result;
! 
! 	y = genrand_int32(self);
! 	z = genrand_int32(self);
! 	result = ((y>>5)*67108864.0+(z>>6))*(1.0/9007199254740992.0);
! 	return PyFloat_FromDouble(result);
! }
! 
! static PyObject *
! random_randbelow(RandomObject *self, PyObject *n)
! {
! 	unsigned long limit;
  
! 	limit = PyInt_AsLong(n);
! 	if (limit == -1  && PyErr_Occurred())
! 		return NULL;
! 	return PyInt_FromLong((long)(genrand_int32(self) % limit));
  }
  
  /* initializes mt[N] with a seed */
! static PyObject * 
  init_genrand(RandomObject *self, unsigned long s)
  {
! 	int mti;
! 	unsigned long *mt;
  
! 	assert(RandomObject_Check(self));
! 	mt = self->state;
! 	mt[0]= s & 0xffffffffUL;
! 	for (mti=1; mti<N; mti++) {
! 		mt[mti] = 
! 		(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); 
! 		/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
! 		/* In the previous versions, MSBs of the seed affect   */
! 		/* only MSBs of the array mt[].                        */
! 		/* 2002/01/09 modified by Makoto Matsumoto             */
! 		mt[mti] &= 0xffffffffUL;
! 		/* for >32 bit machines */
! 	}
! 	self->index = mti;
! 	Py_INCREF(Py_None);
! 	return Py_None;
  }
  
--- 130,165 ----
  random_random(RandomObject *self)
  {
!         unsigned long y, z;
!         double result;
  
!         y = genrand_int32(self);
!         z = genrand_int32(self);
!         result = ((y>>5)*67108864.0+(z>>6))*(1.0/9007199254740992.0);
!         return PyFloat_FromDouble(result);
  }
  
  /* initializes mt[N] with a seed */
! static PyObject *
  init_genrand(RandomObject *self, unsigned long s)
  {
!         int mti;
!         unsigned long *mt;
  
!         assert(RandomObject_Check(self));
!         mt = self->state;
!         mt[0]= s & 0xffffffffUL;
!         for (mti=1; mti<N; mti++) {
!                 mt[mti] =
!                 (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
!                 /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
!                 /* In the previous versions, MSBs of the seed affect   */
!                 /* only MSBs of the array mt[].                        */
!                 /* 2002/01/09 modified by Makoto Matsumoto             */
!                 mt[mti] &= 0xffffffffUL;
!                 /* for >32 bit machines */
!         }
!         self->index = mti;
!         Py_INCREF(Py_None);
!         return Py_None;
  }
  
***************
*** 183,213 ****
  /* init_key is the array for initializing keys */
  /* key_length is its length */
! static PyObject * 
  init_by_array(RandomObject *self, unsigned long init_key[], unsigned long key_length)
  {
! 	unsigned int i, j, k;   /* was signed in the original code. RDH 12/16/2002 */
! 	unsigned long *mt;
  
! 	mt = self->state;
! 	init_genrand(self, 19650218UL);
! 	i=1; j=0;
! 	k = (N>key_length ? N : key_length);
! 	for (; k; k--) {
! 		mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
! 			 + init_key[j] + j; /* non linear */
! 		mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
! 		i++; j++;
! 		if (i>=N) { mt[0] = mt[N-1]; i=1; }
! 		if (j>=key_length) j=0;
! 	}
! 	for (k=N-1; k; k--) {
! 		mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
! 			 - i; /* non linear */
! 		mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
! 		i++;
! 		if (i>=N) { mt[0] = mt[N-1]; i=1; }
! 	}
  
!     mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ 
      Py_INCREF(Py_None);
      return Py_None;
--- 167,197 ----
  /* init_key is the array for initializing keys */
  /* key_length is its length */
! static PyObject *
  init_by_array(RandomObject *self, unsigned long init_key[], unsigned long key_length)
  {
!         unsigned int i, j, k;   /* was signed in the original code. RDH 12/16/2002 */
!         unsigned long *mt;
  
!         mt = self->state;
!         init_genrand(self, 19650218UL);
!         i=1; j=0;
!         k = (N>key_length ? N : key_length);
!         for (; k; k--) {
!                 mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
!                          + init_key[j] + j; /* non linear */
!                 mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
!                 i++; j++;
!                 if (i>=N) { mt[0] = mt[N-1]; i=1; }
!                 if (j>=key_length) j=0;
!         }
!         for (k=N-1; k; k--) {
!                 mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
!                          - i; /* non linear */
!                 mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
!                 i++;
!                 if (i>=N) { mt[0] = mt[N-1]; i=1; }
!         }
  
!     mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
      Py_INCREF(Py_None);
      return Py_None;
***************
*** 217,249 ****
  random_seed(RandomObject *self, PyObject *args)
  {
! 	time_t now;
! 	unsigned long *key;
! 	unsigned long keylength;
! 	long seed;
! 	unsigned int i;
! 	PyObject *result;
  
! 	keylength = PyTuple_Size(args);
! 	if (keylength == 0) {
! 		time(&now);
! 		return init_genrand(self, (unsigned long)now);
! 	}
  
! 	key = (unsigned long *) PyMem_MALLOC(keylength * sizeof(unsigned long));
! 	if (key == NULL)
  		return NULL;
- 	
- 	for (i=0; i<keylength ; i++) {
- 		seed = PyInt_AsLong(PyTuple_GET_ITEM(args, i));
- 		if (seed == -1 && PyErr_Occurred()) {
- 			PyMem_FREE(key);
- 			return NULL;
- 		}
- 		key[i] = (unsigned long)seed;
  	}
  
! 	result = init_by_array(self, key, keylength);
! 	PyMem_FREE(key);
! 	return result;
  }
  
--- 201,278 ----
  random_seed(RandomObject *self, PyObject *args)
  {
!         time_t now;
!         unsigned long *key;
!         unsigned long keylength;
!         unsigned long seed;
!         unsigned int i;
!         PyObject *split;
!         PyObject *result = NULL;
!         PyObject *arg = NULL;
!         PyObject *masklower;
!         PyObject *thirtytwo;
!         PyObject *little = NULL;
!         PyObject *newarg;
!         int err;
  
!         if (!PyArg_ParseTuple(args, "|O:seed", &arg))
!                 return NULL;
  
!         if (arg == NULL || arg == Py_None) {
!                 time(&now);
!                 return init_genrand(self, (unsigned long)now);
!         }
! 
!         split = PyList_New(0);
!         if (split == NULL)
!                 return NULL;
! 
! 	arg = PyNumber_Absolute(arg);
! 	if (arg == NULL) {
! 		Py_DECREF(split);
  		return NULL;
  	}
+         masklower = PyLong_FromUnsignedLong(0xffffffffU);
+         thirtytwo = PyInt_FromLong(32L);
+         while (PyObject_IsTrue(arg)) {
+                 little = PyNumber_And(arg, masklower);
+                 if (little == NULL)
+                         goto Done;
+                 assert(PyLong_Check(little));
  
!                 err = PyList_Append(split, little);
!                 if (err == -1)
!                         goto Done;
! 
!                 newarg = PyNumber_Rshift(arg, thirtytwo);
!                 if (newarg == NULL)
!                         goto Done;
!                 Py_DECREF(arg);
!                 arg = newarg;
!         }
! 
!         if (PyList_Size(split) == 0)
!                 PyList_Append(split, PyLong_FromLong(0L));
! 
!         keylength = PyList_Size(split);
!         key = (unsigned long *) PyMem_MALLOC(keylength * sizeof(unsigned long));
!         if (key == NULL)
!                 goto Done;
!         for (i=0; i<keylength ; i++) {
!                 seed = PyLong_AsUnsignedLong(PyList_GET_ITEM(split, i));
!                 if (seed == -1 && PyErr_Occurred()) {
!                         PyMem_FREE(key);
!                         goto Done;
!                 }
!                 key[i] = seed;
!         }
!         result = init_by_array(self, key, keylength);
!         PyMem_FREE(key);
! Done:
!         Py_DECREF(masklower);
!         Py_DECREF(thirtytwo);
!         Py_XDECREF(little);
!         Py_DECREF(arg);
!         Py_DECREF(split);
!         return result;
  }
  
***************
*** 251,278 ****
  random_getstate(RandomObject *self)
  {
! 	PyObject *state;
! 	PyObject *element;
! 	int i;
  
! 	state = PyTuple_New(N+1);
! 	if (state == NULL)
! 		return NULL;
! 	for (i=0; i<N ; i++) {
! 		element = PyInt_FromLong((long)(self->state[i]));
! 		if (element == NULL)
! 			goto Fail;
! 		PyTuple_SET_ITEM(state, i, element);
! 	}
! 	element = PyInt_FromLong((long)(self->index));
! 	if (element == NULL)
! 		goto Fail;
! 	PyTuple_SET_ITEM(state, i, element);
! 	return state;
  
  Fail:
! 	for (i=i-1; i>=0 ; i--)
! 		Py_DECREF(PyTuple_GET_ITEM(state, i));
! 	Py_DECREF(state);
! 	return NULL;
  }
  
--- 280,307 ----
  random_getstate(RandomObject *self)
  {
!         PyObject *state;
!         PyObject *element;
!         int i;
  
!         state = PyTuple_New(N+1);
!         if (state == NULL)
!                 return NULL;
!         for (i=0; i<N ; i++) {
!                 element = PyInt_FromLong((long)(self->state[i]));
!                 if (element == NULL)
!                         goto Fail;
!                 PyTuple_SET_ITEM(state, i, element);
!         }
!         element = PyInt_FromLong((long)(self->index));
!         if (element == NULL)
!                 goto Fail;
!         PyTuple_SET_ITEM(state, i, element);
!         return state;
  
  Fail:
!         for (i=i-1; i>=0 ; i--)
!                 Py_DECREF(PyTuple_GET_ITEM(state, i));
!         Py_DECREF(state);
!         return NULL;
  }
  
***************
*** 280,308 ****
  random_setstate(RandomObject *self, PyObject *state)
  {
! 	int i;
! 	long element;
  
! 	if (!PyTuple_Check(state)) {
! 		PyErr_SetString(PyExc_TypeError, 
! 			"state vector must be a tuple");
! 		return NULL;
! 	}
! 	if (PyTuple_Size(state) != N+1) {
! 		PyErr_SetString(PyExc_ValueError, 
! 			"state vector is the wrong size");
! 		return NULL;
! 	}
  
! 	for (i=0; i<N ; i++) {
! 		element = PyInt_AsLong(PyTuple_GET_ITEM(state, i));
! 		if (element == -1 && PyErr_Occurred())
  			return NULL;
! 		self->state[i] = (unsigned long)element;
  	}
  
! 	element = PyInt_AsLong(PyTuple_GET_ITEM(state, i));
! 	if (element == -1 && PyErr_Occurred())
! 		return NULL;
! 	self->index = (int)element;
  
  	Py_INCREF(Py_None);
--- 309,380 ----
  random_setstate(RandomObject *self, PyObject *state)
  {
!         int i;
!         long element;
  
!         if (!PyTuple_Check(state)) {
!                 PyErr_SetString(PyExc_TypeError,
!                         "state vector must be a tuple");
!                 return NULL;
!         }
!         if (PyTuple_Size(state) != N+1) {
!                 PyErr_SetString(PyExc_ValueError,
!                         "state vector is the wrong size");
!                 return NULL;
!         }
  
!         for (i=0; i<N ; i++) {
!                 element = PyInt_AsLong(PyTuple_GET_ITEM(state, i));
!                 if (element == -1 && PyErr_Occurred())
!                         return NULL;
!                 self->state[i] = (unsigned long)element;
!         }
! 
!         element = PyInt_AsLong(PyTuple_GET_ITEM(state, i));
!         if (element == -1 && PyErr_Occurred())
!                 return NULL;
!         self->index = (int)element;
! 
!         Py_INCREF(Py_None);
!         return Py_None;
! }
! 
! /*
! Strategy shuffle with n-1 (orthog to gen process)
! 
! Set mti to last so that a new set is generated
! 
! Add (not xor) a value to each place to assure that shuffle
! doesn't leave the first some in the same place and state.
! */
! 
! static PyObject *
! random_jumpahead(RandomObject *self, PyObject *n)
! {
! 	long i, j;
! 	PyObject *iobj;
! 	PyObject *remobj;
!         unsigned long *mt, tmp;
! 
!         assert(RandomObject_Check(self));
!         mt = self->state;
! 	self->index = N;
! 
! 	for (i=N-1 ; i>1 ; i--) {
! 		iobj = PyInt_FromLong(i);
! 		remobj = PyNumber_Remainder(n, iobj);
! 		Py_DECREF(iobj);
! 		if (remobj == NULL) 
  			return NULL;
! 		j = PyInt_AsLong(remobj);
! 		Py_DECREF(remobj);
! 		if (j == -1L && PyErr_Occurred())
! 			return NULL;
! 		tmp = mt[i];
! 		mt[i] = mt[j];
! 		mt[j] = tmp;
  	}
  
! 	for (i=0 ; i<N ; i++)
! 		mt[i] += i+1;
  
  	Py_INCREF(Py_None);
***************
*** 310,391 ****
  }
  
- static PyMethodDef random_methods[] = {
- 	{"random",      (PyCFunction)random_random,  METH_NOARGS,
- 		PyDoc_STR("random() -> x in the interval [0,1).")},
- 	{"_randbelow",   (PyCFunction)random_randbelow,  METH_O,
- 		PyDoc_STR("_randbelow(n) -> integer x in the interval [0,n).")},
- 	{"seed",        (PyCFunction)random_seed,  METH_VARARGS,
- 		PyDoc_STR("seed(*seeds) -> None.  Defaults to current time")},
- 	{"getstate",    (PyCFunction)random_getstate,  METH_NOARGS,
- 		PyDoc_STR("getstate() -> tuple containing the current state.")},
- 	{"setstate",      (PyCFunction)random_setstate,  METH_O,
- 		PyDoc_STR("setstate(state) -> None.  Restores generator state.")},
- 	{NULL,          NULL}           /* sentinel */
- };
- 
- /* Create New Instance */
- 
  static PyObject *
  random_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
  {
! 	RandomObject *self;
! 	self = (RandomObject *)PyObject_New(RandomObject, &Random_Type);
! 	if (self == NULL)
! 		return NULL;
! 	if (random_seed(self, args) == NULL) {
! 		Py_DECREF(self);
! 		return NULL;
! 	}
! 	return (PyObject *)self;
  }
  
  PyDoc_STRVAR(random_doc,
  "Random() -> create a random number generator with its own internal state.");
  
  static PyTypeObject Random_Type = {
! 	/* The ob_type field must be initialized in the module init function
! 	 * to be portable to Windows without using C++. */
! 	PyObject_HEAD_INIT(NULL)
! 	0,                              /*ob_size*/
! 	"MersenneTwister.Random",       /*tp_name*/
! 	sizeof(RandomObject),           /*tp_basicsize*/
! 	0,                              /*tp_itemsize*/
! 	/* methods */
! 	(destructor)PyObject_Del,       /*tp_dealloc*/
! 	0,                              /*tp_print*/
! 	0,                              /*tp_getattr*/
! 	0,                              /*tp_setattr*/
! 	0,                              /*tp_compare*/
! 	0,                              /*tp_repr*/
! 	0,                              /*tp_as_number*/
! 	0,                              /*tp_as_sequence*/
! 	0,                              /*tp_as_mapping*/
! 	0,                              /*tp_hash*/
! 	0,                              /*tp_call*/
! 	0,                              /*tp_str*/
! 	PyObject_GenericGetAttr,        /*tp_getattro*/
! 	0,                              /*tp_setattro*/
! 	0,                              /*tp_as_buffer*/
! 	Py_TPFLAGS_DEFAULT,             /*tp_flags*/
! 	random_doc,                     /*tp_doc*/
! 	0,                              /*tp_traverse*/
! 	0,                              /*tp_clear*/
! 	0,                              /*tp_richcompare*/
! 	0,                              /*tp_weaklistoffset*/
! 	0,                              /*tp_iter*/
! 	0,                              /*tp_iternext*/
! 	random_methods,                 /*tp_methods*/
! 	0,                              /*tp_members*/
! 	0,                              /*tp_getset*/
! 	0,                              /*tp_base*/
! 	0,                              /*tp_dict*/
! 	0,                              /*tp_descr_get*/
! 	0,                              /*tp_descr_set*/
! 	0,                              /*tp_dictoffset*/
! 	0,                              /*tp_init*/
! 	PyType_GenericAlloc,            /*tp_alloc*/
! 	random_new,                     /*tp_new*/
! 	0,                              /*tp_free*/
! 	0,                              /*tp_is_gc*/
  };
  
--- 382,460 ----
  }
  
  static PyObject *
  random_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
  {
!         RandomObject *self;
!         self = (RandomObject *)type->tp_alloc(type, 0);
!         if (self == NULL)
!                 return NULL;
!         if (random_seed(self, args) == NULL) {
!                 Py_DECREF(self);
!                 return NULL;
!         }
!         return (PyObject *)self;
  }
  
+ static PyMethodDef random_methods[] = {
+         {"random",      (PyCFunction)random_random,  METH_NOARGS,
+                 PyDoc_STR("random() -> x in the interval [0,1).")},
+         {"seed",        (PyCFunction)random_seed,  METH_VARARGS,
+                 PyDoc_STR("seed([n]) -> None.  Defaults to current time")},
+         {"getstate",    (PyCFunction)random_getstate,  METH_NOARGS,
+                 PyDoc_STR("getstate() -> tuple containing the current state.")},
+         {"setstate",      (PyCFunction)random_setstate,  METH_O,
+                 PyDoc_STR("setstate(state) -> None.  Restores generator state.")},
+ 	{"jumpahead",	(PyCFunction)random_jumpahead,  METH_O,
+ 		PyDoc_STR("jumpahead(int) -> None.  Create a new state from\n\
+ the existing state and the supplied integer.")},
+         {NULL,          NULL}           /* sentinel */
+ };
+ 
  PyDoc_STRVAR(random_doc,
  "Random() -> create a random number generator with its own internal state.");
  
  static PyTypeObject Random_Type = {
!         PyObject_HEAD_INIT(NULL)
!         0,                              /*ob_size*/
!         "MersenneTwister.Random",       /*tp_name*/
!         sizeof(RandomObject),           /*tp_basicsize*/
!         0,                              /*tp_itemsize*/
!         /* methods */
!         (destructor)PyObject_Del,       /*tp_dealloc*/
!         0,                              /*tp_print*/
!         0,                              /*tp_getattr*/
!         0,                              /*tp_setattr*/
!         0,                              /*tp_compare*/
!         0,                              /*tp_repr*/
!         0,                              /*tp_as_number*/
!         0,                              /*tp_as_sequence*/
!         0,                              /*tp_as_mapping*/
!         0,                              /*tp_hash*/
!         0,                              /*tp_call*/
!         0,                              /*tp_str*/
!         PyObject_GenericGetAttr,        /*tp_getattro*/
!         0,                              /*tp_setattro*/
!         0,                              /*tp_as_buffer*/
!         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,       /*tp_flags*/
!         random_doc,                     /*tp_doc*/
!         0,                              /*tp_traverse*/
!         0,                              /*tp_clear*/
!         0,                              /*tp_richcompare*/
!         0,                              /*tp_weaklistoffset*/
!         0,                              /*tp_iter*/
!         0,                              /*tp_iternext*/
!         random_methods,                 /*tp_methods*/
!         0,                              /*tp_members*/
!         0,                              /*tp_getset*/
!         0,                              /*tp_base*/
!         0,                              /*tp_dict*/
!         0,                              /*tp_descr_get*/
!         0,                              /*tp_descr_set*/
!         0,                              /*tp_dictoffset*/
!         0,                              /*tp_init*/
!         PyType_GenericAlloc,            /*tp_alloc*/
!         random_new,                     /*tp_new*/
!         0,                              /*tp_free*/
!         0,                              /*tp_is_gc*/
  };
  
***************
*** 394,400 ****
  
  static PyMethodDef module_functions[] = {
! 	{"Random",      (PyCFunction)random_new,  METH_VARARGS,
! 		PyDoc_STR("Random(*seeds) -> RandomObject")},
! 	{NULL,          NULL}           /* sentinel */
  };
  
--- 463,467 ----
  
  static PyMethodDef module_functions[] = {
!         {NULL,          NULL}           /* sentinel */
  };
  
***************
*** 402,422 ****
  initMersenneTwister(void)
  {
! 	PyObject *m;
! 
! 	/* Initialize the type of the new type object here; doing it here
! 	 * is required for portability to Windows without requiring C++. */
! 	Random_Type.ob_type = &PyType_Type;
! 
! 	/* Create the module and add the functions */
! 	m = Py_InitModule3("MersenneTwister", module_functions, module_doc);
  
! 	/* Add some symbolic constants to the module */
! 	if (ErrorObject == NULL) {
! 		ErrorObject = PyErr_NewException("MersenneTwister.error", NULL, NULL);
! 		if (ErrorObject == NULL)
! 			return;
! 	}
! 	Py_INCREF(ErrorObject);
! 	PyModule_AddObject(m, "error", ErrorObject);
  }
  
--- 469,477 ----
  initMersenneTwister(void)
  {
!         PyObject *m;
  
!         Random_Type.ob_type = &PyType_Type;
!         m = Py_InitModule3("MersenneTwister", module_functions, module_doc);
!         PyModule_AddObject(m, "Random", (PyObject *)&Random_Type);
  }
  

Index: random.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/twister/random.py,v
retrieving revision 1.5
retrieving revision 1.6
diff -C2 -d -r1.5 -r1.6
*** random.py	18 Dec 2002 20:36:01 -0000	1.5
--- random.py	23 Dec 2002 04:31:35 -0000	1.6
***************
*** 109,114 ****
  
  import MersenneTwister
  
! class Random:
      """Random number generator base class used by bound module functions.
  
--- 109,115 ----
  
  import MersenneTwister
+ CoreGenerator = MersenneTwister.Random
  
! class Random(CoreGenerator):
      """Random number generator base class used by bound module functions.
  
***************
*** 125,148 ****
      """
  
!     VERSION = 1
  
!     def __init__(self, *seeds):
          """Initialize an instance.
  
!         Optional argument "seeds" controls seeding, as for Random.seed().
          """
-         if self.__class__ == Random:
-             geninstance = MersenneTwister.Random()
-             self.random = geninstance.random
-             self.seed = geninstance.seed
-             self._getstate = geninstance.getstate
-             self._setstate = geninstance.setstate
-             self._randbelow = geninstance._randbelow
          self.gauss_next = None
!         self.seed(*seeds)
!         
      def getstate(self):
          """Return internal state; can be passed to setstate() later."""
!         return self.VERSION, self._getstate(), self.gauss_next
  
      def setstate(self, state):
--- 126,142 ----
      """
  
!     VERSION = 2     # used by getstate/setstate
  
!     def __init__(self, x=None):
          """Initialize an instance.
  
!         Optional argument "x" controls seeding, as for Random.seed().
          """
          self.gauss_next = None
!         self.seed(x)
! 
      def getstate(self):
          """Return internal state; can be passed to setstate() later."""
!         return self.VERSION, CoreGenerator.getstate(self), self.gauss_next
  
      def setstate(self, state):
***************
*** 151,155 ****
          if version == 1:
              version, internalstate, self.gauss_next = state
!             self._setstate(internalstate)
          else:
              raise ValueError("state with version %s passed to "
--- 145,149 ----
          if version == 1:
              version, internalstate, self.gauss_next = state
!             CoreGenerator.setstate(self, internalstate)
          else:
              raise ValueError("state with version %s passed to "
***************
*** 170,178 ****
  ## -------------------- integer methods  -------------------
  
!     def _randbelow(self, n, int=int):
!         """Internal helper method returns an integer in the range [0,n)."""
!         return int(self.random() * n)
! 
!     def randrange(self, start, stop=None, step=1, _randbelow=_randbelow, default=None):
          """Choose a random item from range(start, stop[, step]).
  
--- 164,168 ----
  ## -------------------- integer methods  -------------------
  
!     def randrange(self, start, stop=None, step=1, int=int, default=None):
          """Choose a random item from range(start, stop[, step]).
  
***************
*** 189,193 ****
          if stop is default:
              if istart > 0:
!                 return _randbelow(istart)
              raise ValueError, "empty range for randrange()"
  
--- 179,183 ----
          if stop is default:
              if istart > 0:
!                 return int(self.random() * istart)
              raise ValueError, "empty range for randrange()"
  
***************
*** 198,202 ****
          if step == 1 and istart < istop:
              try:
!                 return istart + _randbelow(istop - istart)
              except OverflowError:
                  # This can happen if istop-istart > sys.maxint + 1, and
--- 188,192 ----
          if step == 1 and istart < istop:
              try:
!                 return istart + int(self.random()*(istop - istart))
              except OverflowError:
                  # This can happen if istop-istart > sys.maxint + 1, and
***************
*** 223,227 ****
          if n <= 0:
              raise ValueError, "empty range for randrange()"
!         return istart + istep*self._randbelow(n)
  
      def randint(self, a, b):
--- 213,217 ----
          if n <= 0:
              raise ValueError, "empty range for randrange()"
!         return istart + istep*int(self.random() * n)
  
      def randint(self, a, b):
***************
*** 237,241 ****
          return seq[int(self.random() * len(seq))]
  
!     def shuffle(self, x, random=None):
          """x, random=random.random -> shuffle list x in place; return None.
  
--- 227,231 ----
          return seq[int(self.random() * len(seq))]
  
!     def shuffle(self, x, random=None, int=int):
          """x, random=random.random -> shuffle list x in place; return None.
  
***************
*** 250,262 ****
  
          if random is None:
!             _randbelow = self._randbelow
!         else:
!             _randbelow = lambda n: int(random()*n)
          for i in xrange(len(x)-1, 0, -1):
              # pick an element in x[:i+1] with which to exchange x[i]
!             j = _randbelow(i+1)
              x[i], x[j] = x[j], x[i]
  
!     def sample(self, population, k):
          """Chooses k unique random elements from a population sequence.
  
--- 240,250 ----
  
          if random is None:
!             random = self.random
          for i in xrange(len(x)-1, 0, -1):
              # pick an element in x[:i+1] with which to exchange x[i]
!             j = int(random() * (i+1))
              x[i], x[j] = x[j], x[i]
  
!     def sample(self, population, k, random=None, int=int):
          """Chooses k unique random elements from a population sequence.
  
***************
*** 293,302 ****
          if not 0 <= k <= n:
              raise ValueError, "sample larger than population"
!         _randbelow = self._randbelow
          result = [None] * k
          if n < 6 * k:     # if n len list takes less space than a k len dict
              pool = list(population)
              for i in xrange(k):         # invariant:  non-selected at [0,n-i)
!                 j = _randbelow(n-i)
                  result[i] = pool[j]
                  pool[j] = pool[n-i-1]
--- 281,291 ----
          if not 0 <= k <= n:
              raise ValueError, "sample larger than population"
!         if random is None:
!             random = self.random
          result = [None] * k
          if n < 6 * k:     # if n len list takes less space than a k len dict
              pool = list(population)
              for i in xrange(k):         # invariant:  non-selected at [0,n-i)
!                 j = int(random() * (n-i))
                  result[i] = pool[j]
                  pool[j] = pool[n-i-1]
***************
*** 304,310 ****
              selected = {}
              for i in xrange(k):
!                 j = _randbelow(n)
                  while j in selected:
!                     j = _randbelow(n)
                  result[i] = selected[j] = population[j]
          return result
--- 293,299 ----
              selected = {}
              for i in xrange(k):
!                 j = int(random() * n)
                  while j in selected:
!                     j = int(random() * n)
                  result[i] = selected[j] = population[j]
          return result
***************
*** 627,635 ****
  
  class WichmannHill(Random):
-     # Specific to Wichmann-Hill generator.  Subclasses wishing to use a
-     # different core generator should override the seed(), random(),
-     # getstate(), setstate() and jumpahead() methods.
  
!     def seed(self, *seeds):
          """Initialize internal state from hashable object.
  
--- 616,623 ----
  
  class WichmannHill(Random):
  
!     VERSION = 1     # used by getstate/setstate
! 
!     def seed(self, a=None):
          """Initialize internal state from hashable object.
  
***************
*** 644,658 ****
          """
  
!         if len(seeds):
!             a = 0
!             for s in seeds:
!                 if not isinstance(s, (int, long)):
!                     s = hash(s)
!                 a ^= s
!         else:
              # Initialize from current time
              import time
              a = long(time.time() * 256)
  
          a, x = divmod(a, 30268)
          a, y = divmod(a, 30306)
--- 632,643 ----
          """
  
!         if a is None:
              # Initialize from current time
              import time
              a = long(time.time() * 256)
  
+         if not isinstance(a, (int, long)):
+             a = hash(a)
+ 
          a, x = divmod(a, 30268)
          a, y = divmod(a, 30306)
***************
*** 854,859 ****
              raise ValueError("jumpahead test failed " + `(N, r1, r2)`)
  
-     print _inst.__class__   # Identify which generator was tested
- 
  # Create one instance, seeded from current time, and export its methods
  # as module-level functions.  The functions are not threadsafe, and state
--- 839,842 ----
***************
*** 862,871 ****
  # casual user than making them instantiate their own Random() instance.
  
! _inst = Random()
! #_inst = WichmannHill()
  seed = _inst.seed
  random = _inst.random
  uniform = _inst.uniform
- _randbelow = _inst._randbelow
  randint = _inst.randint
  choice = _inst.choice
--- 845,852 ----
  # casual user than making them instantiate their own Random() instance.
  
! _inst = Random()        # or _inst = WichmannHill()
  seed = _inst.seed
  random = _inst.random
  uniform = _inst.uniform
  randint = _inst.randint
  choice = _inst.choice
***************
*** 886,891 ****
  getstate = _inst.getstate
  setstate = _inst.setstate
  try:
-     jumpahead = _inst.jumpahead
      whseed = _inst.whseed
  except AttributeError:
--- 867,872 ----
  getstate = _inst.getstate
  setstate = _inst.setstate
+ jumpahead = _inst.jumpahead
  try:
      whseed = _inst.whseed
  except AttributeError:

Index: test_twister.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/twister/test_twister.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -d -r1.3 -r1.4
*** test_twister.py	18 Dec 2002 20:36:01 -0000	1.3
--- test_twister.py	23 Dec 2002 04:31:35 -0000	1.4
***************
*** 50,57 ****
                      0.084297823823520, 0.538148646718315, 0.089215024911993,
                      0.784861961053729]
!         self.gen.seed(61731, 24903, 614, 42143)
          actual = self.randomlist(2000)[-10:]
          for a, e in zip(actual, expected):
              self.assertEqual(round(a-e, 14), 0)
  
  def test_main():
--- 50,73 ----
                      0.084297823823520, 0.538148646718315, 0.089215024911993,
                      0.784861961053729]
!         self.gen.seed(61731L + (24903L<<32) + (614L<<64) + (42143L<<96))
          actual = self.randomlist(2000)[-10:]
          for a, e in zip(actual, expected):
              self.assertEqual(round(a-e, 14), 0)
+ 
+     def test_seedargs(self):
+         for arg in [0, 0L, 1, 1L, -1, -1L, 10**20, -(10**20)]:
+             self.gen.seed(arg)
+         for arg in [3.14, 1+2j, 'a', range(3), tuple('abc'), dict(one=1)]:
+             self.assertRaises(TypeError, self.gen.seed, arg)
+ 
+     def test_jumpahead(self):
+         self.gen.seed()
+         state1 = self.gen.getstate()
+         self.gen.jumpahead(100)
+         state2 = self.gen.getstate()    # s/b distinct from state1
+         self.assertNotEqual(state1, state2)
+         self.gen.jumpahead(100)
+         state3 = self.gen.getstate()    # s/b distinct from state2
+         self.assertNotEqual(state2, state3)
  
  def test_main():

Index: todo.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/twister/todo.txt,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** todo.txt	18 Dec 2002 19:29:41 -0000	1.1
--- todo.txt	23 Dec 2002 04:31:35 -0000	1.2
***************
*** 1,17 ****
- Make the C code subclassable and then make Random a subclass of MT.
- 
  Rewrite the module docstring for random.py
  
  Write the docs and a news item.
  
- Consider using Python large integers for seeding by array.
- Makes the interface more consistent with Py2.2.2.
- Makes it a little tougher to test back to the reference
- implementation.
- 
  Get buy-in or second review for design decisions:
! - Keeping Wichmann-Hill alive.
  - signed/unsigned coercion
! - license markup
! _ _randbelow()   interface addition
! 
--- 1,12 ----
  Rewrite the module docstring for random.py
  
  Write the docs and a news item.
  
  Get buy-in or second review for design decisions:
! - Keeping Wichmann-Hill alive as a separate subclass
  - signed/unsigned coercion
! - license/copyright inclusion
! - solution to the jumpahead() problem
! - using pylongs for seeds
! - use of VERSION number
! - internal get/setstate