diff --git a/hhss/c/prelex.c b/hhss/c/prelex.c index 8618b53..4221ee7 100644 --- a/hhss/c/prelex.c +++ b/hhss/c/prelex.c @@ -10,7 +10,7 @@ extern array_t *prelex(array_t *db, int datcnt) { lastpos = array_size(db); for (int k = 0; k < datcnt; k++) { - rv = rand_range(0, lastpos); /* [0, lp) */ + rv = rand_range(0, lastpos - 1); /* [0, lp - 1] */ curr = array_get(db, rv); last = array_get(db, lastpos - 1); diff --git a/hhss/c/replace.c b/hhss/c/replace.c index cf1aa36..97def0b 100644 --- a/hhss/c/replace.c +++ b/hhss/c/replace.c @@ -27,7 +27,7 @@ extern void replace_templates(array_t *pts, array_t *rtdb) { if (rtslen == 0) synerr_empty(); - v = rand_range(0, rtslen); + v = rand_range(0, rtslen - 1); rt = *((char **) array_get(rts, v)); sectarr = rtdbquery(rtdb, rt); @@ -80,9 +80,8 @@ static void rthandle_user(symbol_t *sym, array_t *sectarr) { goto common; } - do { - r = rand_range(0, siz); - } while (r == pre_user); + do r = rand_range(0, siz - 1); + while (r == pre_user); pre_user = r; common: @@ -93,7 +92,7 @@ static void rthandle_else(symbol_t *sym, array_t *sectarr) { int r, siz; siz = array_size(sectarr); - r = rand_range(0, siz); + r = rand_range(0, siz - 1); rthandle_common(sym, sectarr, r); } diff --git a/lib/utils.c b/lib/utils.c index 7d61798..1bfd550 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -5,10 +5,56 @@ extern void seed(void) { srand(time(NULL)); } +/* Refer to https://c-faq.com/lib/randrange.html */ extern int rand_range(int min, int max) { - if (max <= 0) - VERR("max must be greater than 0, but given %d", max); - return min + rand() / (RAND_MAX / max + 1); + if (min > max) + VERR("min must be less than or equal to max," + " but given min=%d, max=%d", min, max); + + /* implicit integer promotion happens */ + /* INT_MAX - INT_MIN == UINT_MAX */ + if ((unsigned int) max - min > RAND_MAX) + VERR("range too large to handle!" + " max - min must be <= %d (RAND_MAX)," + " but given min=%d, max=%d", RAND_MAX, min, max); + + if (min == max) + return min; + + unsigned int + nbucket, /* number of buckets */ + bucket_siz, + threshold, + rv; + + /* like there are 5 numbers in [1,5] since 5 - 1 + 1 = 5, + max - min + 1 means the count of the numbers in [min, max] */ + /* since max - min <= RAND_MAX <= INT_MAX, it's fine to add 1 */ + nbucket = 1U + max - min; + /* since rand() returns [0,RAND_MAX], the number of total + possible return values is RAND_MAX - 0 + 1 */ + /* specify 'U' in order to treat it as a unsigned int value */ + /* integer promotion also happens */ + bucket_siz = (RAND_MAX + 1U) / nbucket; + threshold = bucket_siz * nbucket; + + do rv = rand(); + while (rv >= threshold); + + return min + (int) (rv / bucket_siz); + + /* EXAMPLE CASE + Suppose RAND_MAX = 10, nbucket = 3. + Since bucket_siz = (10 + 1) / 3 = 3 (fractional part discarded), + threshold = 3 * 3 = 9. + Since RAND_MAX is 10, rand() returns [0,10]. + If rand returns 9 or 10, re-roll. + If rand returns 0 ~ 8, then + rv = 0,1,2 => rv / 3 = 0 + rv = 3,4,5 => rv / 3 = 1 + rv = 6,7,8 => rv / 3 = 2 + Thus, all numbers in [min, max] have an equal + possibility to appear. */ } extern int mblen_(char ch) { diff --git a/yandere/c/noise.c b/yandere/c/noise.c index d0e332b..2cb92ef 100644 --- a/yandere/c/noise.c +++ b/yandere/c/noise.c @@ -6,13 +6,13 @@ extern void noise(const char *msg) { bool overflow; for (i = k = 0; msg[i] != '\0'; /* empty */) { - rv = rand_range(0, 6); + rv = rand_range(0, 5); chlen = mblen_(msg[i]); overflow = (k + chlen) >= (BUFMAX - 1); if (overflow) break; if (rv == 0) { /* 1/6 chance */ - buf[k++] = *("#?@" + rand_range(0, 3)); + buf[k++] = *("#?@" + rand_range(0, 2)); i += chlen; } else for (int m = 0; m < chlen; m++)