You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

893 lines
22 KiB

/*
* Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "random_range.h"
/*
* Internal format of the range array set up by parse_range()
*/
struct range {
int min;
int max;
int mult;
};
/*
* parse_ranges() is a function to parse a comma-separated list of range
* tokens each having the following form:
*
* num
* or
* min:max[:mult]
*
* any of the values may be blank (ie. min::mult, :max, etc.) and default
* values for missing arguments may be supplied by the caller.
*
* The special first form is short hand for 'num:num'.
*
* After parsing the string, the ranges are put into an array of integers,
* which is malloc'd by the routine. The min, max, and mult entries of each
* range can be extracted from the array using the range_min(), range_max(),
* and range_mult() functions.
*
* It is the responsibility of the caller to free the space allocated by
* parse_ranges() - a single call to free() will free the space.
*
* str The string to parse - assumed to be a comma-separated
* list of tokens having the above format.
* defmin default value to plug in for min, if it is missing
* defmax default value to plug in for max, if it is missing
* defmult default value to plug in for mult, if missing
* parse_func A user-supplied function pointer, which parse_ranges()
* can call to parse the min, max, and mult strings. This
* allows for customized number formats. The function
* MUST have the following prototype:
* parse_func(char *str, int *val)
* The function should return -1 if str cannot be parsed
* into an integer, or >= 0 if it was successfully
* parsed. The resulting integer will be stored in
* *val. If parse_func is NULL, parse_ranges will parse
* the tokens in a manner consistent with the the sscanf
* %i format.
* range_ptr A user-supplied char **, which will be set to point
* at malloc'd space which holds the parsed range
* values. If range_ptr is NULL, parse_ranges() just
* parses the string. The data returned in range_ptr
* should not be processed directly - use the functions
* range_min(), range_max(), and range_mult() to access
* data for a given range.
* errptr user-supplied char ** which can be set to point to a
* static error string. If errptr is NULL, it is ignored.
*
* parse_range() returns -1 on error, or the number of ranges parsed.
*/
static int str_to_int();
static long long divider(long long, long long, long long, long long);
int parse_ranges(char *str, int defmin, int defmax, int defmult,
int (*parse_func)(), char **rangeptr, char **errptr)
{
int ncommas;
char *tmpstr, *cp, *tok, *n1str, *n2str, *multstr;
struct range *rp, *ranges;
static char errmsg[256];
if (errptr != NULL) {
*errptr = errmsg;
}
for (ncommas = 0, cp = str; *cp != '\0'; cp++) {
if (*cp == ',') {
ncommas++;
}
}
if (parse_func == NULL) {
parse_func = str_to_int;
}
tmpstr = strdup(str);
ranges = malloc((ncommas + 1) * sizeof(struct range));
rp = ranges;
tok = strtok(tmpstr, ",");
while (tok != NULL) {
n1str = tok;
n2str = NULL;
multstr = NULL;
rp->min = defmin;
rp->max = defmax;
rp->mult = defmult;
if ((cp = strchr(n1str, ':')) != NULL) {
*cp = '\0';
n2str = cp + 1;
if ((cp = strchr(n2str, ':')) != NULL) {
*cp = '\0';
multstr = cp + 1;
}
}
/*
* Parse the 'min' field - if it is zero length (:n2[:mult]
* format), retain the default value, otherwise, pass the
* string to the parse function.
*/
if ((int)strlen(n1str) > 0) {
if ((*parse_func) (n1str, &rp->min) < 0) {
sprintf(errmsg,
"error parsing string %s into an integer",
n1str);
free(tmpstr);
free(ranges);
return -1;
}
}
/*
* Process the 'max' field - if one was not present (n1 format)
* set max equal to min. If the field was present, but
* zero length (n1: format), retain the default. Otherwise
* pass the string to the parse function.
*/
if (n2str == NULL) {
rp->max = rp->min;
} else if ((int)strlen(n2str) > 0) {
if ((*parse_func) (n2str, &rp->max) < 0) {
sprintf(errmsg,
"error parsing string %s into an integer",
n2str);
free(tmpstr);
free(ranges);
return -1;
}
}
/*
* Process the 'mult' field - if one was not present
* (n1:n2 format), or the field was zero length (n1:n2: format)
* then set the mult field to defmult - otherwise pass then
* mult field to the parse function.
*/
if (multstr != NULL && (int)strlen(multstr) > 0) {
if ((*parse_func) (multstr, &rp->mult) < 0) {
sprintf(errmsg,
"error parsing string %s into an integer",
multstr);
free(tmpstr);
free(ranges);
return -1;
}
}
rp++;
tok = strtok(NULL, ",");
}
free(tmpstr);
if (rangeptr != NULL) {
*rangeptr = (char *)ranges;
} else {
free(ranges); /* just running in parse mode */
}
return (rp - ranges);
}
/*
* The default integer-parsing function
*/
static int str_to_int(char *str, int *ip)
{
char c;
if (sscanf(str, "%i%c", ip, &c) != 1) {
return -1;
} else {
return 0;
}
}
/*
* Three simple functions to return the min, max, and mult values for a given
* range. It is assumed that rbuf is a range buffer set up by parse_ranges(),
* and that r is a valid range within that buffer.
*/
int range_min(char *rbuf, int r)
{
return ((struct range *)rbuf)[r].min;
}
int range_max(char *rbuf, int r)
{
return ((struct range *)rbuf)[r].max;
}
int range_mult(char *rbuf, int r)
{
return ((struct range *)rbuf)[r].mult;
}
/*****************************************************************************
* random_range(int start, int end, int mult, char **errp)
*
* Returns a psuedo-random number which is >= 'start', <= 'end', and a multiple
* of 'mult'. Start and end may be any valid integer, but mult must be an
* integer > 0. errp is a char ** which will be set to point to a static
* error message buffer if it is not NULL, and an error occurs.
*
* The errp is the only way to check if the routine fails - currently the only
* failure conditions are:
*
* mult < 1
* no numbers in the start-end range that are a multiple of 'mult'
*
* If random_range_fails, and errp is a valid pointer, it will point to an
* internal error buffer. If errp is a vaild pointer, and random_range
* is successful, errp will be set to NULL.
*
* Note - if mult is 1 (the most common case), there are error conditions
* possible, and errp need not be used.
*
* Note: Uses lrand48(), assuming that set_random_seed() uses srand48() when
* setting the seed.
*****************************************************************************/
long random_range(int min, int max, int mult, char **errp)
{
int r, nmults, orig_min, orig_max, orig_mult, tmp;
extern long lrand48();
static char errbuf[128];
/*
* Sanity check
*/
if (mult < 1) {
if (errp != NULL) {
sprintf(errbuf, "mult arg must be greater than 0");
*errp = errbuf;
}
return -1;
}
/*
* Save original parameter values for use in error message
*/
orig_min = min;
orig_max = max;
orig_mult = mult;
/*
* switch min/max if max < min
*/
if (max < min) {
tmp = max;
max = min;
min = tmp;
}
/*
* select the random number
*/
if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
min += mult - r;
if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
max -= r;
if (min > max) { /* no 'mult' multiples between min & max */
if (errp != NULL) {
sprintf(errbuf,
"no numbers in the range %d:%d that are a multiple of %d",
orig_min, orig_max, orig_mult);
*errp = errbuf;
}
return -1;
}
if (errp != NULL) {
*errp = NULL;
}
nmults = ((max - min) / mult) + 1;
#if CRAY
/*
* If max is less than 2gb, then the value can fit in 32 bits
* and the standard lrand48() routine can be used.
*/
if (max <= (long)2147483647) {
return (long)(min + (((long)lrand48() % nmults) * mult));
} else {
/*
* max is greater than 2gb - meeds more than 32 bits.
* Since lrand48 only will get a number up to 32bits.
*/
long randnum;
randnum = divider(min, max, 0, -1);
return (long)(min + ((randnum % nmults) * mult));
}
#else
return (min + ((lrand48() % nmults) * mult));
#endif
}
/*
* Just like random_range, but all values are longs.
*/
long random_rangel(long min, long max, long mult, char **errp)
{
long r, nmults, orig_min, orig_max, orig_mult, tmp;
extern long lrand48();
static char errbuf[128];
/*
* Sanity check
*/
if (mult < 1) {
if (errp != NULL) {
sprintf(errbuf, "mult arg must be greater than 0");
*errp = errbuf;
}
return -1;
}
/*
* Save original parameter values for use in error message
*/
orig_min = min;
orig_max = max;
orig_mult = mult;
/*
* switch min/max if max < min
*/
if (max < min) {
tmp = max;
max = min;
min = tmp;
}
/*
* select the random number
*/
if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
min += mult - r;
if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
max -= r;
if (min > max) { /* no 'mult' multiples between min & max */
if (errp != NULL) {
sprintf(errbuf,
"no numbers in the range %ld:%ld that are a multiple of %ld",
orig_min, orig_max, orig_mult);
*errp = errbuf;
}
return -1;
}
if (errp != NULL) {
*errp = NULL;
}
nmults = ((max - min) / mult) + 1;
#if CRAY || (_MIPS_SZLONG == 64)
/*
* If max is less than 2gb, then the value can fit in 32 bits
* and the standard lrand48() routine can be used.
*/
if (max <= (long)2147483647) {
return (long)(min + (((long)lrand48() % nmults) * mult));
} else {
/*
* max is greater than 2gb - meeds more than 32 bits.
* Since lrand48 only will get a number up to 32bits.
*/
long randnum;
randnum = divider(min, max, 0, -1);
return (long)(min + ((randnum % nmults) * mult));
}
#else
return (min + ((lrand48() % nmults) * mult));
#endif
}
/*
* Attempts to be just like random_range, but everything is long long (64 bit)
*/
long long random_rangell(long long min, long long max,
long long mult, char **errp)
{
long long r, nmults, orig_min, orig_max, orig_mult, tmp;
long long randnum;
extern long lrand48();
static char errbuf[128];
/*
* Sanity check
*/
if (mult < 1) {
if (errp != NULL) {
sprintf(errbuf, "mult arg must be greater than 0");
*errp = errbuf;
}
return -1;
}
/*
* Save original parameter values for use in error message
*/
orig_min = min;
orig_max = max;
orig_mult = mult;
/*
* switch min/max if max < min
*/
if (max < min) {
tmp = max;
max = min;
min = tmp;
}
/*
* select the random number
*/
if ((r = min % mult)) /* bump to the next higher 'mult' multiple */
min += mult - r;
if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */
max -= r;
if (min > max) { /* no 'mult' multiples between min & max */
if (errp != NULL) {
sprintf(errbuf,
"no numbers in the range %lld:%lld that are a multiple of %lld",
orig_min, orig_max, orig_mult);
*errp = errbuf;
}
return -1;
}
if (errp != NULL) {
*errp = NULL;
}
nmults = ((max - min) / mult) + 1;
/*
* If max is less than 2gb, then the value can fit in 32 bits
* and the standard lrand48() routine can be used.
*/
if (max <= (long)2147483647) {
return (long long)(min +
(((long long)lrand48() % nmults) * mult));
} else {
/*
* max is greater than 2gb - meeds more than 32 bits.
* Since lrand48 only will get a number up to 32bits.
*/
randnum = divider(min, max, 0, -1);
return (long long)(min + ((randnum % nmults) * mult));
}
}
/*
* This functional will recusively call itself to return a random
* number min and max. It was designed to work the 64bit numbers
* even when compiled as 32 bit process.
* algorithm: to use the official lrand48() routine - limited to 32 bits.
* find the difference between min and max (max-min).
* if the difference is 2g or less, use the random number gotton from lrand48().
* Determine the midway point between min and max.
* if the midway point is less than 2g from min or max,
* randomly add the random number gotton from lrand48() to
* either min or the midpoint.
* Otherwise, call outself with min and max being min and midway value or
* midway value and max. This will reduce the range in half.
*/
static long long
divider(long long min, long long max, long long cnt, long long rand)
{
long long med, half, diff;
/*
* prevent run away code. We are dividing by two each count.
* if we get to a count of more than 32, we should have gotten
* to 2gb.
*/
if (cnt > 32)
return -1;
/*
* Only get a random number the first time.
*/
if (cnt == 0 || rand < -1) {
rand = (long long)lrand48(); /* 32 bit random number */
}
diff = max - min;
if (diff <= 2147483647)
return min + rand;
half = diff / (long long)2; /* half the distance between min and max */
med = min + half; /* med way point between min and max */
#if DEBUG
printf("divider: min=%lld, max=%lld, cnt=%lld, rand=%lld\n", min, max,
cnt, rand);
printf(" diff = %lld, half = %lld, med = %lld\n", diff, half, med);
#endif
if (half <= 2147483647) {
/*
* If half is smaller than 2gb, we can use the random number
* to pick the number within the min to med or med to max
* if the cnt bit of rand is zero or one, respectively.
*/
if (rand & (1 << cnt))
return med + rand;
else
return min + rand;
} else {
/*
* recursively call ourself to reduce the value to the bottom half
* or top half (bit cnt is set).
*/
if (rand & (1 << cnt)) {
return divider(med, max, cnt + 1, rand);
} else {
return divider(min, med, cnt + 1, rand);
}
}
}
/*****************************************************************************
* random_range_seed(s)
*
* Sets the random seed to s. Uses srand48(), assuming that lrand48() will
* be used in random_range().
*****************************************************************************/
void random_range_seed(long s)
{
extern void srand48();
srand48(s);
}
/****************************************************************************
* random_bit(mask)
*
* This function randomly returns a single bit from the bits
* set in mask. If mask is zero, zero is returned.
*
****************************************************************************/
long random_bit(long mask)
{
int nbits = 0; /* number of set bits in mask */
long bit; /* used to count bits and num of set bits choosen */
int nshift; /* used to count bit shifts */
if (mask == 0)
return 0;
/*
* get the number of bits set in mask
*/
#ifndef CRAY
bit = 1L;
for (nshift = 0; (unsigned int)nshift < sizeof(long) * 8; nshift++) {
if (mask & bit)
nbits++;
bit = bit << 1;
}
#else
nbits = _popcnt(mask);
#endif /* if CRAY */
/*
* randomly choose a bit.
*/
bit = random_range(1, nbits, 1, NULL);
/*
* shift bits until you determine which bit was randomly choosen.
* nshift will hold the number of shifts to make.
*/
nshift = 0;
while (bit) {
/* check if the current one's bit is set */
if (mask & 1L) {
bit--;
}
mask = mask >> 1;
nshift++;
}
return 01L << (nshift - 1);
}
#if RANDOM_BIT_UNITTEST
/*
* The following is a unit test main function for random_bit().
*/
main(argc, argv)
int argc;
char **argv;
{
int ind;
int cnt, iter;
long mask, ret;
printf("test for first and last bit set\n");
mask = 1L;
ret = random_bit(mask);
printf("random_bit(%#o) returned %#o\n", mask, ret);
mask = 1L << (sizeof(long) * 8 - 1);
ret = random_bit(mask);
printf("random_bit(%#o) returned %#o\n", mask, ret);
if (argc >= 3) {
iter = atoi(argv[1]);
for (ind = 2; ind < argc; ind++) {
printf("Calling random_bit %d times for mask %#o\n",
iter, mask);
sscanf(argv[ind], "%i", &mask);
for (cnt = 0; cnt < iter; cnt++) {
ret = random_bit(mask);
printf("random_bit(%#o) returned %#o\n", mask,
ret);
}
}
}
exit(0);
}
#endif /* end if RANDOM_BIT_UNITTEST */
#if UNIT_TEST
/*
* The following is a unit test main function for random_range*().
*/
#define PARTNUM 10 /* used to determine even distribution of random numbers */
#define MEG 1024*1024*1024
#define GIG 1073741824
int main(argc, argv)
int argc;
char **argv;
{
int ind;
int cnt, iter = 10;
int imin = 0, imult = 1, itmin, itmax = 0;
#if CRAY
int imax = 6 * GIG; /* higher than 32 bits */
#else
int imax = 1048576;
#endif
long lret, lmin = 0, lmult = 1, ltmin, ltmax = 0;
#if CRAY || (_MIPS_SZLONG == 64)
long lmax = 6 * (long)GIG; /* higher than 32 bits */
#else
long lmax = 1048576;
#endif
long long llret, llmin = 0, llmult = 1, lltmin, lltmax = 0;
long long llmax = (long long)80 * (long long)GIG;
long part;
long long lpart;
long cntarr[PARTNUM];
long valbound[PARTNUM];
long long lvalbound[PARTNUM];
for (ind = 0; ind < PARTNUM; ind++)
cntarr[ind] = 0;
if (argc < 2) {
printf("Usage: %s func [iterations] \n", argv[0]);
printf
("func can be random_range, random_rangel, random_rangell\n");
exit(1);
}
if (argc >= 3) {
if (sscanf(argv[2], "%i", &iter) != 1) {
printf("Usage: %s [func iterations] \n", argv[0]);
printf("argv[2] is not a number\n");
exit(1);
}
}
/*
* random_rangel ()
*/
if (strcmp(argv[1], "random_rangel") == 0) {
ltmin = lmax;
part = lmax / PARTNUM;
for (ind = 0; ind < PARTNUM; ind++) {
valbound[ind] = part * ind;
}
for (cnt = 0; cnt < iter; cnt++) {
lret = random_rangel(lmin, lmax, lmult, NULL);
if (iter < 100)
printf("%ld\n", lret);
if (lret < ltmin)
ltmin = lret;
if (lret > ltmax)
ltmax = lret;
for (ind = 0; ind < PARTNUM - 1; ind++) {
if (valbound[ind] < lret
&& lret <= valbound[ind + 1]) {
cntarr[ind]++;
break;
}
}
if (lret > valbound[PARTNUM - 1]) {
cntarr[PARTNUM - 1]++;
}
}
for (ind = 0; ind < PARTNUM - 1; ind++) {
printf("%2d %-13ld to %-13ld %5ld %4.4f\n", ind + 1,
valbound[ind], valbound[ind + 1], cntarr[ind],
(float)(cntarr[ind] / (float)iter));
}
printf("%2d %-13ld to %-13ld %5ld %4.4f\n", PARTNUM,
valbound[PARTNUM - 1], lmax, cntarr[PARTNUM - 1],
(float)(cntarr[PARTNUM - 1] / (float)iter));
printf(" min=%ld, max=%ld\n", ltmin, ltmax);
} else if (strcmp(argv[1], "random_rangell") == 0) {
/*
* random_rangell() unit test
*/
lltmin = llmax;
lpart = llmax / PARTNUM;
for (ind = 0; ind < PARTNUM; ind++) {
lvalbound[ind] = (long long)(lpart * ind);
}
for (cnt = 0; cnt < iter; cnt++) {
llret = random_rangell(llmin, llmax, llmult, NULL);
if (iter < 100)
printf("random_rangell returned %lld\n", llret);
if (llret < lltmin)
lltmin = llret;
if (llret > lltmax)
lltmax = llret;
for (ind = 0; ind < PARTNUM - 1; ind++) {
if (lvalbound[ind] < llret
&& llret <= lvalbound[ind + 1]) {
cntarr[ind]++;
break;
}
}
if (llret > lvalbound[PARTNUM - 1]) {
cntarr[PARTNUM - 1]++;
}
}
for (ind = 0; ind < PARTNUM - 1; ind++) {
printf("%2d %-13lld to %-13lld %5ld %4.4f\n",
ind + 1, lvalbound[ind], lvalbound[ind + 1],
cntarr[ind], (float)(cntarr[ind] / (float)iter));
}
printf("%2d %-13lld to %-13lld %5ld %4.4f\n", PARTNUM,
lvalbound[PARTNUM - 1], llmax, cntarr[PARTNUM - 1],
(float)(cntarr[PARTNUM - 1] / (float)iter));
printf(" min=%lld, max=%lld\n", lltmin, lltmax);
} else {
/*
* random_range() unit test
*/
itmin = imax;
part = imax / PARTNUM;
for (ind = 0; ind < PARTNUM; ind++) {
valbound[ind] = part * ind;
}
for (cnt = 0; cnt < iter; cnt++) {
lret = random_range(imin, imax, imult, NULL);
if (iter < 100)
printf("%ld\n", lret);
if (lret < itmin)
itmin = lret;
if (lret > itmax)
itmax = lret;
for (ind = 0; ind < PARTNUM - 1; ind++) {
if (valbound[ind] < lret
&& lret <= valbound[ind + 1]) {
cntarr[ind]++;
break;
}
}
if (lret > valbound[PARTNUM - 1]) {
cntarr[PARTNUM - 1]++;
}
}
for (ind = 0; ind < PARTNUM - 1; ind++) {
printf("%2d %-13ld to %-13ld %5ld %4.4f\n", ind + 1,
valbound[ind], valbound[ind + 1], cntarr[ind],
(float)(cntarr[ind] / (float)iter));
}
printf("%2d %-13ld to %-13ld %5ld %4.4f\n", PARTNUM,
valbound[PARTNUM - 1], (long)imax, cntarr[PARTNUM - 1],
(float)(cntarr[PARTNUM - 1] / (float)iter));
printf(" min=%d, max=%d\n", itmin, itmax);
}
exit(0);
}
#endif