-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSimpleSet.h
136 lines (119 loc) · 4.01 KB
/
SimpleSet.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#ifndef SIMPLE_SET_H
#define SIMPLE_SET_H 1
#define SIMPLE_SET_MULT_FACTOR 4
#include "util.h" // for next_prime()
#include <cstddef> // for std::size_t
#include <functional> // for std::hash
//#include <iostream>
// TODO: play with different multiplicative factors
inline constexpr std::size_t allocate_size_for_capacity_1(std::size_t capacity) {
return capacity * SIMPLE_SET_MULT_FACTOR;
}
// for collisions, it is best if this returns a prime number
inline constexpr std::size_t allocate_size_for_capacity_2(std::size_t capacity) {
return next_prime(capacity * SIMPLE_SET_MULT_FACTOR);
}
inline constexpr std::size_t allocate_size_for_capacity(std::size_t capacity) {
return allocate_size_for_capacity_2(capacity);
}
/** A simple set that does no dynamic allocations except at creation.
*
* It uses an open addressing approach to conflict resolution. Since this
* set is so simple, you can only insert, check if an element is contained,
* and clear the set. That means you cannot even remove elements unless
* you remove all of them.
*
* If you want to use the data pointer directly, an empty location is the
* same as the zero-initialized value of Key. That means you cannot insert
* an element that is equal to the zero-initialization value.
*/
template <class Key,
std::size_t Capacity=25000,
class Hash = std::hash<Key>,
class Pred = std::equal_to<Key>
>
class SimpleSet {
public:
SimpleSet() noexcept
: _size(0)
, _internal_capacity(allocate_size_for_capacity(Capacity))
, _collisions(0)
, _hash()
, _is_equal()
, _data{} // zero initialize array
{}
/** Getters */
inline int size() const noexcept { return _size; }
inline int capacity() const noexcept { return Capacity; }
inline long collisions() const noexcept { return _collisions; }
/** Returns the raw internal data pointer. Use with care */
inline const Key* data() const noexcept { return _data; }
inline size_t data_size() const noexcept { return _internal_capacity; }
/** Inserts an element into the set.
*
* Returns true if the element was not in the set (the opposite of
* contains).
*/
inline bool insert(const Key& elem) noexcept {
auto idx = _hash(elem) % _internal_capacity;
bool is_contained = false;
for (auto current_idx = idx;
current_idx != prev_idx(idx);
current_idx = next_idx(current_idx))
{
//std::cout << " insert(" << elem << ")" << std::endl;
if (_is_equal(_data[current_idx], Key())) {
_data[current_idx] = elem;
break;
} else if (_is_equal(_data[current_idx], elem)) {
is_contained = true;
break;
}
_collisions += 1;
}
return !is_contained;
}
/** Returns true if elem is in the set */
inline bool contains(const Key& elem) const noexcept {
auto idx = _hash(elem) % _internal_capacity;
bool is_contained = false;
for (auto current_idx = idx;
current_idx != prev_idx(idx);
current_idx = next_idx(current_idx))
{
//std::cout << " contains(" << elem << ")" << std::endl;
if (_is_equal(_data[current_idx], Key())) {
is_contained = false;
break;
} else if (_is_equal(_data[current_idx], elem)) {
is_contained = true;
break;
}
_collisions += 1;
}
return is_contained;
}
/** Clears out the set. It will be empty after this call */
inline void clear() noexcept {
for (int i = 0; i < _internal_capacity; i++) {
_data[i] = Key();
}
}
private:
/// Treats data as a circular buffer.
inline size_t next_idx(size_t idx) const noexcept {
return (idx + 1) % _internal_capacity;
}
/// Again treated as a circular buffer
inline size_t prev_idx(size_t idx) const noexcept {
return (idx + _internal_capacity - 1) % _internal_capacity;
}
private:
size_t _size;
size_t _internal_capacity;
mutable size_t _collisions;
Hash _hash;
Pred _is_equal;
Key _data[allocate_size_for_capacity(Capacity)];
};
#endif // SIMPLE_SET_H