static int fset_locate (fset_t *dbs, mem_hash_t *mem, void *key, size_t *ref, size_t *tomb) { size_t k = dbs->key_length; for (int i = 1; *mem == EMPTY || *mem == TOMB; i++) { *mem = MurmurHash32(key, k, i); } size_t h = home_loc (*mem); *tomb = NONE; dbs->lookups++; //Debug ("Locating key %zu,%zu with hash %u", ((size_t*)data)[0], ((size_t*)data)[1], mem); for (size_t rh = 0; rh <= 1000; rh++) { *ref = h & dbs->mask; size_t line_begin = *ref & CACHE_LINE_MEM_MASK; size_t line_end = line_begin + CACHE_LINE_MEM_SIZE; for (size_t i = 0; i < CACHE_LINE_MEM_SIZE; i++) { dbs->probes++; if (*memoized(dbs,*ref) == TOMB) { if (*tomb == NONE) *tomb = *ref; // first found tombstone } else if (*memoized(dbs,*ref) == EMPTY) { return 0; } else if ( *mem == *memoized(dbs,*ref) && memcmp (key, bucket(dbs,dbs->data,*ref), k) == 0 ) { return 1; } *ref = (*ref+1 == line_end ? line_begin : *ref+1); // next in line } h = rehash (h, *mem); } return FSET_FULL; }
/** * If we delete a bucket before an empty bucket on the probe line, we * may delete the entire chain of tomb stones leading to the bucket. * This will not break any probe sequence as the line already contained at least * one empty bucket (no rehashes could have occurred from this probe line). */ void wipe_chain (fset_t *dbs, size_t ref) { size_t line_begin = ref & CACHE_LINE_MEM_MASK; size_t line_end = line_begin + CACHE_LINE_MEM_SIZE; size_t next = (ref+1 == line_end ? line_begin : ref+1); if (*memoized (dbs, next) != EMPTY) { *memoized (dbs, ref) = TOMB; // wipe in chain dbs->tombs++; dbs->load--; return; } // chain end: do { *memoized (dbs, ref) = EMPTY; // wipe chain end ref = (ref == line_begin ? line_end-1 : ref-1); dbs->tombs--; } while (*memoized (dbs, ref) == TOMB); dbs->tombs++; // first wipe was ref itself (not a tomb stone) dbs->load--; }
int main(int argc, char* argv[]) { std::ifstream in {argv[1]}; std::string line; std::getline(in, line); std::istringstream ss(line); std::size_t N, K, B; ss >> N >> K >> B; std::getline(in, line); ss.str(line); ss.clear(); std::vector<std::size_t> steps; std::copy_n(std::istream_iterator<std::size_t>(ss), K, std::back_inserter(steps)); std::getline(in, line); ss.str(line); ss.clear(); std::vector<std::size_t> invalid_stairs; std::copy_n(std::istream_iterator<std::size_t>(ss), B, std::back_inserter(invalid_stairs)); std::vector<bool> is_invalid_stair(N+1,false); for (auto& stair : invalid_stairs) { is_invalid_stair[stair] = true; } std::vector<std::vector<std::size_t>> valid_steps(N+1, steps); for (std::size_t i = 0; i != N+1; ++i) { valid_steps[i].erase( std::remove_if(valid_steps[i].begin(), valid_steps[i].end(), [i,&is_invalid_stair](std::size_t s) { return (s >= i || is_invalid_stair[i-s]); }), valid_steps[i].end()); } std::vector<std::pair<bool,std::size_t>> memoized(N+1, std::make_pair(false, 0)); for (std::size_t i = 1; i != N+1; ++i) { hopper(memoized,valid_steps,1000000009,i); } std::cout << hopper(memoized, valid_steps, 1000000009, N) << std::endl; return 0; }
int fset_find (fset_t *dbs, mem_hash_t *m, void *key, void **data, bool insert_absent) { HREassert (dbs->data_length == 0 || data); size_t ref; size_t tomb = NONE; size_t k = dbs->key_length; mem_hash_t mem = m == NULL ? EMPTY : *m; int found = fset_locate (dbs, &mem, key, &ref, &tomb); if (insert_absent && !found) { // insert: if (tomb != NONE) { ref = tomb; dbs->tombs--; } if (dbs->key_length) memcpy (bucket(dbs, dbs->data, ref), key, k); *memoized(dbs, ref) = mem; dbs->load++; dbs->max_load = max (dbs->max_load, dbs->load); if (dbs->tombs << 1 > dbs->size) { bool res = resize (dbs, REHASH); // >50% tombs ==> rehash HREassert (res, "Cannot rehash table?"); fset_locate (dbs, &mem, key, &ref, &tomb); // update ref } else if (((dbs->load + dbs->tombs) << 2) > dbs->size3) { if (!resize(dbs, GROW)) { // > 75% full ==> grow Debug ("Hash table almost full (size = %zu, load = %zu, tombs = %zu)", dbs->size, dbs->load, dbs->tombs); } fset_locate (dbs, &mem, key, &ref, &tomb); // update ref } } if (data) *data = bucket(dbs, dbs->data, ref) + k; return found; }
static int resize (fset_t *dbs, fset_resize_t mode) { void *key, *data; bool res; if (dbs->resizing) return true; dbs->resizing = 1; size_t old_size = dbs->size; switch (mode) { case GROW: if (dbs->size == dbs->size_max) { dbs->resizing = 0; return false; } memset (dbs->hash + dbs->size, 0, sizeof (mem_hash_t[dbs->size])); dbs->size <<= 1; dbs->size3 <<= 1; break; case SHRINK: if (dbs->size == dbs->init_size) { dbs->resizing = 0; return true; } dbs->size >>= 1; dbs->size3 >>= 1; break; case REHASH: break; } dbs->mask = dbs->size - 1; Debug ("%s %zu to %zu", fset_resize_names[mode], old_size, dbs->size); //RTstartTimer (dbs->timer); size_t tombs = 0; size_t todos = 0; for (size_t i = 0; i < old_size; i++) { mem_hash_t h = *memoized(dbs,i); if (TOMB == h) { tombs++; *memoized(dbs, i) = EMPTY; } else if (h != EMPTY) {// && home_loc(h) & dbs->mask != i) { dbs->todo[todos] = *memoized(dbs,i); void *tdata = bucket(dbs, dbs->todo_data, todos); void *data = bucket(dbs, dbs->data, i); memcpy (tdata, data, dbs->total_length); todos++; *memoized(dbs, i) = EMPTY; } } dbs->tombs -= tombs; dbs->load -= todos; HREassert (dbs->tombs == 0); for (size_t i = 0; i < todos; i++) { mem_hash_t h = dbs->todo[i]; key = bucket(dbs, dbs->todo_data, i); res = fset_find (dbs, &h, key, &data, true); // load++ HREassert (!res); memcpy (data, key + dbs->key_length, dbs->data_length); } //RTstopTimer (dbs->timer); Debug ("%s %zu to %zu took %zu/%zu todos and cleaned %zu/%zu tombstones in %.2f sec", fset_resize_names[mode], old_size, dbs->size, todos, dbs->load, tombs, dbs->load + tombs, RTrealTime(dbs->timer)); dbs->max_todos = max (todos, dbs->max_todos); dbs->max_grow = max (dbs->max_grow, dbs->size); dbs->resizes++; dbs->resizing = 0; return true; }