skip to content
==> tests/ranges.c <==
1/* jobs.rs's `ranges` test, ported, and the set operations against a bitset over random operations. */
2#include "mesh/jobs/ranges.h"
3#include "mesh/check.h"
4
5#include <stdlib.h>
6#include <string.h>
7
8static void word_is(const mj_ranges *r, const char *want) {
9    char *w = mj_ranges_word_new(r);
10    CHECK_S(w, want);
11    free(w);
12}
13
14static mj_ranges parsed(const char *s) {
15    mj_ranges r = {0};
16    char *err = NULL;
17    if (!mj_ranges_parse(s, &r, &err)) {
18        CHECK(!"parse");
19        free(err);
20    }
21    return r;
22}
23
24static void the_rust_test(void) {
25    mj_ranges r = {0};
26    mj_ranges_insert(&r, 10, 20);
27    mj_ranges_insert(&r, 30, 40);
28    mj_ranges_insert(&r, 20, 25);
29    word_is(&r, "10-25,30-40");
30    mj_ranges_insert(&r, 24, 31);
31    word_is(&r, "10-40");
32    CHECK(mj_ranges_remove(&r, 15, 17));
33    word_is(&r, "10-15,17-40");
34    CHECK(!mj_ranges_remove(&r, 15, 17));
35    CHECK_U(mj_ranges_len(&r), 28);
36    CHECK(mj_ranges_contains(&r, 14) && !mj_ranges_contains(&r, 15) && mj_ranges_contains(&r, 17) && !mj_ranges_contains(&r, 40));
37    uint64_t x;
38    CHECK(mj_ranges_nth(&r, 0, &x) && x == 10);
39    CHECK(mj_ranges_nth(&r, 5, &x) && x == 17);
40    CHECK(!mj_ranges_nth(&r, 28, &x));
41    mj_ranges f = mj_ranges_take_front(&r, 7);
42    word_is(&f, "10-15,17-19");
43    word_is(&r, "19-40");
44    mj_ranges hundred = mj_ranges_of(0, 100), p = parsed("5-10,50,99-200");
45    mj_ranges i = mj_ranges_intersect(&hundred, &p);
46    word_is(&i, "5-10,50-51,99-100");
47    mj_ranges e7 = parsed("7-");
48    CHECK_U(mj_ranges_len(&e7), UINT64_MAX - 7);
49    word_is(&e7, "7-");
50    mj_ranges junk = {0};
51    char *err = NULL;
52    CHECK(!mj_ranges_parse("5-5", &junk, &err));
53    CHECK_S(err, "items \"5-5\": `<n>` (0..n), or `a-b` (half-open), `a-` (endless), `a`, comma-separated");
54    free(err);
55    err = NULL;
56    CHECK(!mj_ranges_parse("x", &junk, &err));
57    free(err);
58    mj_ranges e = parsed("0-");
59    mj_ranges t = mj_ranges_take_front(&e, 3);
60    word_is(&t, "0-3");
61    word_is(&e, "3-");
62    mj_ranges dash = parsed("-");
63    CHECK(mj_ranges_is_empty(&dash));
64    word_is(&dash, "-");
65    mj_ranges u = parsed("1_000-2_000,7");
66    word_is(&u, "7-8,1000-2000");
67    CHECK(!mj_ranges_parse("", &junk, NULL) && !mj_ranges_parse("1,", &junk, NULL) && !mj_ranges_parse("-5", &junk, NULL));
68    CHECK(!mj_ranges_parse("18446744073709551616", &junk, NULL));
69    for (mj_ranges *z[] = {&r, &f, &hundred, &p, &i, &e7, &e, &t, &dash, &u}, **q = z; q < z + 10; q++) mj_ranges_free(*q);
70}
71
72static uint64_t rng = 0x9e3779b97f4a7c15u;
73static uint64_t rnd(uint64_t n) {
74    rng ^= rng << 13;
75    rng ^= rng >> 7;
76    rng ^= rng << 17;
77    return rng % n;
78}
79
80#define N 300
81static void bits_of(const mj_ranges *r, bool *b) {
82    memset(b, 0, N);
83    for (size_t i = 0; i < r->n; i++) {
84        CHECK(r->v[i][0] < r->v[i][1]);
85        if (i) CHECK(r->v[i - 1][1] < r->v[i][0]); /* disjoint and never touching */
86        for (uint64_t x = r->v[i][0]; x < r->v[i][1] && x < N; x++) b[x] = true;
87    }
88}
89
90static void random_ops(void) {
91    mj_ranges r = {0};
92    bool ref[N] = {0}, got[N];
93    for (int step = 0; step < 20000; step++) {
94        uint64_t s = rnd(N), e = s + rnd(20);
95        if (e > N) e = N;
96        switch (rnd(5)) {
97        case 0:
98        case 1:
99            mj_ranges_insert(&r, s, e);
100            for (uint64_t x = s; x < e; x++) ref[x] = true;
101            break;
102        case 2: {
103            bool any = false;
104            for (uint64_t x = s; x < e; x++) any |= ref[x], ref[x] = false;
105            CHECK(mj_ranges_remove(&r, s, e) == any);
106            break;
107        }
108        case 3: {
109            mj_ranges o = mj_ranges_of(s, e), in = mj_ranges_intersect(&r, &o);
110            bits_of(&in, got);
111            for (uint64_t x = 0; x < N; x++) CHECK(got[x] == (ref[x] && x >= s && x < e));
112            mj_ranges_free(&o);
113            mj_ranges_free(&in);
114            break;
115        }
116        default: {
117            uint64_t k = rnd(40), want = 0, n = 0, x;
118            bool has = false;
119            for (uint64_t y = 0; y < N; y++)
120                if (ref[y] && n++ == k) {
121                    want = y;
122                    has = true;
123                    break;
124                }
125            CHECK(mj_ranges_nth(&r, k, &x) == has);
126            if (has) CHECK_U(x, want);
127            CHECK(mj_ranges_contains(&r, s) == ref[s]);
128        }
129        }
130        if (step % 97 == 0) {
131            bits_of(&r, got);
132            uint64_t n = 0;
133            for (int x = 0; x < N; x++) {
134                CHECK(got[x] == ref[x]);
135                n += ref[x];
136            }
137            CHECK_U(mj_ranges_len(&r), n);
138            /* The word form parses back to the same set. */
139            char *w = mj_ranges_word_new(&r);
140            mj_ranges back = {0};
141            CHECK(mj_ranges_parse(w, &back, NULL) && mj_ranges_eq(&back, &r));
142            mj_ranges_free(&back);
143            free(w);
144        }
145    }
146    mj_ranges_free(&r);
147}
148
149int main(void) {
150    the_rust_test();
151    random_ops();
152    return mesh_check_done();
153}