All, Here is a potentially contentious patch for PR fortran/52879.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52879 As demonstrated in that PR, it is possible to provide RANDOM_SEED with sets of seeds that result in a very poor sequences of PRN. Gfortran's RANDOM_NUMBER uses 3 independent KISS PRNG to generate the bits of the significands of the real PRN. Each KISS PRNG requires 4 seeds. This patch removes the need for a user to specify 12 seeds. It uses a Lehmer linear congruent generator to generate the 12 KISS seeds. This LCG requires a single seed. I have selected to have seed(1)=0 correspond to the current default set of KISS seeds. Internally, the LCG declares the seed as a GFC_UINTEGER_8 (ie., uint64_t) type, so for gfortran's default integer kind one has 2**31-1 possible seeds and if one use -fdefault-integer-8 then one has 2**32 possible seeds. It is possible to extend the patch to allow a skip count such that for example seed(2)=10 would use every tenth value in the LCG sequence. I haven't pursued this as it appears to be an unneeded complication. The only testsuite failure is for gfortran.dg/random_seed_1.f90, which is a test designed to specifically test for the 12 KISS seeds. This test will be removed if the patch is OK. -- Steve Index: gcc/fortran/check.c =================================================================== --- gcc/fortran/check.c (revision 207633) +++ gcc/fortran/check.c (working copy) @@ -4925,16 +4925,9 @@ gfc_check_random_number (gfc_expr *harve bool gfc_check_random_seed (gfc_expr *size, gfc_expr *put, gfc_expr *get) { - unsigned int nargs = 0, kiss_size; + unsigned int nargs = 0; locus *where = NULL; mpz_t put_size, get_size; - bool have_gfc_real_16; /* Try and mimic HAVE_GFC_REAL_16 in libgfortran. */ - - have_gfc_real_16 = gfc_validate_kind (BT_REAL, 16, true) != -1; - - /* Keep the number of bytes in sync with kiss_size in - libgfortran/intrinsics/random.c. */ - kiss_size = (have_gfc_real_16 ? 48 : 32) / gfc_default_integer_kind; if (size != NULL) { @@ -4975,13 +4968,6 @@ gfc_check_random_seed (gfc_expr *size, g if (!kind_value_check (put, 1, gfc_default_integer_kind)) return false; - - if (gfc_array_size (put, &put_size) - && mpz_get_ui (put_size) < kiss_size) - gfc_error ("Size of '%s' argument of '%s' intrinsic at %L " - "too small (%i/%i)", - gfc_current_intrinsic_arg[1]->name, gfc_current_intrinsic, - where, (int) mpz_get_ui (put_size), kiss_size); } if (get != NULL) @@ -5007,13 +4993,6 @@ gfc_check_random_seed (gfc_expr *size, g if (!kind_value_check (get, 2, gfc_default_integer_kind)) return false; - - if (gfc_array_size (get, &get_size) - && mpz_get_ui (get_size) < kiss_size) - gfc_error ("Size of '%s' argument of '%s' intrinsic at %L " - "too small (%i/%i)", - gfc_current_intrinsic_arg[2]->name, gfc_current_intrinsic, - where, (int) mpz_get_ui (get_size), kiss_size); } /* RANDOM_SEED may not have more than one non-optional argument. */ Index: libgfortran/intrinsics/random.c =================================================================== --- libgfortran/intrinsics/random.c (revision 207633) +++ libgfortran/intrinsics/random.c (working copy) @@ -224,9 +224,20 @@ KISS algorithm. */ z=0,c=0 and z=2^32-1,c=698769068 should be avoided. */ -/* Any modifications to the seeds that change kiss_size below need to be - reflected in check.c (gfc_check_random_seed) to enable correct - compile-time checking of PUT size for the RANDOM_SEED intrinsic. */ +/* The 3 KISS generators require 3 sets for 4 seeds. It is possible for a + a user to specify a set of seeds that is inadequate for reseeding the + generators. For example, change only the value of one of the 12 seeds + may not reset the PRNG sequences. To work-around this issue, use a + simple Lehmer linear congruential generator that requires only a single + seed to generate the 12 KISS seeds. */ + +static const GFC_INTEGER_4 lehmer_lcg_size = 1; +static GFC_UINTEGER_8 lehmer_lcg_seed = 0; +static GFC_UINTEGER_4 +lehmer_lcg(GFC_UINTEGER_4 a) +{ + return ((GFC_UINTEGER_8)a * 279470273UL) % 4294967291UL; +} #define KISS_DEFAULT_SEED_1 123456789, 362436069, 521288629, 316191069 #define KISS_DEFAULT_SEED_2 987654321, 458629013, 582859209, 438195021 @@ -636,28 +647,6 @@ arandom_r16 (gfc_array_r16 *x) #endif - -static void -scramble_seed (unsigned char *dest, unsigned char *src, int size) -{ - int i; - - for (i = 0; i < size; i++) - dest[(i % 2) * (size / 2) + i / 2] = src[i]; -} - - -static void -unscramble_seed (unsigned char *dest, unsigned char *src, int size) -{ - int i; - - for (i = 0; i < size; i++) - dest[i] = src[(i % 2) * (size / 2) + i / 2]; -} - - - /* random_seed is used to seed the PRNG with either a default set of seeds or user specified set of seeds. random_seed must be called with no argument or exactly one argument. */ @@ -666,7 +655,7 @@ void random_seed_i4 (GFC_INTEGER_4 *size, gfc_array_i4 *put, gfc_array_i4 *get) { int i; - unsigned char seed[4*kiss_size]; + GFC_UINTEGER_4 seed; __gthread_mutex_lock (&random_lock); @@ -676,54 +665,33 @@ random_seed_i4 (GFC_INTEGER_4 *size, gfc /* From the standard: "If no argument is present, the processor assigns a processor-dependent value to the seed." */ - if (size == NULL && put == NULL && get == NULL) + if (size == NULL && put == NULL && get == NULL) { + lehmer_lcg_seed = 0; for (i = 0; i < kiss_size; i++) kiss_seed[i] = kiss_default_seed[i]; + } if (size != NULL) - *size = kiss_size; + *size = lehmer_lcg_size; if (put != NULL) { - /* If the rank of the array is not 1, abort. */ - if (GFC_DESCRIPTOR_RANK (put) != 1) - runtime_error ("Array rank of PUT is not 1."); - - /* If the array is too small, abort. */ - if (GFC_DESCRIPTOR_EXTENT(put,0) < kiss_size) - runtime_error ("Array size of PUT is too small."); - - /* We copy the seed given by the user. */ - for (i = 0; i < kiss_size; i++) - memcpy (seed + i * sizeof(GFC_UINTEGER_4), - &(put->base_addr[(kiss_size - 1 - i) * GFC_DESCRIPTOR_STRIDE(put,0)]), - sizeof(GFC_UINTEGER_4)); - - /* We put it after scrambling the bytes, to paper around users who - provide seeds with quality only in the lower or upper part. */ - scramble_seed ((unsigned char *) kiss_seed, seed, 4*kiss_size); + /* We copy the seed given by the user. */ + lehmer_lcg_seed = put->base_addr[0]; + if (lehmer_lcg_seed == 0) { + for (i = 0; i < kiss_size; i++) + kiss_seed[i] = kiss_default_seed[i]; + } else { + for (i = 0, seed = lehmer_lcg_seed; i < kiss_size; i++) { + seed = lehmer_lcg(seed); + kiss_seed[i] = seed; + } + } } /* Return the seed to GET data. */ if (get != NULL) - { - /* If the rank of the array is not 1, abort. */ - if (GFC_DESCRIPTOR_RANK (get) != 1) - runtime_error ("Array rank of GET is not 1."); - - /* If the array is too small, abort. */ - if (GFC_DESCRIPTOR_EXTENT(get,0) < kiss_size) - runtime_error ("Array size of GET is too small."); - - /* Unscramble the seed. */ - unscramble_seed (seed, (unsigned char *) kiss_seed, 4*kiss_size); - - /* Then copy it back to the user variable. */ - for (i = 0; i < kiss_size; i++) - memcpy (&(get->base_addr[(kiss_size - 1 - i) * GFC_DESCRIPTOR_STRIDE(get,0)]), - seed + i * sizeof(GFC_UINTEGER_4), - sizeof(GFC_UINTEGER_4)); - } + get->base_addr[0] = lehmer_lcg_seed; __gthread_mutex_unlock (&random_lock); } @@ -734,6 +702,7 @@ void random_seed_i8 (GFC_INTEGER_8 *size, gfc_array_i8 *put, gfc_array_i8 *get) { int i; + GFC_UINTEGER_4 seed; __gthread_mutex_lock (&random_lock); @@ -743,45 +712,33 @@ random_seed_i8 (GFC_INTEGER_8 *size, gfc /* From the standard: "If no argument is present, the processor assigns a processor-dependent value to the seed." */ - if (size == NULL && put == NULL && get == NULL) + if (size == NULL && put == NULL && get == NULL) { + lehmer_lcg_seed = 0; for (i = 0; i < kiss_size; i++) kiss_seed[i] = kiss_default_seed[i]; + } if (size != NULL) - *size = kiss_size / 2; + *size = lehmer_lcg_size; if (put != NULL) { - /* If the rank of the array is not 1, abort. */ - if (GFC_DESCRIPTOR_RANK (put) != 1) - runtime_error ("Array rank of PUT is not 1."); - - /* If the array is too small, abort. */ - if (GFC_DESCRIPTOR_EXTENT(put,0) < kiss_size / 2) - runtime_error ("Array size of PUT is too small."); - - /* This code now should do correct strides. */ - for (i = 0; i < kiss_size / 2; i++) - memcpy (&kiss_seed[2*i], &(put->base_addr[i * GFC_DESCRIPTOR_STRIDE(put,0)]), - sizeof (GFC_UINTEGER_8)); + /* We copy the seed given by the user. */ + lehmer_lcg_seed = put->base_addr[0]; + if (lehmer_lcg_seed == 0) { + for (i = 0; i < kiss_size; i++) + kiss_seed[i] = kiss_default_seed[i]; + } else { + for (i = 0, seed = lehmer_lcg_seed; i < kiss_size; i++) { + seed = lehmer_lcg(seed); + kiss_seed[i] = seed; + } + } } /* Return the seed to GET data. */ if (get != NULL) - { - /* If the rank of the array is not 1, abort. */ - if (GFC_DESCRIPTOR_RANK (get) != 1) - runtime_error ("Array rank of GET is not 1."); - - /* If the array is too small, abort. */ - if (GFC_DESCRIPTOR_EXTENT(get,0) < kiss_size / 2) - runtime_error ("Array size of GET is too small."); - - /* This code now should do correct strides. */ - for (i = 0; i < kiss_size / 2; i++) - memcpy (&(get->base_addr[i * GFC_DESCRIPTOR_STRIDE(get,0)]), &kiss_seed[2*i], - sizeof (GFC_UINTEGER_8)); - } + get->base_addr[0] = lehmer_lcg_seed; __gthread_mutex_unlock (&random_lock); }