math: add random float generator to gen, make check work for ulp tests
[libc-test] / src / math / gen / rnd.c
diff --git a/src/math/gen/rnd.c b/src/math/gen/rnd.c
new file mode 100644 (file)
index 0000000..21ca038
--- /dev/null
@@ -0,0 +1,286 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+static uint64_t seed = -1;
+static uint32_t rand32(void)
+{
+       seed = 6364136223846793005ull*seed + 1;
+       return seed >> 32;
+}
+static uint64_t rand64(void)
+{
+       uint64_t u = rand32();
+       return u<<32 | rand32();
+}
+static double frand(){return rand64() * 0x1p-64;}
+static float frandf(){return rand32() * 0x1p-32f;}
+static long double frandl(){return rand64() * 0x1p-64L;}
+
+/* uniform random in [0,n), n > 0 must hold */
+uint64_t randn(uint64_t n)
+{
+       uint64_t r, m;
+
+       /* m is the largest multiple of n */
+       m = -1;
+       m -= m%n;
+       while ((r = rand64()) >= m);
+       return r%n;
+}
+
+/* uniform on [a,b] */
+uint64_t randint(uint64_t a, uint64_t b)
+{
+       if (b < a) {
+               uint64_t t = b;
+               b = a;
+               a = t;
+       }
+       return a + randn(b - a + 1);
+}
+
+int insert(uint64_t *tab, size_t len, uint64_t v)
+{
+       size_t i = v & (len-1);
+       size_t j = 1;
+
+       /* 0 means empty, v > 0 must hold */
+       while (tab[i]) {
+               if (tab[i] == v)
+                       return -1;
+               i += j++;
+               i &= len-1;
+       }
+       tab[i] = v;
+       return 0;
+}
+
+static void shuffle2(uint64_t *p, uint64_t *q, size_t np, size_t nq)
+{
+       size_t i,r,t;
+
+       i = np+nq;
+       while (i > np) {
+               r = randn(i);
+               i--;
+               t = q[i-np];
+               if (r < np) {
+                       q[i-np] = p[r];
+                       p[r] = t;
+               } else {
+                       q[i-np] = q[r-np];
+                       q[r-np] = t;
+               }
+       }
+}
+
+/* choose k unique numbers from [0,n), k <= n */
+int choose(uint64_t n, size_t k, uint64_t *p)
+{
+       uint64_t *tab;
+       size_t i, j, len;
+
+       if (n < k)
+               return -1;
+
+       if (n < 16) {
+               /* no alloc */
+               while (k)
+                       if (randn(n--) < k)
+                               p[--k] = n;
+               return 0;
+       }
+
+       if (k < 8) {
+               /* no alloc, n > 15 > 2*k */
+               for (i = 0; i < k;) {
+                       p[i] = randn(n);
+                       for (j = 0; p[j] != p[i]; j++);
+                       if (j == i)
+                               i++;
+               }
+               return 0;
+       }
+
+       if (n < 5*k && (n-k)*sizeof *tab < (size_t)-1) {
+               /* allocation is < 4*k */
+               tab = malloc((n-k) * sizeof *tab);
+               if (!tab)
+                       return -1;
+               for (i = 0; i < k; i++)
+                       p[i] = i;
+               for (; i < n; i++)
+                       tab[i-k] = i;
+               if (n-k < k)
+                       shuffle2(p, tab, k, n-k);
+               else
+                       shuffle2(tab, p, n-k, k);
+               free(tab);
+               return 0;
+       }
+
+       /* allocation is < 4*k */
+       for (len = 16; len <= 2*k; len *= 2);
+       tab = calloc(len, sizeof *tab);
+       if (!tab)
+               return -1;
+       for (i = 0; i < k; i++)
+               while (insert(tab, len, randn(n)+1));
+       for (i = 0; i < len; i++)
+               if (tab[i])
+                       *p++ = tab[i]-1;
+       free(tab);
+       return 0;
+}
+
+static int cmp64(const void *a, const void *b)
+{
+       const uint64_t *ua = a, *ub = b;
+       return *ua < *ub ? -1 : (*ua > *ub ? 1 : 0);
+}
+
+// todo: in place flip problem
+
+/* choose k unique uint64_t numbers */
+int choose64(size_t k, uint64_t *p)
+{
+       size_t i, c;
+
+       /* no alloc, collisions should be very rare */
+       for (i = 0; i < k; i++)
+               p[i] = rand64();
+       do {
+               c = 0;
+               qsort(p, k, sizeof *p, cmp64);
+               for (i = 1; i < k; i++)
+                       if (p[i] == p[i-1]) {
+                               p[i-1] = rand64();
+                               c = 1;
+                       }
+       } while (c);
+       return 0;
+}
+
+/* equidistant sampling with some randomness */
+int sample(uint64_t n, size_t k, uint64_t *p)
+{
+       uint64_t a = 0;
+       uint64_t d = n/k;
+       size_t m = n%k;
+       size_t i, j;
+       uint64_t *q;
+
+       if (!d)
+               return -1;
+       q = malloc((m+1) * sizeof *q);
+       if (!q)
+               return -1;
+       if (choose(k, m, q))
+               return -1;
+       qsort(q, m, sizeof *q, cmp64);
+       q[m] = k;
+       for (i = j = 0; i < k; i++) {
+               uint64_t t;
+
+               while (q[j] < i)
+                       j++;
+               if (q[j] == i)
+                       t = d+1;
+               else
+                       t = d;
+               p[i] = a + randn(t);
+               a += t;
+       }
+       free(q);
+       return 0;
+}
+
+/* [-inf,inf] uniform on representation */
+int genall(size_t k, uint64_t *p)
+{
+       size_t i;
+       uint64_t n, d;
+       d = 1;
+       d <<= 52;
+       if (sample(-2*d, k, p))
+               return -1;
+       n = 0x7ff;
+       n <<= 52;
+       for (i = 0; i < k; i++)
+               if (p[i] > n)
+                       p[i] += d-1;
+       return 0;
+}
+
+/* [a,b) uniform on representation, 0 <= a <= b */
+int genab(size_t k, uint64_t a, uint64_t b, uint64_t *p)
+{
+       size_t i;
+
+       if (sample(b-a, k, p))
+               return -1;
+       for (i = 0; i < k; i++)
+               p[i] += a;
+       return 0;
+}
+
+#define asfloat(x) ((union{uint64_t af_i; double af_f;}){.af_i=x}.af_f)
+#define asint(x)   ((union{uint64_t af_i; double af_f;}){.af_f=x}.af_i)
+
+int main(int argc, char *argv[])
+{
+       uint64_t k, i;
+       uint64_t *p;
+       double a,b,m;
+       char *e;
+       int opt;
+
+       k = 1000;
+       a = 0;
+       b = 1;
+       m = 1;
+       while ((opt = getopt(argc, argv, "n:a:b:m:s:")) != -1) {
+               switch(opt) {
+               case 'n':
+                       k = strtoull(optarg,&e,0);
+                       break;
+               case 'a':
+                       a = strtod(optarg,&e);
+                       if (a < 0)
+                               goto usage;
+                       break;
+               case 'b':
+                       b = strtod(optarg,&e);
+                       if (b < 0)
+                               goto usage;
+                       break;
+               case 'm':
+                       m = strtod(optarg,&e);
+                       break;
+               case 's':
+                       seed = strtoull(optarg,&e,0);
+                       break;
+               default:
+usage:
+                       fprintf(stderr, "usage: %s -n num -a absmin -b absmax -m mult -s seed\n", argv[0]);
+                       return -1;
+               }
+               if (*e || errno)
+                       goto usage;
+       }
+       if (!(a <= b))
+               goto usage;
+       p = malloc(k * sizeof *p);
+       if (!p)
+               return -1;
+       if (genab(k, asint(a), asint(b), p))
+//     if (genall(k,p))
+               return -1;
+       for (i = 0; i < k; i++)
+//             printf("0x%016llx\n", p[i]);
+               printf("%a\n", m*asfloat(p[i]));
+       return 0;
+}