diff options
Diffstat (limited to 'core/test-module.c')
| -rw-r--r-- | core/test-module.c | 124 |
1 files changed, 121 insertions, 3 deletions
diff --git a/core/test-module.c b/core/test-module.c index 3c9dfa1..846d7aa 100644 --- a/core/test-module.c +++ b/core/test-module.c @@ -2266,7 +2266,7 @@ static void gen_own(Gen *g, int depth) { * key the row gives, ANDed with the item's own tests, and an own value. */ static void gen_shaped(Gen *g, int list) { static const uint8_t relations[] = {PURE_OP_EQ, PURE_OP_EQ, PURE_OP_LT, PURE_OP_LE, PURE_OP_GT, PURE_OP_GE}; - unsigned how = roll(g, 5) < 2 ? PURE_FOLD_COUNT : roll(g, 5), before = roll(g, 3) ? 0 : 1 + roll(g, 2), + unsigned how = roll(g, 5) < 2 ? PURE_FOLD_COUNT : roll(g, 6), before = roll(g, 3) ? 0 : 1 + roll(g, 2), after = roll(g, 3) ? 0 : 1; size_t at = g->count; put_op(g, PURE_OP_FOLD, 0, 0); @@ -2305,7 +2305,7 @@ static void gen_shaped(Gen *g, int list) { /* A fold over list leaving a number (first's index as one), in a column of L (outer) or not. */ static void gen_fold(Gen *g, int list, int outer) { - unsigned how = roll(g, 5); + unsigned how = roll(g, 6); /* count, sum, max, min, first, distinct */ size_t at = g->count; put_op(g, PURE_OP_FOLD, 0, 0); int fold_list = g->fold_list, fold_outer = g->outer; @@ -2424,7 +2424,7 @@ static void lane_folds(void) { pure_module_release(&module_a); pure_module_release(&module_b); } - CHECK(planned > folds / 2 && faults > 100 && cells > 1000 && shaped[PURE_SHAPE_GROUP] > 150 && + CHECK(planned > folds / 2 && faults > 100 && cells > 1000 && shaped[PURE_SHAPE_GROUP] > 80 && shaped[PURE_SHAPE_RANK] > 100 && passes > 60); printf("{\"lane_folds\":%u,\"planned\":%u,\"groups\":%u,\"ranks\":%u,\"passes\":%u,\"faults_alike\":%u," "\"cells\":%u}\n", folds, planned, shaped[PURE_SHAPE_GROUP], shaped[PURE_SHAPE_RANK], passes, faults, cells); @@ -2876,6 +2876,123 @@ static void locals(void) { printf("{\"locals\":{\"events\":80}}\n"); } +/* ---- Key ranges ----------------------------------------------------------- + * Folds, a filter and walks whose tests bound a keyed list's key read only + * the items in bounds; each answers as the same module reading every item: + * two bounds, one, an equality, a bound on the right, a test that may fault + * (no range), a walk that changes and removes items in its range, and a + * walk whose body moves its own bound (no range). */ +enum { R_LO = 0, R_HI = 1, R_N = 2, R_L = 3 }; +static const S range_numbers[] = {{"lo", 0}, {"hi", 0}, {"n", 0}}; +static const T range_lists[] = {{"l", 3 | 1 << 8, 0}}; +static const RF range_fields[] = {{"key", 0, 0}, {"a", 0, 0}, {"b", 0, 0}}; +static const R range_records[] = {{"K", range_fields, 3}}; +static const F bounds_fields[] = {{"lo", 3}, {"hi", 3}}; +static const I range_inputs[] = {{"bounds", NULL, PURE_KIND_MESSAGE, false, bounds_fields, 2}, KEY("walk", "KeyW"), + KEY("drift", "KeyD")}; +#define KEYED(field) {IG, ITEM(R_L, field), 0} +static const O range_code[] = { + /* 0 v0 = count(x in l where x.key >= lo && x.key < hi) */ + {FOLD, FOLDING(PURE_FOLD_COUNT, R_L, 9), 0}, {ROW, R_L, 0}, KEYED(0), {L, R_LO, 0}, {PURE_OP_GE, 0, 0}, + {ROW, R_L, 0}, KEYED(0), {L, R_HI, 0}, {PURE_OP_LT, 0, 0}, {PURE_OP_AND, 0, 0}, + /* 10 v1 = sum(x.a for x in l where x.key > lo && x.key <= hi && x.b > 0) */ + {FOLD, FOLDING(PURE_FOLD_SUM, R_L, 16), 0}, {ROW, R_L, 0}, KEYED(1), + {ROW, R_L, 0}, KEYED(0), {L, R_LO, 0}, {GT, 0, 0}, {ROW, R_L, 0}, KEYED(0), {L, R_HI, 0}, {PURE_OP_LE, 0, 0}, + {PURE_OP_AND, 0, 0}, {ROW, R_L, 0}, KEYED(2), {C, 0, 0}, {GT, 0, 0}, {PURE_OP_AND, 0, 0}, + /* 27 v2 = first(x in l where x.key == lo).position */ + {FOLD, FOLDING(PURE_FOLD_FIRST, R_L, 4), 0}, {ROW, R_L, 0}, KEYED(0), {L, R_LO, 0}, {EQ, 0, 0}, {XN, 0, 0}, + /* 33 v3 = min(x.b for x in l where hi > x.key && x.key >= lo) */ + {FOLD, FOLDING(PURE_FOLD_MIN, R_L, 11), 0}, {ROW, R_L, 0}, KEYED(2), + {L, R_HI, 0}, {ROW, R_L, 0}, KEYED(0), {GT, 0, 0}, {ROW, R_L, 0}, KEYED(0), {L, R_LO, 0}, {PURE_OP_GE, 0, 0}, + {PURE_OP_AND, 0, 0}, + /* 45 v4 = count(x in l where x.key >= lo && x.a / x.b > 0): a test that may fault */ + {FOLD, FOLDING(PURE_FOLD_COUNT, R_L, 12), 0}, {ROW, R_L, 0}, KEYED(0), {L, R_LO, 0}, {PURE_OP_GE, 0, 0}, + {ROW, R_L, 0}, KEYED(1), {ROW, R_L, 0}, KEYED(2), {DIV, 0, 0}, {C, 0, 0}, {GT, 0, 0}, {PURE_OP_AND, 0, 0}, + /* 58 table t: each x in l where x.key >= lo && x.key < hi && x.a > 1 */ + {ROW, R_L, 0}, KEYED(0), {L, R_LO, 0}, {PURE_OP_GE, 0, 0}, {ROW, R_L, 0}, KEYED(0), {L, R_HI, 0}, {PURE_OP_LT, 0, 0}, + {PURE_OP_AND, 0, 0}, {ROW, R_L, 0}, KEYED(1), {C, 0, 1}, {GT, 0, 0}, {PURE_OP_AND, 0, 0}, + /* 72 column c = x.b */ + {ROW, R_L, 0}, KEYED(2), + /* 74 on bounds: lo = bounds.lo; hi = bounds.hi */ + {FIELD, 0, 0}, {ST, R_LO, 0}, {FIELD, 1, 0}, {ST, R_HI, 0}, + /* 78 on walk: for x in l where x.key >= lo && x.key < hi { x.a = x.a + 1; if x.b < 0 { l.remove(x) } } */ + {EACH, WALKING(R_L, 23), 0}, + {ROW, R_L, 0}, KEYED(0), {L, R_LO, 0}, {PURE_OP_GE, 0, 0}, {ROW, R_L, 0}, KEYED(0), {L, R_HI, 0}, {PURE_OP_LT, 0, 0}, + {PURE_OP_AND, 0, 0}, {JZ, 102, 0}, + {ROW, R_L, 0}, {ROW, R_L, 0}, KEYED(1), {C, 0, 1}, {ADD, 0, 0}, {IS, ITEM(R_L, 1), 0}, + {ROW, R_L, 0}, KEYED(2), {C, 0, 0}, {PURE_OP_LT, 0, 0}, {JZ, 102, 0}, {ROW, R_L, 0}, {LR, R_L, 0}, + /* 102 on drift: for x in l where x.key < hi { hi = hi - 1; n = n + 1 }: its bound moves */ + {EACH, WALKING(R_L, 13), 0}, + {ROW, R_L, 0}, KEYED(0), {L, R_HI, 0}, {PURE_OP_LT, 0, 0}, {JZ, 116, 0}, + {L, R_HI, 0}, {C, 0, 1}, {PURE_OP_SUB, 0, 0}, {ST, R_HI, 0}, {L, R_N, 0}, {C, 0, 1}, {ADD, 0, 0}, {ST, R_N, 0}, +}; +#undef KEYED +static const V range_views[] = {{"v0", 0, 10}, {"v1", 10, 17}, {"v2", 27, 6}, {"v3", 33, 12}, {"v4", 45, 13}}; +static const W range_tables[] = {{"t", 2 | (R_L + 1) << 8, 0, 58, 14}, {"c", 0, 6, 72, 2}}; +static const H range_handlers[] = {{1, 74, 4}, {3, 78, 24}, {5, 102, 14}}; + +static bool same_lists(const PureApp *a, const PureApp *b) { + const PureList *x = a->lists[R_L], *y = b->lists[R_L]; + if (pure_list_count(x) != pure_list_count(y)) return false; + for (size_t i = 0; i < pure_list_count(x); ++i) + for (unsigned f = 0; f < 3; ++f) + if (pure_list_get(x, i, f).num != pure_list_get(y, i, f).num) return false; + return true; +} + +static void ranges(void) { + Spec spec = {range_numbers, 3, range_inputs, 3, range_views, 5, range_handlers, 3, range_code, LEN(range_code), NULL, 0, + range_lists, 1, NULL, 0, NULL, 0, range_records, 1, range_tables, 2}; + CHECK(load(spec, &module_a) == PURE_OK && load(spec, &module_b) == PURE_OK); + CHECK(module_a.code[0].range && module_a.code[10].range && module_a.code[27].range && module_a.code[33].range && + !module_a.code[45].range && module_a.view_range[5] && module_a.code[78].range && !module_a.code[102].range); + for (uint32_t i = 0; i < module_b.code_count; ++i) module_b.code[i].range = 0; + memset(module_b.view_range, 0, sizeof(module_b.view_range)); + uint32_t seed = 0x7a45e11u; + unsigned compared = 0, walked = 0; + for (unsigned round = 0; round < 60; ++round) { + PureApp a, b; + CHECK(pure_app_start(&a, &module_a, (PureData){0}) == PURE_OK && pure_app_start(&b, &module_b, (PureData){0}) == PURE_OK); + double key = (double)(next_random(&seed) % 5); + for (size_t i = 0, items = next_random(&seed) % 400; i < items; ++i) { + key += 1 + next_random(&seed) % 3; + PureCell cells[3] = {{key, NULL, 0}, {(double)(next_random(&seed) % 9) - 3, NULL, 0}, + {(double)(next_random(&seed) % 7) - 3, NULL, 0}}; + CHECK(pure_list_insert(a.lists[R_L], i, cells, NULL) == PURE_OK && pure_list_insert(b.lists[R_L], i, cells, NULL) == PURE_OK); + } + for (unsigned step = 0; step < 12; ++step) { + unsigned char payload[16]; + double lo = (double)(next_random(&seed) % 900) - 50, hi = lo + (double)(next_random(&seed) % 400) - 20; + put_double(payload, lo); + put_double(payload + 8, hi); + CHECK(pure_app_message(&a, 0, NULL, -1, (PureSpan){payload, 16}) == PURE_OK && + pure_app_message(&b, 0, NULL, -1, (PureSpan){payload, 16}) == PURE_OK); + for (uint32_t v = 0; v < 5; ++v) { + double x = 0, y = 0; + bool ok_a = pure_app_view(&a, v, &x), ok_b = pure_app_view(&b, v, &y); + if (!same_number(ok_a, x, ok_b, y)) { + fprintf(stderr, "range round %u view %u: %d %g / %d %g\n", round, v, ok_a, x, ok_b, y); + abort(); + } + ++compared; + } + size_t rows = pure_app_rows(&a, 5); + CHECK(rows == pure_app_rows(&b, 5)); + for (size_t row = 0; row < rows; ++row) { + double x, y; + CHECK(pure_app_cell(&a, 6, row, &x) && pure_app_cell(&b, 6, row, &y) && x == y); + } + unsigned input = 1 + next_random(&seed) % 2; + PureResult went_a = pure_app_input(&a, input, PURE_EDGE_DOWN), went_b = pure_app_input(&b, input, PURE_EDGE_DOWN); + CHECK(went_a == went_b && same_lists(&a, &b) && !memcmp(a.values, b.values, 3 * sizeof(double))); + walked += input == 1; + } + pure_app_stop(&a); + pure_app_stop(&b); + } + printf("{\"ranges\":{\"compared\":%u,\"walks\":%u}}\n", compared, walked); +} + /* Damaged fold and filter modules: whatever loads runs safely. */ static void fold_mutations(void) { static unsigned char valid[65536], bytes[65536]; @@ -3801,6 +3918,7 @@ int main(void) { walk_mutations(); view_arguments(); locals(); + ranges(); keyed(); keyed_mutations(); snapshots(); |
