diff --git a/include/sl.h b/include/sl.h index 88e7f36..91ee9a1 100644 --- a/include/sl.h +++ b/include/sl.h @@ -61,10 +61,10 @@ * * A skiplist is a sorted list with O(log(n)) on average for most operations. * It is a probabilistic datastructure, meaning that it does not guarantee - * O(log(n)) it approximates it over time. This implementation includes - * improves the probability by integrating the splay list algorithm for - * rebalancing trading off a bit of computational overhead and code complexity - * for a nearly always optimal, or "perfect" skiplist. + * O(log(n)) it approximates it over time. This implementation improves the + * probability by integrating the splay list algorithm for rebalancing trading + * off a bit of computational overhead and code complexity for a nearly always + * optimal, or "perfect" skiplist. * * Conceptually, a skiplist is arranged as follows: * @@ -235,8 +235,8 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li #define __SKIP_IS_LAST_ENTRY_T2B() if (lvl == 0) #define __SKIP_ALL_ENTRIES_B2T(field, elm) for (size_t lvl = 0; lvl < slist->slh_max_height; lvl++) -#define __SKIP_ENTRIES_B2T(field, elm) for (size_t lvl = 0; lvl < elm->field.sle_height; lvl++) -#define __SKIP_ENTRIES_B2T_FROM(field, elm, off) for (size_t lvl = off; lvl < elm->field.sle_height; lvl++) +#define __SKIP_ENTRIES_B2T(field, elm) for (size_t lvl = 0; lvl <= elm->field.sle_height; lvl++) +#define __SKIP_ENTRIES_B2T_FROM(field, elm, off) for (size_t lvl = off; lvl <= elm->field.sle_height; lvl++) #define __SKIP_IS_LAST_ENTRY_B2T() if (lvl + 1 == elm->field.sle_height) /* Iterate over the subtree to the left (v, or 'lt') and right (u) or "CHu" and "CHv". */ @@ -263,6 +263,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li struct decl { \ size_t slh_length, slh_max_height; \ void *slh_aux; \ + int slh_prng_state; \ decl##_node_t *slh_head; \ decl##_node_t *slh_tail; \ struct { \ @@ -289,6 +290,19 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li size_t pu; /* sum of hits from intersection to level[1] */ \ } __skiplist_path_##decl##_t; \ \ + /* Xorshift algorithm for PRNG */ \ + static uint32_t __##decl##_xorshift32(int *state) \ + { \ + uint32_t x = *state; \ + if (x == 0) \ + x = 123456789; \ + x ^= x << 13; \ + x ^= x >> 17; \ + x ^= x << 5; \ + *state = x; \ + return x; \ + } \ + \ /** \ * -- __skip_compare_entries_fn_ \ * \ @@ -379,12 +393,12 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li * Skiplist. For example, when `max = 6` this function returns 0 with \ * probability 0.5, 1 with 0.25, 2 with 0.125, etc. until 6 with 0.5^7. \ */ \ - static int __skip_toss_##decl(size_t max) \ + static int __skip_toss_##decl(decl##_t *slist, size_t max) \ { \ size_t level = 0; \ double probability = 0.5; \ \ - double random_value = (double)rand() / RAND_MAX; /* NOLINT(*-msc50-cpp) */ \ + double random_value = (double)__##decl##_xorshift32(&slist->slh_prng_state) / RAND_MAX; \ while (random_value < probability && level < max) { \ level++; \ probability *= 0.5; \ @@ -456,9 +470,9 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li * seed the PRNG in a predictable way and have reproducible random numbers. \ */ \ if (max < 0) \ - srand(-max); \ + slist->slh_prng_state = -max; \ else \ - srand(((unsigned int)time(NULL) ^ getpid())); \ + slist->slh_prng_state = ((unsigned int)time(NULL) ^ getpid()); \ fail:; \ return rc; \ } \ @@ -787,7 +801,7 @@ void __attribute__((format(printf, 4, 5))) __skip_diag_(const char *file, int li } \ /* Coin toss to determine level of this new node [0, max) */ \ cur_height = slist->slh_head->field.sle_height; \ - new_height = __skip_toss_##decl(slist->slh_max_height - 1); \ + new_height = __skip_toss_##decl(slist, slist->slh_max_height - 1); \ new->field.sle_height = new_height; \ /* Trim the path to at most the new height for the new node. */ \ for (i = cur_height + 1; i <= new_height; i++) { \