From 67826159fd70c0de32597bf2968344cbae6517ae Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Wed, 15 Mar 2023 15:52:50 -0500 Subject: [PATCH] Added TEST_PERMUTATION, made it easier to reproduce perm/fuzz failures TEST_PERMUTATION/BENCH_PERMUTATION make it possible to map an integer to a specific permutation efficiently. This is helpful since our testing framework really only parameterizes single integers. The exact implementation took a bit of trial and error. It's based on https://stackoverflow.com/a/7919887 and https://stackoverflow.com/a/24257996, but modified to run in O(n) with no extra memory. In the discussion it seemed like this may not actually be possible for lexicographic ordering of permutations, but fortunately we don't care about the specific ordering, only the reproducibility. Here's how it works: 1. First populate an array with all numbers 0-n. 2. Iterate through each index, selecting only from the remaining numbers based on our current permutation. .- i%rem --. v .----+----. [p0 p1 |-> r0 r1 r2 r3] Normally to maintain lexicographic ordering you should have to do a O(n) shift at this step as you remove each number. But instead we can just swap the removed number and number under the index. This effectively shrinks the remaining part of the array, but permutes the numbers a bit. Fortunately, since each successive permutation swaps at the same location, the resulting permutations will be both exhaustive and reproducible, if unintuitive. Now permutation/fuzz tests can reproduce specific failures by defining either -DPERMUTATION=x or -DSEED=x. --- runners/bench_runner.c | 36 + runners/bench_runner.h | 7 + runners/test_runner.c | 36 + runners/test_runner.h | 7 + tests/test_btree.toml | 100 ++- tests/test_rbyd.toml | 1893 +++++++++++++--------------------------- 6 files changed, 756 insertions(+), 1323 deletions(-) diff --git a/runners/bench_runner.c b/runners/bench_runner.c index c19534cc..c419b1b9 100644 --- a/runners/bench_runner.c +++ b/runners/bench_runner.c @@ -603,6 +603,42 @@ uint32_t bench_prng(uint32_t *state) { return x; } +// bench factorial +size_t bench_factorial(size_t x) { + size_t y = 1; + for (size_t i = 2; i <= x; i++) { + y *= i; + } + return y; +} + +// bench array permutations +void bench_permutation(size_t i, uint32_t *buffer, size_t size) { + // https://stackoverflow.com/a/7919887 and + // https://stackoverflow.com/a/24257996 helped a lot with this, but + // changed to run in O(n) with no extra memory. This has a tradeoff + // of generating the permutations in an unintuitive order. + + // initialize array + for (size_t j = 0; j < size; j++) { + buffer[j] = j; + } + + for (size_t j = 0; j < size; j++) { + // swap index with digit + // + // .- i%rem --. + // v .----+----. + // [p0 p1 |-> r0 r1 r2 r3] + // + size_t t = buffer[j + (i % (size-j))]; + buffer[j + (i % (size-j))] = buffer[j]; + buffer[j] = t; + // update i + i /= (size-j); + } +} + // bench recording state static struct lfs_config *bench_cfg = NULL; diff --git a/runners/bench_runner.h b/runners/bench_runner.h index 44f8668c..ca146a30 100644 --- a/runners/bench_runner.h +++ b/runners/bench_runner.h @@ -80,6 +80,13 @@ uint32_t bench_prng(uint32_t *state); #define BENCH_PRNG(state) bench_prng(state) +// generation of specific permutations of an array for exhaustive benching +size_t bench_factorial(size_t x); +void bench_permutation(size_t i, uint32_t *buffer, size_t size); + +#define BENCH_FACTORIAL(x) bench_factorial(x) +#define BENCH_PERMUTATION(i, buffer, size) bench_permutation(i, buffer, size) + // access generated bench defines intmax_t bench_define(size_t define); diff --git a/runners/test_runner.c b/runners/test_runner.c index aea66bd7..0dc3e547 100644 --- a/runners/test_runner.c +++ b/runners/test_runner.c @@ -619,6 +619,42 @@ uint32_t test_prng(uint32_t *state) { return x; } +// test factorial +size_t test_factorial(size_t x) { + size_t y = 1; + for (size_t i = 2; i <= x; i++) { + y *= i; + } + return y; +} + +// test array permutations +void test_permutation(size_t i, uint32_t *buffer, size_t size) { + // https://stackoverflow.com/a/7919887 and + // https://stackoverflow.com/a/24257996 helped a lot with this, but + // changed to run in O(n) with no extra memory. This has a tradeoff + // of generating the permutations in an unintuitive order. + + // initialize array + for (size_t j = 0; j < size; j++) { + buffer[j] = j; + } + + for (size_t j = 0; j < size; j++) { + // swap index with digit + // + // .- i%rem --. + // v .----+----. + // [p0 p1 |-> r0 r1 r2 r3] + // + size_t t = buffer[j + (i % (size-j))]; + buffer[j + (i % (size-j))] = buffer[j]; + buffer[j] = t; + // update i + i /= (size-j); + } +} + // encode our permutation into a reusable id static void perm_printid( diff --git a/runners/test_runner.h b/runners/test_runner.h index fa3fca58..5f797838 100644 --- a/runners/test_runner.h +++ b/runners/test_runner.h @@ -73,6 +73,13 @@ uint32_t test_prng(uint32_t *state); #define TEST_PRNG(state) test_prng(state) +// generation of specific permutations of an array for exhaustive testing +size_t test_factorial(size_t x); +void test_permutation(size_t i, uint32_t *buffer, size_t size); + +#define TEST_FACTORIAL(x) test_factorial(x) +#define TEST_PERMUTATION(i, buffer, size) test_permutation(i, buffer, size) + // access generated test defines intmax_t test_define(size_t define); diff --git a/tests/test_btree.toml b/tests/test_btree.toml index 87782e8b..b547d010 100644 --- a/tests/test_btree.toml +++ b/tests/test_btree.toml @@ -409,13 +409,19 @@ code = ''' [cases.test_btree_push_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] -defines.ITER = 10 +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -566,13 +572,19 @@ code = ''' [cases.test_btree_push_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.W = 5 -defines.ITER = 10 +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -929,14 +941,20 @@ code = ''' [cases.test_btree_update_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] -defines.ITER = 10 +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -1095,14 +1113,20 @@ code = ''' [cases.test_btree_update_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.W = 5 -defines.ITER = 10 +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -1711,14 +1735,20 @@ code = ''' [cases.test_btree_pop_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.REMAINING = [64, 2, 1, 0] -defines.ITER = 10 +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 if = 'N > REMAINING' in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -1915,14 +1945,20 @@ code = ''' defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.REMAINING = [64, 2, 1, 0] defines.W = 5 -defines.ITER = 10 +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 if = 'N > REMAINING' in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -2124,14 +2160,20 @@ code = ''' [cases.test_btree_split_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] -defines.ITER = 10 +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -2275,14 +2317,20 @@ code = ''' [cases.test_btree_split_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] defines.W = 5 -defines.ITER = 10 +defines.SAMPLES = 10 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; const char *uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -2428,13 +2476,19 @@ code = ''' # Some more general fuzz testing [cases.test_btree_general_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] -defines.ITER = 100 +defines.SAMPLES = 100 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; @@ -2540,13 +2594,19 @@ code = ''' [cases.test_btree_general_sparse_fuzz] defines.N = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] defines.W = 5 -defines.ITER = 100 +defines.SAMPLES = 100 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 in = 'lfs.c' code = ''' const char *alphas = "abcdefghijklmnopqrstuvwxyz"; // iterate through severals seeds that we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { + printf("--- seed: %d ---\n", seed); // create lfs here since we need to reset each iteration, we're // space constrained and we can't expect gc to work at this point lfs_t lfs; diff --git a/tests/test_rbyd.toml b/tests/test_rbyd.toml index 392a53f0..26098e34 100644 --- a/tests/test_rbyd.toml +++ b/tests/test_rbyd.toml @@ -2026,6 +2026,9 @@ code = ''' [cases.test_rbyd_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; @@ -2048,19 +2051,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -2093,33 +2096,20 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; + printf("--- summary --\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -2129,6 +2119,9 @@ code = ''' [cases.test_rbyd_multi_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -2153,19 +2146,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -2193,33 +2186,20 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; + printf("--- summary --\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -2415,6 +2395,9 @@ code = ''' [cases.test_rbyd_traverse_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; @@ -2436,17 +2419,16 @@ code = ''' lfs_size_t size_; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -2482,29 +2464,14 @@ code = ''' } lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, &tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT; - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_multi_traverse_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -2528,17 +2495,16 @@ code = ''' lfs_size_t size_; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -2570,24 +2536,6 @@ code = ''' } lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, &tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT; - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' @@ -2595,6 +2543,9 @@ code = ''' # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_update_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -2619,6 +2570,7 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; @@ -2637,17 +2589,16 @@ code = ''' rbyd.block, 0, backup_block, rbyd.off) => 0; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -2682,24 +2633,9 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } @@ -2709,9 +2645,11 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N; + printf("--- summary --\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -2901,6 +2839,9 @@ code = ''' # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_remove_permutations] defines.N = 'range(1, 7)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -2925,19 +2866,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -3022,37 +2963,24 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; + } } // cleanup free(backup_block); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + 2; + printf("--- summary --\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -3064,6 +2992,9 @@ code = ''' # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_remove_traverse_permutations] defines.N = 'range(1, 7)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -3087,17 +3018,16 @@ code = ''' lfs_size_t size_; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -3161,24 +3091,6 @@ code = ''' // cleanup free(backup_block); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' @@ -3661,6 +3573,9 @@ code = ''' # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_remove_all_permutations] defines.N = 'range(1, 7)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -3685,6 +3600,7 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; @@ -3703,17 +3619,16 @@ code = ''' rbyd.block, 0, backup_block, rbyd.off) => 0; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -3759,24 +3674,9 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } @@ -3786,9 +3686,11 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N + 1; + printf("--- summary --\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -3800,7 +3702,10 @@ code = ''' # balancing algorithm [cases.test_rbyd_fuzz_append_removes] defines.N = 'range(1, 33)' -defines.ITER = 1000 +defines.SAMPLES = 1000 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -3826,7 +3731,9 @@ code = ''' uint32_t worst_seed = 0; // iterate through seeds so we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { printf("--- seed: %d ---\n", seed); printf("perm: ["); uint32_t prng = seed; @@ -3914,7 +3821,7 @@ code = ''' } } - // keep track of the worst permutation + // keep track of the worst seed if (rbyd.off > worst_size) { worst_size = rbyd.off; worst_seed = seed; @@ -3924,49 +3831,10 @@ code = ''' free(sim); } - // print the worst seed + size, and rerun it so it's left on the disk - // if used with -ddisk - printf("--- worst ---\n"); - printf("worst_seed: %d\n", worst_seed); - printf("worst_size: %d\n", worst_size); - printf("worst_perm: ["); - uint32_t prng = worst_seed; - for (unsigned i = 0; i < N; i++) { - // choose an attr - uint8_t attr = TEST_PRNG(&prng) % N; - // choose append or remove - if (TEST_PRNG(&prng) & 1) { - printf("a0x%02x=%c", attr, alpha[i % 26]); - } else { - printf("r0x%02x", attr); - } - if (i < N-1) { - printf(", "); - } - } - printf("]\n"); - - // set up rbyd block - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - prng = worst_seed; - for (unsigned i = 0; i < N; i++) { - // choose an attr - uint8_t attr = TEST_PRNG(&prng) % N; - // choose append or remove - if (TEST_PRNG(&prng) & 1) { - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(UATTR(attr), -1, &alpha[i % 26], 1, - NULL)) => 0; - } else { - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(RMUATTR(attr), -1, NULL, 0, - NULL)) => 0; - } - } + // print the worst seed + size + printf("--- summary ---\n"); + printf("worst seed: %d\n", worst_seed); + printf("worst size: %d\n", worst_size); // our tree should be strictly <= 2*log(n)+1, assume tags are strictly // <=12 bytes, note this only holds true with byte-level progs @@ -4316,6 +4184,9 @@ code = ''' [cases.test_rbyd_create_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; @@ -4343,19 +4214,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -4396,33 +4267,20 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -4432,6 +4290,9 @@ code = ''' [cases.test_rbyd_multi_create_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -4461,19 +4322,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -4509,33 +4370,20 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -4773,6 +4621,9 @@ code = ''' [cases.test_rbyd_create_traverse_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; @@ -4803,17 +4654,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -4863,29 +4713,14 @@ code = ''' } lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, &tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT; - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_multi_create_traverse_permutations] defines.N = 'range(1, 8)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -4918,17 +4753,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -4972,24 +4806,6 @@ code = ''' } lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, &tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT; - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' @@ -5595,6 +5411,9 @@ code = ''' [cases.test_rbyd_mixed_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; @@ -5622,19 +5441,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -5689,33 +5508,20 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -5726,6 +5532,9 @@ code = ''' [cases.test_rbyd_multi_mixed_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -5755,19 +5564,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -5814,33 +5623,20 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -6203,6 +5999,9 @@ code = ''' [cases.test_rbyd_mixed_traverse_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 in = 'lfs.c' code = ''' lfs_t lfs; @@ -6233,17 +6032,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -6313,30 +6111,15 @@ code = ''' } lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, &tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT; - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_multi_mixed_traverse_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -6369,17 +6152,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -6441,24 +6223,6 @@ code = ''' } lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, &tag_, &id_, NULL, &off_, &size_) => LFS_ERR_NOENT; - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' @@ -6467,6 +6231,9 @@ code = ''' [cases.test_rbyd_mixed_update_permutations] defines.N = 'range(1, 4)' defines.M = 'range(1, 3)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -6496,6 +6263,7 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; @@ -6521,17 +6289,16 @@ code = ''' rbyd.block, 0, backup_block, rbyd.off) => 0; // test all permutations of a given size - uint8_t perm[N*M]; - unsigned stack[N*M]; - for (uint8_t i = 0; i < N*M; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N*M); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N*M]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N*M); - unsigned i = 1; - while (i < N*M) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N*M; j++) { if (j > 0) { printf(", "); @@ -6569,24 +6336,9 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } @@ -6596,9 +6348,11 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M + N*M; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -6611,6 +6365,9 @@ code = ''' [cases.test_rbyd_mixed_remove_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -6644,19 +6401,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint8_t perm[N]; - unsigned stack[N]; - for (uint8_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -6780,37 +6537,24 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; + } } // cleanup free(backup_block); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N+N*M + 2; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -6823,6 +6567,9 @@ code = ''' [cases.test_rbyd_mixed_remove_all_permutations] defines.N = 'range(1, 4)' defines.M = 'range(1, 3)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -6852,6 +6599,7 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; @@ -6877,17 +6625,16 @@ code = ''' rbyd.block, 0, backup_block, rbyd.off) => 0; // test all permutations of a given size - uint8_t perm[N*M]; - unsigned stack[N*M]; - for (uint8_t i = 0; i < N*M; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N*M); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N*M]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N*M); - unsigned i = 1; - while (i < N*M) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N*M; j++) { if (j > 0) { printf(", "); @@ -6923,24 +6670,9 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint8_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint8_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } @@ -6950,9 +6682,11 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + N*M + N*M; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -7457,6 +7191,9 @@ code = ''' # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_permutations] defines.N = 'range(1, 7)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -7486,19 +7223,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -7585,37 +7322,24 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; + } } // cleanup free(backup_block); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N + 2; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -7628,6 +7352,9 @@ code = ''' [cases.test_rbyd_delete_range_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -7657,19 +7384,19 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -7792,37 +7519,24 @@ code = ''' } // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; + } } // cleanup free(backup_block); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N+N*M + 1 + 1+M; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -7834,6 +7548,9 @@ code = ''' # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_traverse_permutations] defines.N = 'range(1, 7)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -7866,17 +7583,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -7954,24 +7670,6 @@ code = ''' // cleanup free(backup_block); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' @@ -7980,6 +7678,9 @@ code = ''' [cases.test_rbyd_delete_traverse_range_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -8012,17 +7713,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -8121,24 +7821,6 @@ code = ''' // cleanup free(backup_block); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' @@ -8413,6 +8095,9 @@ code = ''' # use emubd's copy-on-write copy to speed this up significantly [cases.test_rbyd_delete_all_permutations] defines.N = 'range(1, 7)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -8442,6 +8127,7 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; @@ -8462,17 +8148,16 @@ code = ''' rbyd.block, 0, backup_block, rbyd.off) => 0; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -8529,24 +8214,9 @@ code = ''' => LFS_ERR_NOENT; // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } @@ -8556,9 +8226,11 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + 2*N + 1; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -8571,6 +8243,9 @@ code = ''' [cases.test_rbyd_delete_all_range_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -8600,6 +8275,7 @@ code = ''' // keep track of the worst case log size lfs_size_t worst_size = 0; + size_t worst_perm_i = 0; // create one consistent block rbyd = init_rbyd; @@ -8626,17 +8302,16 @@ code = ''' rbyd.block, 0, backup_block, rbyd.off) => 0; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -8707,24 +8382,9 @@ code = ''' => LFS_ERR_NOENT; // keep track of the worst size - worst_size = lfs_max(worst_size, rbyd.off); - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; + if (rbyd.off > worst_size) { + worst_size = rbyd.off; + worst_perm_i = perm_i; } } @@ -8734,9 +8394,11 @@ code = ''' // test that tree is self-balancing, we should be strictly bounded // by height <= 2*log(n)+1, assume tags are strictly <=12 bytes lfs_size_t n = 1 + N+N*M + N + 1+M; + printf("--- summary ---\n"); + printf("worst permutation: %zd\n", worst_perm_i); printf("worst size: %u B (N=%u, estimate=%u)\n", worst_size, n, 12*n*(2*lfs_nlog2(n)+1)+4); - printf("avg height: %u B (N=%u, estimate=%u)\n", + printf("worst avg height: %u B (N=%u, estimate=%u)\n", worst_size / n, n, 12*(2*lfs_nlog2(n)+1)+4); // note this only holds true with byte-level progs if (PROG_SIZE == 1) { @@ -8748,7 +8410,10 @@ code = ''' # balancing algorithm [cases.test_rbyd_fuzz_create_deletes] defines.N = 'range(1, 33)' -defines.ITER = 1000 +defines.SAMPLES = 1000 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -8774,7 +8439,9 @@ code = ''' uint32_t worst_seed = 0; // iterate through seeds so we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { printf("--- seed: %d ---\n", seed); printf("perm: ["); uint32_t prng = seed; @@ -8862,7 +8529,7 @@ code = ''' assert(memcmp(&sim[id], buffer, 1) == 0); } - // keep track of the worst permutation + // keep track of the worst seed if (rbyd.off > worst_size) { worst_size = rbyd.off; worst_seed = seed; @@ -8874,54 +8541,9 @@ code = ''' // print the worst seed + size, and rerun it so it's left on the disk // if used with -ddisk - printf("--- worst ---\n"); + printf("--- summary ---\n"); printf("worst_seed: %d\n", worst_seed); printf("worst_size: %d\n", worst_size); - printf("worst_perm: ["); - uint32_t prng = worst_seed; - lfs_size_t count = 0; - for (unsigned i = 0; i < N; i++) { - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose create or delete - if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) { - printf("c%d=%c", id, alpha[i % 26]); - count += 1; - } else { - printf("d%d", id); - count -= 1; - } - if (i < N-1) { - printf(", "); - } - } - printf("]\n"); - - // set up rbyd block - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - prng = worst_seed; - count = 0; - for (unsigned i = 0; i < N; i++) { - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose create or delete - if (id == (lfs_ssize_t)count || (TEST_PRNG(&prng) & 1)) { - count += 1; - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(GROW, id, NULL, 1, - LFSR_ATTR(MKREG, id, &alpha[i % 26], 1, - NULL))) => 0; - } else { - count -= 1; - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(SHRINK, id, NULL, 1, - NULL)) => 0; - } - } // our tree should be strictly <= 2*log(n)+1, assume tags are strictly // <=12 bytes, note this only holds true with byte-level progs @@ -9378,6 +9000,9 @@ code = ''' [cases.test_rbyd_grow_permutations] defines.N = 'range(1, 8)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -9411,17 +9036,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -9461,30 +9085,15 @@ code = ''' lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, j*W+W-1, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_grow_traverse_permutations] defines.N = 'range(1, 8)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -9518,17 +9127,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -9573,24 +9181,6 @@ code = ''' } lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, &tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT; - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' @@ -10540,6 +10130,9 @@ code = ''' [cases.test_rbyd_mixed_grow_permutations] defines.N = 'range(1, 8)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -10573,17 +10166,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -10647,30 +10239,15 @@ code = ''' lfsr_rbyd_get(&lfs, &rbyd, LFSR_TAG_MKREG, j*W+W-1, buffer, 4) => 4; assert(memcmp(buffer, names[j % 6], 4) == 0); } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_mixed_grow_traverse_permutations] defines.N = 'range(1, 8)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -10704,17 +10281,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -10784,30 +10360,15 @@ code = ''' } lfsr_rbyd_lookup(&lfs, &rbyd, lfsr_tag_next(tag_), id_, &tag_, &id_, &weight_, &off_, &size_) => LFS_ERR_NOENT; - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_sparse_grow_permutations] defines.N = 'range(1, 7)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -10841,17 +10402,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -10931,30 +10491,15 @@ code = ''' assert(memcmp(buffer, names[k % 6], 4) == 0); } } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_sparse_shrink_permutations] defines.N = 'range(1, 7)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -10988,17 +10533,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -11078,30 +10622,15 @@ code = ''' assert(memcmp(buffer, names[k % 6], 4) == 0); } } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_sparse_delete_permutations] defines.N = 'range(1, 7)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -11135,17 +10664,16 @@ code = ''' uint8_t buffer[6]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -11256,30 +10784,15 @@ code = ''' } } } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_sparse_attr_permutations] defines.N = 'range(1, 7)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -11313,17 +10826,16 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -11453,24 +10965,6 @@ code = ''' } } } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' @@ -11480,7 +10974,10 @@ code = ''' [cases.test_rbyd_fuzz_mixed] defines.N = 'range(1, 33)' defines.M = 3 -defines.ITER = 1000 +defines.SAMPLES = 1000 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -11506,7 +11003,9 @@ code = ''' uint32_t worst_seed = 0; // iterate through seeds so we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { printf("--- seed: %d ---\n", seed); printf("perm: ["); uint32_t prng = seed; @@ -11638,7 +11137,7 @@ code = ''' assert(memcmp(&sim[id*(M+1)], buffer, 1) == 0); } - // keep track of the worst permutation + // keep track of the worst seed if (rbyd.off > worst_size) { worst_size = rbyd.off; worst_seed = seed; @@ -11650,77 +11149,9 @@ code = ''' // print the worst seed + size, and rerun it so it's left on the disk // if used with -ddisk - printf("--- worst ---\n"); + printf("--- summary ---\n"); printf("worst_seed: %d\n", worst_seed); printf("worst_size: %d\n", worst_size); - printf("worst_perm: ["); - uint32_t prng = worst_seed; - lfs_size_t count = 0; - for (unsigned i = 0; i < N; i++) { - // choose create/delete or attr append/remove - uint8_t op = TEST_PRNG(&prng) % 4; - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose an attr - uint8_t u = TEST_PRNG(&prng) % M; - - if (id == (lfs_ssize_t)count || op == 0) { - printf("c%d=%c", id, alpha[i % 26]); - count += 1; - } else if (op == 1) { - printf("d%d", id); - count -= 1; - } else if (op == 2) { - printf("a%d,%d=%c", id, u, alpha[i % 26]); - } else if (op == 3) { - printf("r%d,%d", id, u); - } - if (i < N-1) { - printf(", "); - } - } - printf("]\n"); - - // set up rbyd block - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - prng = worst_seed; - count = 0; - for (unsigned i = 0; i < N; i++) { - // choose create/delete or attr append/remove - uint8_t op = TEST_PRNG(&prng) % 4; - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose an attr - uint8_t u = TEST_PRNG(&prng) % M; - - if (id == (lfs_ssize_t)count || op == 0) { - count += 1; - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(GROW, id, NULL, 1, - LFSR_ATTR(MKREG, id, &alpha[i % 26], 1, - NULL))) => 0; - } else if (op == 1) { - count -= 1; - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(SHRINK, id, NULL, 1, - NULL)) => 0; - } else if (op == 2) { - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(UATTR(u), id, &alpha[i % 26], 1, - NULL)) => 0; - - } else if (op == 3) { - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(RMUATTR(u), id, NULL, 0, - NULL)) => 0; - } - } // our tree should be strictly <= 2*log(n)+1, assume tags are strictly // <=12 bytes, note this only holds true with byte-level progs @@ -11732,7 +11163,10 @@ code = ''' [cases.test_rbyd_fuzz_sparse] defines.N = 'range(1, 33)' defines.W = 5 -defines.ITER = 1000 +defines.SAMPLES = 1000 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -11763,7 +11197,9 @@ code = ''' uint32_t worst_seed = 0; // iterate through seeds so we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { printf("--- seed: %d ---\n", seed); printf("perm: ["); uint32_t prng = seed; @@ -11924,7 +11360,7 @@ code = ''' assert(memcmp(&sim[id], buffer, 1) == 0); } - // keep track of the worst permutation + // keep track of the worst seed if (rbyd.off > worst_size) { worst_size = rbyd.off; worst_seed = seed; @@ -11937,114 +11373,9 @@ code = ''' // print the worst seed + size, and rerun it so it's left on the disk // if used with -ddisk - printf("--- worst ---\n"); + printf("--- summary ---\n"); printf("worst_seed: %d\n", worst_seed); printf("worst_size: %d\n", worst_size); - printf("worst_perm: ["); - uint32_t prng = worst_seed; - lfs_size_t count = 0; - for (unsigned i = 0; i < N; i++) { - // choose create/delete/grow/shrink - uint8_t op = TEST_PRNG(&prng) % 4; - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose a weight - lfs_size_t weight = 1 + (TEST_PRNG(&prng) % (W-1)); - - if (id == (lfs_ssize_t)count || op == 0) { - printf("c%dw%d=%c", id, weight, alpha[i % 26]); - count += 1; - } else if (op == 1) { - printf("d%d", id); - count -= 1; - } else if (op == 2) { - printf("g%dw%d", id, weight); - } else if (op == 3) { - printf("s%dw%d", id, weight); - } - if (i < N-1) { - printf(", "); - } - } - printf("]\n"); - - // note we still need sim here, since we use it to calculate shrink limits - - // set up a simulation to compare against, fun fact this performs - // worst than our actual rbyd block! - char *sim = malloc(N); - lfs_size_t *sim_weights = malloc(N*sizeof(lfs_size_t)); - memset(sim, 0, N); - memset(sim_weights, 0, N*sizeof(lfs_size_t)); - - // set up rbyd block - rbyd = init_rbyd; - lfs_bd_erase(&lfs, rbyd.block) => 0; - - prng = worst_seed; - count = 0; - for (unsigned i = 0; i < N; i++) { - // choose create/delete/grow/shrink - uint8_t op = TEST_PRNG(&prng) % 4; - // choose an id - lfs_ssize_t id = TEST_PRNG(&prng) % (count+1); - // choose a weight - lfs_size_t weight = 1 + (TEST_PRNG(&prng) % (W-1)); - - // calculate actual id in rbyd space - lfs_ssize_t weighted_id = 0; - for (lfs_ssize_t j = 0; j < id; j++) { - weighted_id += sim_weights[j]; - } - - if (id == (lfs_ssize_t)count || op == 0) { - // update our sim - memmove(sim+id+1, sim+id, count-id); - memmove(sim_weights+id+1, sim_weights+id, - (count-id)*sizeof(lfs_size_t)); - sim[id] = alpha[i % 26]; - sim_weights[id] = weight; - count += 1; - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(GROW, weighted_id, NULL, weight, - LFSR_ATTR(MKREG, weighted_id+weight-1, &alpha[i % 26], 1, - NULL))) => 0; - } else if (op == 1) { - // get the correct weight from the sim - weight = sim_weights[id]; - // update our sim - memmove(sim+id, sim+id+1, count-id-1); - memmove(sim_weights+id, sim_weights+id+1, - (count-id-1)*sizeof(lfs_size_t)); - count -= 1; - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(SHRINK, weighted_id, NULL, weight, - NULL)) => 0; - } else if (op == 2) { - // update our sim - sim_weights[id] += weight; - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(GROW, weighted_id, NULL, weight, - NULL)) => 0; - } else if (op == 3) { - // don't let shrink go to zero here! this is already hard enough - // to simulate - weight = lfs_min(weight, sim_weights[id]-1); - // update our sim - sim_weights[id] -= weight; - // update our rbyd - lfsr_rbyd_commit(&lfs, &rbyd, - LFSR_ATTR(SHRINK, weighted_id, NULL, weight, - NULL)) => 0; - } - } - - // cleanup - free(sim); - free(sim_weights); // our tree should be strictly <= 2*log(n)+1, assume tags are strictly // <=12 bytes, note this only holds true with byte-level progs @@ -12059,6 +11390,9 @@ code = ''' [cases.test_rbyd_unwritten_permutations] defines.N = 'range(1, 7)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -12081,20 +11415,19 @@ code = ''' lfsr_data_t data_ = LFSR_DATA_NULL; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // test each number of written/unwritten tags, this gives us a quick // way to test several unwritten situations for (unsigned w = 0; w <= N; w++) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -12146,30 +11479,15 @@ code = ''' lfsr_tag_next(tag_), id_, &tag_, &id_, &data_) => LFS_ERR_NOENT; } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_unwritten_fuzz] defines.N = 'range(1, 13)' -defines.ITER = 1000 +defines.SAMPLES = 1000 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -12191,7 +11509,9 @@ code = ''' uint8_t buffer[4]; // iterate through seeds so we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { // test each number of written/unwritten tags, this gives us a quick // way to test several unwritten situations for (unsigned w = 0; w <= N; w++) { @@ -12320,6 +11640,9 @@ code = ''' [cases.test_rbyd_unwritten_create_permutations] defines.N = 'range(1, 7)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -12351,20 +11674,19 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // test each number of written/unwritten tags, this gives us a quick // way to test several unwritten situations for (unsigned w = 0; w <= N; w++) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -12425,30 +11747,15 @@ code = ''' lfsr_tag_next(tag_), id_, &tag_, &id_, &data_) => LFS_ERR_NOENT; } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_unwritten_create_fuzz] defines.N = 'range(1, 13)' -defines.ITER = 1000 +defines.SAMPLES = 1000 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -12470,7 +11777,9 @@ code = ''' uint8_t buffer[4]; // iterate through seeds so we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { // test each number of written/unwritten tags, this gives us a quick // way to test several unwritten situations for (unsigned w = 0; w <= N; w++) { @@ -12600,6 +11909,9 @@ code = ''' [cases.test_rbyd_unwritten_mixed_permutations] defines.N = 'range(1, 7)' defines.M = 'range(1, 4)' +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -12631,20 +11943,19 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // test each number of written/unwritten tags, this gives us a quick // way to test several unwritten situations for (signed w = -1; w <= N; w++) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -12750,31 +12061,16 @@ code = ''' lfsr_tag_next(tag_), id_, &tag_, &id_, &data_) => LFS_ERR_NOENT; } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_unwritten_mixed_fuzz] defines.N = 'range(1, 13)' defines.M = 3 -defines.ITER = 1000 +defines.SAMPLES = 1000 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -12796,7 +12092,9 @@ code = ''' uint8_t buffer[4]; // iterate through seeds so we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { // test each number of written/unwritten tags, this gives us a quick // way to test several unwritten situations for (unsigned w = 0; w <= N; w++) { @@ -12990,6 +12288,9 @@ code = ''' [cases.test_rbyd_unwritten_sparse_permutations] defines.N = 'range(1, 7)' defines.W = 5 +# -1 => exhaust all permutations +# n => reproduce a specific permutation +defines.PERMUTATION = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -13021,20 +12322,19 @@ code = ''' uint8_t buffer[4]; // test all permutations of a given size - uint16_t perm[N]; - unsigned stack[N]; - for (uint16_t i = 0; i < N; i++) { - perm[i] = i; - stack[i] = 0; - } + size_t perm_count = TEST_FACTORIAL(N); + for (size_t i = 0; + i < (PERMUTATION == -1 ? perm_count : 1); + i++) { + uint32_t perm[N]; + size_t perm_i = PERMUTATION == -1 ? i : (size_t)PERMUTATION; + TEST_PERMUTATION(perm_i, perm, N); - unsigned i = 1; - while (i < N) { // test each number of written/unwritten tags, this gives us a quick // way to test several unwritten situations for (unsigned w = 0; w <= N; w++) { // print permutation to help debugging - printf("--- permutation: ["); + printf("--- permutation: %zd [", perm_i); for (unsigned j = 0; j < N; j++) { if (j > 0) { printf(", "); @@ -13095,31 +12395,16 @@ code = ''' lfsr_tag_next(tag_), id_, &tag_, &id_, &data_) => LFS_ERR_NOENT; } - - // next permutation using Heap's algorithm - if (stack[i] < i) { - if (i % 2 == 0) { - uint16_t t = perm[0]; - perm[0] = perm[i]; - perm[i] = t; - } else { - uint16_t t = perm[stack[i]]; - perm[stack[i]] = perm[i]; - perm[i] = t; - } - stack[i] += 1; - i = 1; - } else { - stack[i] = 0; - i += 1; - } } ''' [cases.test_rbyd_unwritten_sparse_fuzz] defines.N = 'range(1, 13)' defines.W = 5 -defines.ITER = 1000 +defines.SAMPLES = 1000 +# -1 => all pseudo-random seeds +# n => reproduce a specific seed +defines.SEED = -1 # large progs take too long for now if = 'PROG_SIZE < 512' in = 'lfs.c' @@ -13144,7 +12429,9 @@ code = ''' uint8_t buffer[4]; // iterate through seeds so we can reproduce easily - for (uint32_t seed = 1; seed < ITER+1; seed++) { + for (uint32_t seed = (SEED == -1 ? 1 : SEED); + (SEED == -1 ? seed < SAMPLES+1 : seed == SEED); + seed++) { // test each number of written/unwritten tags, this gives us a quick // way to test several unwritten situations for (unsigned w = 0; w <= N; w++) {