Browse code

working but inconsistent and slow

Tom Sherman authored on 22/08/2018 20:02:28
Showing4 changed files

... ...
@@ -4,18 +4,19 @@
4 4
 #include <vector>
5 5
 
6 6
 // should only ever use this when copying a default constructed bucket
7
-AtomBucket& AtomBucket::operator=(const AtomBucket& other)
7
+// in the vector of buckets used in the allocator
8
+/*AtomBucket& AtomBucket::operator=(const AtomBucket& other)
8 9
 {
9
-    GAPS_ASSERT(!other.mFull);
10
+    GAPS_ASSERT(other.mSize == 0);
10 11
     GAPS_ASSERT(other.mPrev == NULL);
11 12
     GAPS_ASSERT(other.mNext == NULL);
12 13
     GAPS_ASSERT(other.mOverflow == NULL);
13 14
 
14
-    mFull = false;
15
+    mSize = 0;
15 16
     mPrev = NULL;
16 17
     mNext = NULL;
17 18
     mOverflow = NULL;
18
-}
19
+}*/
19 20
 
20 21
 /////////////////////// ALLOCATOR FOR OVERFLOW BUCKETS /////////////////////////
21 22
 
... ...
@@ -130,61 +131,114 @@ bool AtomNeighborhood::hasRight()
130 131
 // cases are fast
131 132
 
132 133
 AtomBucket::AtomBucket()
133
-    : mFull(false), mOverflow(NULL), mPrev(NULL), mNext(NULL)
134
+    : mSize(0), mOverflow(NULL), mPrev(NULL), mNext(NULL)
134 135
 {}
135 136
 
136 137
 unsigned AtomBucket::size() const
137 138
 {
138
-    unsigned thisSize = mFull ? 1 : 0;
139
-    return mOverflow == NULL ? thisSize : thisSize + mOverflow->size();
139
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
140
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
141
+
142
+    return mOverflow == NULL ? mSize : mSize + mOverflow->size();
140 143
 }
141 144
 
142 145
 bool AtomBucket::isEmpty() const
143 146
 {
144
-    return !mFull;
147
+    return mSize == 0;
145 148
 }
146 149
 
147 150
 bool AtomBucket::contains(uint64_t pos) const
148 151
 {
149
-    if (mOverflow != NULL && pos > mBucket.pos)
152
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
153
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
154
+    GAPS_ASSERT(pos > 0);
155
+
156
+    if (mOverflow != NULL && !mOverflow->isEmpty() && pos > mBucket[1].pos)
150 157
     {
151 158
         return mOverflow->contains(pos);
152 159
     }
153 160
     else
154 161
     {
155
-        return mFull ? pos == mBucket.pos : false;
162
+        switch (mSize)
163
+        {
164
+            case 0:
165
+                return false;
166
+            case 1:
167
+                return mBucket[0].pos == pos;
168
+            case 2:
169
+                return mBucket[0].pos == pos || mBucket[1].pos == pos;
170
+        }
156 171
     }
157 172
 }
158 173
 
159 174
 Atom* AtomBucket::operator[](unsigned index)
160 175
 {
161
-    return index == 0 ? &mBucket : mOverflow->operator[](index - 1);
176
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
177
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
178
+    GAPS_ASSERT(index < size());
179
+
180
+    if (index > 1) // must be overflowed
181
+    {
182
+        return mOverflow->operator[](index - 2);
183
+    }
184
+    return &(mBucket[index]);
162 185
 }
163 186
 
164 187
 void AtomBucket::insert(uint64_t pos, float mass)
165 188
 {
166
-    if (!mFull)
189
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
190
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
191
+    GAPS_ASSERT(pos > 0);
192
+
193
+    if (mSize == 0)
167 194
     {
168
-        mBucket = Atom(pos, mass);
169
-        mFull = true;
195
+        mBucket[0] = Atom(pos, mass);
196
+        ++mSize;
197
+    }
198
+    else if (mSize == 1)
199
+    {
200
+        if (pos < mBucket[0].pos)
201
+        {
202
+            mBucket[1] = mBucket[0];
203
+            mBucket[0] = Atom(pos, mass);
204
+        }
205
+        else
206
+        {
207
+            mBucket[1] = Atom(pos, mass);
208
+        }
209
+        ++mSize;
170 210
     }
171 211
     else
172 212
     {
213
+        // check if we need to allocate the overflow bucket
173 214
         if (mOverflow == NULL)
174 215
         {
175 216
             mOverflow = createAtomBucket();
176 217
             mOverflow->mPrev = this;
177 218
             mOverflow->mNext = mNext;
178 219
         }
179
-        
180
-        if (pos < mBucket.pos)
220
+        else if (mOverflow->isEmpty())
181 221
         {
182
-            mOverflow->insert(mBucket.pos, mBucket.mass);
183
-            mBucket = Atom(pos, mass);
222
+            mOverflow->mPrev = this;
223
+            mOverflow->mNext = mNext;
224
+        }
225
+
226
+        // push correct atom into overflow bucket
227
+        if (pos > mBucket[1].pos)
228
+        {
229
+            return mOverflow->insert(pos, mass);
230
+        }
231
+        mOverflow->insert(mBucket[1].pos, mBucket[1].mass);
232
+
233
+        // if inserting in this bucket, find correct position
234
+        if (pos < mBucket[0].pos)
235
+        {
236
+            mBucket[1] = mBucket[0];
237
+            mBucket[0] = Atom(pos, mass);
184 238
         }
185 239
         else
186 240
         {
187
-            mOverflow->insert(pos, mass);
241
+            mBucket[1] = Atom(pos, mass);
188 242
         }
189 243
     }
190 244
 }
... ...
@@ -192,55 +246,100 @@ void AtomBucket::insert(uint64_t pos, float mass)
192 246
 // assumes pos is contained in this chain
193 247
 void AtomBucket::erase(uint64_t pos)
194 248
 {
249
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
250
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
251
+    GAPS_ASSERT(pos > 0);
252
+    GAPS_ASSERT(mSize > 0);
195 253
     GAPS_ASSERT(contains(pos));
196
-    if (mBucket.pos == pos)
254
+
255
+    if (mSize == 1)
197 256
     {
257
+        connectAdjacent();
258
+        mSize = 0;
259
+    }
260
+    else if (mSize == 2)
261
+    {
262
+        // check if this position is in overflow bucket    
263
+        if (pos > mBucket[1].pos)
264
+        {
265
+            return mOverflow->erase(pos);
266
+        }
267
+
268
+        // shift top position down if needed
269
+        if (mBucket[0].pos == pos)
270
+        {
271
+            mBucket[0] = mBucket[1];
272
+        }
273
+        
274
+        // pull first atom from overflow if it's there
198 275
         if (mOverflow != NULL && !mOverflow->isEmpty())
199 276
         {
200
-            mBucket = *(mOverflow->front());
277
+            mBucket[1] = *(mOverflow->front());
201 278
             mOverflow->eraseFront();
202 279
         }
203
-        else
280
+        else // just delete atom at last position
204 281
         {
205
-            mFull = false;
206
-            connectAdjacent();
282
+            --mSize;
207 283
         }
208 284
     }
209
-    else
210
-    {
211
-        mOverflow->erase(pos);
212
-    }
213 285
 }
214 286
 
215 287
 void AtomBucket::eraseFront()
216 288
 {
217
-    if (mOverflow != NULL && !mOverflow->isEmpty())
289
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
290
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
291
+
292
+    GAPS_ASSERT(mSize > 0);
293
+
294
+    if (mSize == 1)
218 295
     {
219
-        mBucket = *(mOverflow->front());
220
-        mOverflow->eraseFront();
296
+        connectAdjacent();
297
+        mSize = 0;
221 298
     }
222
-    else
299
+    else if (mSize == 2)
223 300
     {
224
-        mFull = false;
225
-        connectAdjacent();
301
+        mBucket[0] = mBucket[1];
302
+        
303
+        // pull first atom from overflow if it's there
304
+        if (mOverflow != NULL && !mOverflow->isEmpty())
305
+        {
306
+            mBucket[1] = *(mOverflow->front());
307
+            mOverflow->eraseFront();
308
+        }
309
+        else
310
+        {
311
+            --mSize;
312
+        }
226 313
     }
227 314
 }
228 315
 
229 316
 AtomNeighborhood AtomBucket::getNeighbors(unsigned index)
230 317
 {
318
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
319
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
320
+    GAPS_ASSERT(index < size());
321
+
231 322
     return AtomNeighborhood(getLeft(index), this->operator[](index), getRight(index));
232 323
 }
233 324
 
234 325
 AtomNeighborhood AtomBucket::getRightNeighbor(unsigned index)
235 326
 {
327
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
328
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
329
+    GAPS_ASSERT(index < size());
330
+
236 331
     return AtomNeighborhood(NULL, this->operator[](index), getRight(index));
237 332
 }
238 333
 
239
-// needs to propogate through overflow
334
+// needs to propogate through overflow so that the last overflow will know
335
+// where the next bucket is
240 336
 void AtomBucket::setRightAdjacentBucket(AtomBucket *bucket)
241 337
 {
338
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
339
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
340
+
242 341
     mNext = bucket;
243
-    if (mOverflow != NULL)
342
+    if (mOverflow != NULL && !mOverflow->isEmpty())
244 343
     {
245 344
         mOverflow->setRightAdjacentBucket(bucket);
246 345
     }
... ...
@@ -248,12 +347,20 @@ void AtomBucket::setRightAdjacentBucket(AtomBucket *bucket)
248 347
 
249 348
 void AtomBucket::setLeftAdjacentBucket(AtomBucket *bucket)
250 349
 {
350
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
351
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
352
+    GAPS_ASSERT(bucket == NULL || !bucket->isEmpty());
353
+
251 354
     mPrev = bucket;
252 355
 }
253 356
 
254 357
 void AtomBucket::connectAdjacent()
255 358
 {
256
-    if (mNext != NULL)
359
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
360
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
361
+
362
+    // be careful when connecting overflow buckets
363
+    if (mNext != NULL && (mPrev == NULL || mPrev->mOverflow != this))
257 364
     {
258 365
         mNext->setLeftAdjacentBucket(mPrev);
259 366
     }
... ...
@@ -261,61 +368,87 @@ void AtomBucket::connectAdjacent()
261 368
     {
262 369
         mPrev->setRightAdjacentBucket(mNext);
263 370
     }
371
+
372
+    mNext = NULL;
373
+    mPrev = NULL;
264 374
 }
265 375
 
266 376
 Atom* AtomBucket::front()
267 377
 {
268
-    return &mBucket;
378
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
379
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
380
+    GAPS_ASSERT(mSize > 0);
381
+    GAPS_ASSERT(mBucket[0].pos > 0);
382
+
383
+    return &(mBucket[0]);
269 384
 }
270 385
 
271 386
 Atom* AtomBucket::back()
272 387
 {
388
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
389
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
390
+    GAPS_ASSERT(mSize > 0);
391
+
273 392
     if (mOverflow == NULL || (mOverflow != NULL && mOverflow->isEmpty()))
274 393
     {
275
-        return &mBucket;
394
+        return &(mBucket[mSize - 1]);
276 395
     }
277 396
     return mOverflow->back();
278 397
 }
279 398
 
280 399
 Atom* AtomBucket::getLeft(unsigned index)
281 400
 {
401
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
402
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
403
+    GAPS_ASSERT(mSize > 0);
404
+    GAPS_ASSERT(index < size());
405
+
282 406
     if (index == 0)
283 407
     {
284
-        return mPrev != NULL ? mPrev->back() : NULL;
408
+        GAPS_ASSERT(mPrev == NULL || mPrev->mOverflow != this);
409
+        return mPrev != NULL ? mPrev->back() : NULL; // mPrev can't be overflow here
285 410
     }
286
-    else if (index == 1)
411
+    else if (index < 3)
287 412
     {
288
-        return &mBucket;
413
+        return &(mBucket[index - 1]);
289 414
     }
290 415
     else
291 416
     {
292
-        return mOverflow->getLeft(index - 1);
417
+        GAPS_ASSERT(mOverflow != NULL);
418
+        return mOverflow->getLeft(index - 2);
293 419
     }    
294 420
 }
295 421
 
296 422
 Atom* AtomBucket::getRight(unsigned index)
297 423
 {
298
-    if (index == 0)
424
+    GAPS_ASSERT(mPrev == NULL || !mPrev->isEmpty());
425
+    GAPS_ASSERT(mNext == NULL || !mNext->isEmpty());
426
+    GAPS_ASSERT(mSize > 0);
427
+    GAPS_ASSERT(index < size());
428
+
429
+    if (index == mSize - 1) // this is last atom in bucket
299 430
     {
300 431
         if (mOverflow != NULL && !mOverflow->isEmpty())
301 432
         {
302 433
             return mOverflow->front();
303 434
         }
304
-        else
305
-        {
306
-            return mNext != NULL ? mNext->front() : NULL;
307
-        }
435
+        return mNext != NULL ? mNext->front() : NULL;
436
+    }        
437
+    else if (index == 0)
438
+    {
439
+        GAPS_ASSERT(mBucket[1].pos > 0);
440
+        return &(mBucket[1]);
308 441
     }
309
-    else // must be overflowed
442
+    else
310 443
     {
311
-        return mOverflow->getRight(index - 1);
444
+        mOverflow->getRight(index - 2);
312 445
     }
313 446
 }
314 447
 
315 448
 Archive& operator<<(Archive &ar, AtomBucket &b)
316 449
 {
317 450
     bool hasOverflow = (b.mOverflow != NULL);
318
-    ar << b.mBucket << b.mFull << hasOverflow;
451
+    ar << b.mBucket[0] << b.mBucket[1] << b.mSize << hasOverflow;
319 452
 
320 453
     if (hasOverflow)
321 454
     {
... ...
@@ -327,11 +460,11 @@ Archive& operator<<(Archive &ar, AtomBucket &b)
327 460
 Archive& operator>>(Archive& ar, AtomBucket &b)
328 461
 {
329 462
     bool hasOverflow = false;
330
-    ar >> b.mBucket >> b.mFull >> hasOverflow;
463
+    ar >> b.mBucket[0] >> b.mBucket[1] >> b.mSize >> hasOverflow;
331 464
 
332 465
     if (hasOverflow)
333 466
     {
334
-        b.mOverflow = new AtomBucket();
467
+        b.mOverflow = createAtomBucket();
335 468
         ar >> *b.mOverflow;
336 469
     }
337 470
     return ar;
... ...
@@ -420,23 +553,25 @@ void AtomHashMap::insert(uint64_t pos, float mass)
420 553
     if (mHashMap[index].isEmpty())
421 554
     {
422 555
         mHashMap[index].insert(pos, mass);
556
+        GAPS_ASSERT(mHashMap[index].contains(pos));
423 557
         std::set<unsigned>::iterator it = mFullBuckets.insert(index).first;
424 558
         if (it != mFullBuckets.begin())
425 559
         {
426 560
             --it;
427
-            mHashMap[*it].setRightAdjacentBucket(&mHashMap[index]);
428 561
             mHashMap[index].setLeftAdjacentBucket(&mHashMap[*it]);
562
+            mHashMap[*it].setRightAdjacentBucket(&mHashMap[index]);
429 563
             ++it;
430 564
         }
431 565
         if (++it != mFullBuckets.end())
432 566
         {
433
-            mHashMap[*it].setLeftAdjacentBucket(&mHashMap[index]);
434 567
             mHashMap[index].setRightAdjacentBucket(&mHashMap[*it]);
568
+            mHashMap[*it].setLeftAdjacentBucket(&mHashMap[index]);
435 569
         }
436 570
     }
437 571
     else
438 572
     {
439 573
         mHashMap[index].insert(pos, mass);
574
+        GAPS_ASSERT(mHashMap[index].contains(pos));
440 575
     }
441 576
     GAPS_ASSERT(mHashMap[index].contains(pos));
442 577
 
... ...
@@ -55,9 +55,6 @@ public:
55 55
     void setRightAdjacentBucket(AtomBucket *bucket);
56 56
     void setLeftAdjacentBucket(AtomBucket *bucket);
57 57
 
58
-    AtomBucket& operator=(const AtomBucket& other);
59
-
60
-    unsigned getIndex(uint64_t pos);
61 58
     Atom* getLeft(unsigned index);
62 59
     Atom* getRight(unsigned index);
63 60
 
... ...
@@ -65,8 +62,8 @@ public:
65 62
 private:
66 63
 #endif
67 64
 
68
-    Atom mBucket;
69
-    bool mFull;
65
+    Atom mBucket[2];
66
+    uint32_t mSize;
70 67
     
71 68
     AtomBucket *mOverflow;
72 69
     AtomBucket *mPrev;
... ...
@@ -76,6 +73,8 @@ private:
76 73
     void connectAdjacent();
77 74
 
78 75
     Atom* back();
76
+
77
+    AtomBucket& operator=(const AtomBucket& other); // prevent copies
79 78
     
80 79
     friend Archive& operator<<(Archive& ar, AtomBucket &b);
81 80
     friend Archive& operator>>(Archive& ar, AtomBucket &b);
... ...
@@ -21,6 +21,7 @@ OBJECTS =   AtomicDomain.o \
21 21
             math/Math.o \
22 22
             math/Random.o \
23 23
             cpp_tests/testAlgorithms.o \
24
+            cpp_tests/testAtomicDomain.o \
24 25
             cpp_tests/testEfficientSets.o \
25 26
             cpp_tests/testFileParsers.o \
26 27
             cpp_tests/testMatrix.o \
... ...
@@ -1 +1,655 @@
1
-// TODO
2 1
\ No newline at end of file
2
+#include "catch.h"
3
+#include "../AtomicDomain.h"
4
+#include "../GapsPrint.h"
5
+
6
+// used to create aligned buckets for testing
7
+AtomBucket* getAlignedBucket()
8
+{
9
+    return new AtomBucket();
10
+}
11
+
12
+TEST_CASE("AtomBucket")
13
+{
14
+    GapsRng::setSeed(12345);
15
+
16
+    SECTION("Properties of Default Constructed Buckets")
17
+    {
18
+        AtomBucket *bucket = getAlignedBucket();
19
+        REQUIRE(bucket->isEmpty());
20
+        REQUIRE(bucket->size() == 0);
21
+        REQUIRE(!bucket->contains(1));
22
+
23
+        // verify internal state
24
+        #ifdef GAPS_INTERNAL_TESTS
25
+        REQUIRE(bucket->mOverflow == NULL);
26
+        #endif
27
+    }
28
+
29
+    SECTION("Insert")
30
+    {
31
+        AtomBucket *bucket = getAlignedBucket();
32
+
33
+        // insert first atom
34
+        bucket->insert(1, 1.f);
35
+
36
+        REQUIRE(!bucket->isEmpty());
37
+        REQUIRE(bucket->size() == 1);
38
+
39
+        REQUIRE(bucket->contains(1));
40
+        REQUIRE(!bucket->contains(2));
41
+        REQUIRE(!bucket->contains(3));
42
+
43
+        REQUIRE(bucket->front()->pos == 1);
44
+        REQUIRE(bucket->front()->mass == 1.f);
45
+        REQUIRE((*bucket)[0]->pos == 1);
46
+        REQUIRE((*bucket)[0]->mass == 1.f);
47
+
48
+        // insert second atom
49
+        bucket->insert(3, 3.f);
50
+
51
+        REQUIRE(!bucket->isEmpty());
52
+        REQUIRE(bucket->size() == 2);
53
+
54
+        REQUIRE(bucket->contains(1));
55
+        REQUIRE(!bucket->contains(2));
56
+        REQUIRE(bucket->contains(3));
57
+
58
+        REQUIRE(bucket->front()->pos == 1);
59
+        REQUIRE(bucket->front()->mass == 1.f);
60
+        REQUIRE((*bucket)[0]->pos == 1);
61
+        REQUIRE((*bucket)[0]->mass == 1.f);
62
+        REQUIRE((*bucket)[1]->pos == 3);
63
+        REQUIRE((*bucket)[1]->mass == 3.f);
64
+
65
+        // insert third atom
66
+        bucket->insert(2, 2.f);
67
+
68
+        REQUIRE(!bucket->isEmpty());
69
+        REQUIRE(bucket->size() == 3);
70
+
71
+        REQUIRE(bucket->contains(1));
72
+        REQUIRE(bucket->contains(2));
73
+        REQUIRE(bucket->contains(3));
74
+
75
+        REQUIRE(bucket->front()->pos == 1);
76
+        REQUIRE(bucket->front()->mass == 1.f);
77
+        REQUIRE((*bucket)[0]->pos == 1);
78
+        REQUIRE((*bucket)[0]->mass == 1.f);
79
+        REQUIRE((*bucket)[1]->pos == 2);
80
+        REQUIRE((*bucket)[1]->mass == 2.f);
81
+        REQUIRE((*bucket)[2]->pos == 3);
82
+        REQUIRE((*bucket)[2]->mass == 3.f);
83
+
84
+        // verify internal state
85
+        #ifdef GAPS_INTERNAL_TESTS
86
+        REQUIRE(bucket->mOverflow == NULL);
87
+        REQUIRE(bucket->back()->pos == 3);
88
+        REQUIRE(bucket->back()->mass == 3.f);
89
+        REQUIRE(bucket->getLeft(1)->pos == 1);
90
+        REQUIRE(bucket->getLeft(1)->mass == 1.f);
91
+        REQUIRE(bucket->getRight(1)->pos == 3);
92
+        REQUIRE(bucket->getRight(1)->mass == 3.f);
93
+        #endif
94
+    }
95
+
96
+    SECTION("Erase")
97
+    {
98
+        AtomBucket *bucket = getAlignedBucket();
99
+
100
+        // erase one atom
101
+        bucket->insert(1, 1.f);
102
+        bucket->erase(1);
103
+        REQUIRE(bucket->isEmpty());
104
+        REQUIRE(bucket->size() == 0);
105
+        REQUIRE(!bucket->contains(1));
106
+
107
+        // erase two atoms
108
+        bucket->insert(1, 1.f);
109
+        bucket->insert(3, 3.f);
110
+
111
+        bucket->erase(1);
112
+        REQUIRE(!bucket->isEmpty());
113
+        REQUIRE(bucket->size() == 1);
114
+        REQUIRE(!bucket->contains(1));
115
+        REQUIRE(bucket->contains(3));
116
+        REQUIRE(bucket->front()->pos == 3);
117
+        REQUIRE((*bucket)[0]->pos == 3);
118
+        bucket->insert(1, 1.f);
119
+
120
+        bucket->erase(3);
121
+        REQUIRE(!bucket->isEmpty());
122
+        REQUIRE(bucket->size() == 1);
123
+        REQUIRE(bucket->contains(1));
124
+        REQUIRE(!bucket->contains(3));
125
+        REQUIRE(bucket->front()->pos == 1);
126
+        REQUIRE((*bucket)[0]->pos == 1);
127
+        bucket->insert(3, 3.f);
128
+
129
+        bucket->erase(1);
130
+        bucket->erase(3);
131
+        REQUIRE(bucket->isEmpty());
132
+        REQUIRE(bucket->size() == 0);
133
+        REQUIRE(!bucket->contains(1));
134
+        REQUIRE(!bucket->contains(3));
135
+    
136
+        // erase three atoms
137
+        bucket->insert(1, 1.f);
138
+        bucket->insert(3, 3.f);
139
+        bucket->insert(2, 2.f);
140
+
141
+        bucket->erase(2);
142
+        REQUIRE(!bucket->isEmpty());
143
+        REQUIRE(bucket->size() == 2);
144
+        REQUIRE(!bucket->contains(2));
145
+        REQUIRE(bucket->contains(1));
146
+        REQUIRE(bucket->contains(3));
147
+        REQUIRE(bucket->front()->pos == 1);
148
+        REQUIRE((*bucket)[0]->pos == 1);
149
+        REQUIRE((*bucket)[1]->pos == 3);
150
+        bucket->insert(2, 2.f);
151
+
152
+        bucket->erase(1);
153
+        bucket->erase(2);
154
+        REQUIRE(!bucket->isEmpty());
155
+        REQUIRE(bucket->size() == 1);
156
+        REQUIRE(!bucket->contains(1));
157
+        REQUIRE(!bucket->contains(2));
158
+        REQUIRE(bucket->contains(3));
159
+        REQUIRE(bucket->front()->pos == 3);
160
+        REQUIRE((*bucket)[0]->pos == 3);
161
+        bucket->insert(1, 1.f);
162
+        bucket->insert(2, 2.f);
163
+
164
+        bucket->erase(1);
165
+        bucket->erase(2);
166
+        bucket->erase(3);
167
+        REQUIRE(bucket->isEmpty());
168
+        REQUIRE(bucket->size() == 0);
169
+        REQUIRE(!bucket->contains(1));
170
+        REQUIRE(!bucket->contains(2));
171
+        REQUIRE(!bucket->contains(3));
172
+
173
+        // verify internal state
174
+        #ifdef GAPS_INTERNAL_TESTS
175
+        REQUIRE(bucket->mOverflow == NULL);
176
+        #endif
177
+    }
178
+
179
+    SECTION("Handling Overflow")
180
+    {
181
+        AtomBucket *bucket = getAlignedBucket();
182
+
183
+        // insert 1-100 at indices 0-99 (cant use 0 for position)
184
+        for (unsigned i = 100; i > 0; --i)
185
+        {
186
+            bucket->insert(i, static_cast<float>(i));
187
+        }
188
+
189
+        REQUIRE(!bucket->isEmpty());
190
+        REQUIRE(bucket->size() == 100);
191
+        REQUIRE(bucket->contains(100));
192
+        REQUIRE(!bucket->contains(101));
193
+        REQUIRE(bucket->front()->pos == 1);
194
+
195
+        #ifdef GAPS_INTERNAL_TESTS
196
+        for (unsigned i = 1; i < 99; ++i)
197
+        {
198
+            REQUIRE((*bucket)[i]->pos == i + 1);
199
+            REQUIRE(bucket->contains(i + 1));
200
+            REQUIRE(bucket->getLeft(i)->pos == i);
201
+            REQUIRE(bucket->getRight(i)->pos == i + 2);
202
+        }
203
+
204
+        REQUIRE(bucket->mOverflow != NULL);
205
+        REQUIRE(boost::alignment::is_aligned(64, bucket->mOverflow));
206
+        REQUIRE(bucket->back()->pos == 100);
207
+        #endif
208
+    }
209
+
210
+    SECTION("Special Overflow Case")
211
+    {
212
+        AtomBucket *bucket6 = getAlignedBucket();
213
+        bucket6->insert(1, 1.f);
214
+        bucket6->insert(2, 2.f);
215
+        bucket6->insert(3, 3.f);
216
+        bucket6->insert(4, 4.f);
217
+        bucket6->insert(5, 5.f);
218
+        bucket6->insert(6, 6.f);
219
+        bucket6->insert(7, 7.f);
220
+
221
+        REQUIRE(bucket6->size() == 7);
222
+        bucket6->erase(7);
223
+        REQUIRE(bucket6->size() == 6);
224
+        bucket6->erase(6);
225
+        REQUIRE(bucket6->size() == 5);
226
+    }
227
+
228
+    #ifdef GAPS_INTERNAL_TESTS
229
+    SECTION("Traversing Adjacent Blocks")
230
+    {
231
+        // create buckets
232
+        AtomBucket *first = getAlignedBucket();
233
+        AtomBucket *second = getAlignedBucket();
234
+        AtomBucket *third = getAlignedBucket();
235
+
236
+        // set up order
237
+        first->setRightAdjacentBucket(second);
238
+        second->setRightAdjacentBucket(third);
239
+
240
+        third->setLeftAdjacentBucket(second);
241
+        second->setLeftAdjacentBucket(first);
242
+
243
+        // insert into all buckets
244
+        for (unsigned i = 10; i <= 19; ++i)
245
+        {
246
+            first->insert(i, static_cast<float>(i));
247
+        }
248
+        for (unsigned i = 20; i <= 29; ++i)
249
+        {
250
+            second->insert(i, static_cast<float>(i));
251
+        }
252
+        for (unsigned i = 30; i <= 39; ++i)
253
+        {
254
+            third->insert(i, static_cast<float>(i));
255
+        }
256
+
257
+        // check properties
258
+        REQUIRE(!first->isEmpty());
259
+        REQUIRE(!second->isEmpty());
260
+        REQUIRE(!third->isEmpty());
261
+        REQUIRE(first->size() == 10);
262
+        REQUIRE(second->size() == 10);
263
+        REQUIRE(third->size() == 10);
264
+
265
+        // get neighbors across boundaries
266
+        REQUIRE(first->getRight(9)->pos == 20);
267
+        REQUIRE(second->getRight(9)->pos == 30);
268
+
269
+        REQUIRE(second->getLeft(0)->pos == 19);
270
+        REQUIRE(third->getLeft(0)->pos == 29);
271
+
272
+        // delete middle, get neighbors again
273
+        second->connectAdjacent();
274
+        REQUIRE(first->getRight(9)->pos == 30);
275
+        REQUIRE(third->getLeft(0)->pos == 19);
276
+    }
277
+    #endif
278
+
279
+    SECTION("Querying Neighborhood")
280
+    {
281
+        // create buckets
282
+        AtomBucket *first = getAlignedBucket();
283
+        AtomBucket *second = getAlignedBucket();
284
+        AtomBucket *third = getAlignedBucket();
285
+
286
+        // insert into all buckets
287
+        for (unsigned i = 10; i <= 19; ++i)
288
+        {
289
+            first->insert(i, static_cast<float>(i));
290
+        }
291
+        for (unsigned i = 20; i <= 29; ++i)
292
+        {
293
+            second->insert(i, static_cast<float>(i));
294
+        }
295
+        for (unsigned i = 30; i <= 39; ++i)
296
+        {
297
+            third->insert(i, static_cast<float>(i));
298
+        }
299
+
300
+        // set up order
301
+        first->setRightAdjacentBucket(second);
302
+        second->setRightAdjacentBucket(third);
303
+
304
+        third->setLeftAdjacentBucket(second);
305
+        second->setLeftAdjacentBucket(first);
306
+
307
+        // get neighbors of border atoms
308
+        REQUIRE(first->getNeighbors(9).left->pos == 18);
309
+        REQUIRE(first->getNeighbors(9).center->pos == 19);
310
+        REQUIRE(first->getNeighbors(9).right->pos == 20);
311
+        REQUIRE(first->getRightNeighbor(9).center->pos == 19);
312
+        REQUIRE(first->getRightNeighbor(9).right->pos == 20);
313
+
314
+        REQUIRE(second->getNeighbors(9).left->pos == 28);
315
+        REQUIRE(second->getNeighbors(9).center->pos == 29);
316
+        REQUIRE(second->getNeighbors(9).right->pos == 30);
317
+        REQUIRE(second->getRightNeighbor(9).center->pos == 29);
318
+        REQUIRE(second->getRightNeighbor(9).right->pos == 30);
319
+
320
+        REQUIRE(second->getNeighbors(0).left->pos == 19);
321
+        REQUIRE(second->getNeighbors(0).center->pos == 20);
322
+        REQUIRE(second->getNeighbors(0).right->pos == 21);
323
+        REQUIRE(second->getRightNeighbor(0).center->pos == 20);
324
+        REQUIRE(second->getRightNeighbor(0).right->pos == 21);
325
+
326
+        REQUIRE(third->getNeighbors(0).left->pos == 29);
327
+        REQUIRE(third->getNeighbors(0).center->pos == 30);
328
+        REQUIRE(third->getNeighbors(0).right->pos == 31);
329
+        REQUIRE(third->getRightNeighbor(0).center->pos == 30);
330
+        REQUIRE(third->getRightNeighbor(0).right->pos == 31);
331
+
332
+        // get neighbors of edge atoms (they shouldn't be there)
333
+        AtomNeighborhood hood1 = first->getNeighbors(0);
334
+        REQUIRE(!hood1.hasLeft());
335
+        REQUIRE(hood1.hasRight());
336
+
337
+        AtomNeighborhood hood3 = third->getNeighbors(9);
338
+        REQUIRE(hood3.hasLeft());
339
+        REQUIRE(!hood3.hasRight());
340
+
341
+        #ifdef GAPS_INTERNAL_TESTS
342
+        // delete middle, get neighbors again
343
+        second->connectAdjacent();
344
+        REQUIRE(first->getNeighbors(9).left->pos == 18);
345
+        REQUIRE(first->getNeighbors(9).center->pos == 19);
346
+        REQUIRE(first->getNeighbors(9).right->pos == 30);
347
+        REQUIRE(first->getRightNeighbor(9).center->pos == 19);
348
+        REQUIRE(first->getRightNeighbor(9).right->pos == 30);
349
+
350
+        REQUIRE(third->getNeighbors(0).left->pos == 19);
351
+        REQUIRE(third->getNeighbors(0).center->pos == 30);
352
+        REQUIRE(third->getNeighbors(0).right->pos == 31);
353
+        REQUIRE(third->getRightNeighbor(0).center->pos == 30);
354
+        REQUIRE(third->getRightNeighbor(0).right->pos == 31);
355
+        #endif
356
+    }
357
+}
358
+
359
+TEST_CASE("AtomHashMap")
360
+{
361
+    SECTION("Default Construction")
362
+    {
363
+        AtomHashMap map(100);
364
+        REQUIRE(map.size() == 0);
365
+    }
366
+
367
+    SECTION("Length Calculation and Hashing")
368
+    {
369
+        // nice size
370
+        AtomHashMap map(100);
371
+        map.setTotalLength(1000);
372
+        
373
+        #ifdef GAPS_INTERNAL_TESTS
374
+        REQUIRE(map.mTotalLength == 1000);
375
+        REQUIRE(map.mBucketLength == 10);
376
+
377
+        REQUIRE(map.hash(1) == 0);
378
+        REQUIRE(map.hash(9) == 0);
379
+        REQUIRE(map.hash(10) == 1);
380
+        REQUIRE(map.hash(19) == 1);
381
+        REQUIRE(map.hash(20) == 2);
382
+        REQUIRE(map.hash(1000) == 100);
383
+        #endif
384
+    }
385
+
386
+    SECTION("Insert")
387
+    {
388
+        AtomHashMap map(100);
389
+        map.setTotalLength(1000);
390
+
391
+        map.insert(1, 1.f);
392
+        REQUIRE(map.size() == 1);
393
+        REQUIRE(map.contains(1));
394
+        REQUIRE(map.front()->pos == 1);
395
+
396
+        map.insert(2, 3.f);
397
+        map.insert(3, 3.f);
398
+        REQUIRE(map.size() == 3);
399
+        REQUIRE(map.contains(2));
400
+        REQUIRE(map.contains(3));
401
+        REQUIRE(map.front()->pos == 1);
402
+    }
403
+
404
+    SECTION("Erase")
405
+    {
406
+        AtomHashMap map(100);
407
+        map.setTotalLength(1000);
408
+
409
+        map.insert(1, 1.f);
410
+        map.insert(2, 3.f);
411
+        map.insert(3, 3.f);
412
+
413
+        map.erase(1);
414
+        REQUIRE(map.size() == 2);
415
+        REQUIRE(!map.contains(1));
416
+        REQUIRE(map.contains(2));
417
+        REQUIRE(map.contains(3));
418
+        REQUIRE(map.front()->pos == 2);
419
+    }
420
+
421
+    #ifdef GAPS_INTERNAL_TESTS
422
+    SECTION("Keeping Track of Full Buckets")
423
+    {
424
+        AtomHashMap map(100);
425
+        map.setTotalLength(1000);
426
+
427
+        map.insert(1, 1.f);
428
+        REQUIRE(map.mFullBuckets.size() == 1);
429
+        REQUIRE(map.mFullBuckets.count(0));
430
+
431
+        map.insert(2, 2.f);
432
+        REQUIRE(map.mFullBuckets.size() == 1);
433
+
434
+        map.insert(10, 10.f);
435
+        REQUIRE(map.mFullBuckets.size() == 2);
436
+        REQUIRE(map.mFullBuckets.count(1));
437
+
438
+        map.insert(20, 20.f);
439
+        REQUIRE(map.mFullBuckets.size() == 3);
440
+        REQUIRE(map.mFullBuckets.count(2));
441
+
442
+        map.erase(10);
443
+        REQUIRE(map.mFullBuckets.size() == 2);
444
+        REQUIRE(map.mFullBuckets.count(0));
445
+        REQUIRE(!map.mFullBuckets.count(1));
446
+        REQUIRE(map.mFullBuckets.count(2));
447
+    }
448
+    
449
+    SECTION("Keeping Track of Largest Bucket")
450
+    {
451
+        AtomHashMap map(100);
452
+        map.setTotalLength(1000);
453
+
454
+        for (unsigned i = 0; i < 9; ++i)
455
+        {
456
+            for (unsigned j = 1; j <= i + 1; ++j)
457
+            {
458
+                map.insert(10 * i + j, static_cast<float>(10 * i + j));
459
+            }
460
+            REQUIRE(map.mWhichLongestBucket == i);
461
+            REQUIRE(map.mLongestBucketSize == i + 1);
462
+        }
463
+
464
+        map.erase(89);
465
+        REQUIRE(map.mLongestBucketSize == 8);
466
+
467
+        map.erase(88);
468
+        map.erase(78);
469
+        REQUIRE(map.mLongestBucketSize == 7);
470
+
471
+        map.erase(87);
472
+        map.erase(77);
473
+        map.erase(67);
474
+        REQUIRE(map.mLongestBucketSize == 6);
475
+
476
+        map.erase(86);
477
+        map.erase(76);
478
+        map.erase(66);
479
+        map.erase(56);
480
+        REQUIRE(map.mLongestBucketSize == 5);
481
+
482
+        map.erase(85);
483
+        map.erase(75);
484
+        map.erase(65);
485
+        map.erase(55);
486
+        map.erase(45);
487
+        REQUIRE(map.mLongestBucketSize == 4);
488
+    }
489
+
490
+    SECTION("Random Index Selection")
491
+    {
492
+        // create has map
493
+        AtomHashMap map(100);
494
+        map.setTotalLength(1000);
495
+
496
+        for (unsigned i = 0; i < 9; ++i)
497
+        {
498
+            for (unsigned j = 1; j <= i + 1; ++j)
499
+            {
500
+                map.insert(10 * i + j, static_cast<float>(10 * i + j));
501
+            }
502
+            REQUIRE(map.mWhichLongestBucket == i);
503
+            REQUIRE(map.mLongestBucketSize == i + 1);
504
+        }
505
+
506
+        // all buckets should be selected uniformly
507
+        unsigned counts[9] = {0};
508
+        for (unsigned i = 0; i < 45000; ++i)
509
+        {
510
+            HashMapIndex index = map.getRandomIndex();
511
+            counts[index.bucket]++;
512
+        }
513
+
514
+        for (unsigned i = 0; i < 9; ++i)
515
+        {
516
+            gaps_printf("bucket histogram: %d %d\n", i, counts[i]);
517
+            REQUIRE(counts[i] > 950 * (i + 1));
518
+            REQUIRE(counts[i] < 1050 * (i + 1));
519
+        }
520
+
521
+        // all positions should be selected uniformly
522
+        unsigned largestBucket[9] = {0};
523
+        for (unsigned i = 0; i < 45000; ++i)
524
+        {
525
+            HashMapIndex index = map.getRandomIndex();
526
+            if (index.bucket == 8)
527
+            {
528
+                largestBucket[index.index]++;
529
+            }
530
+        }
531
+
532
+        for (unsigned i = 0; i < 9; ++i)
533
+        {
534
+            gaps_printf("position histogram: %d %d\n", i, largestBucket[i]);
535
+            REQUIRE(largestBucket[i] > 950);
536
+            REQUIRE(largestBucket[i] < 1050);
537
+        }
538
+    }
539
+
540
+    SECTION("Maintaining Adjacent Buckets")
541
+    {
542
+        AtomHashMap map(100);
543
+        map.setTotalLength(1000);
544
+
545
+        map.insert(1, 1.f);
546
+        map.insert(9, 9.f);
547
+        map.insert(10, 10.f);
548
+        map.insert(19, 19.f);
549
+        map.insert(20, 20.f);
550
+        map.insert(29, 29.f);
551
+
552
+        REQUIRE(map.mHashMap[0].getRight(1)->pos == 10);
553
+        REQUIRE(map.mHashMap[1].getLeft(0)->pos == 9);
554
+
555
+        REQUIRE(map.mHashMap[1].getRight(1)->pos == 20);
556
+        REQUIRE(map.mHashMap[2].getLeft(0)->pos == 19);
557
+
558
+        map.erase(10);
559
+        map.erase(19);
560
+
561
+        REQUIRE(map.mHashMap[0].getRight(1)->pos == 20);
562
+        REQUIRE(map.mHashMap[2].getLeft(0)->pos == 9);
563
+    }
564
+    #endif
565
+
566
+    SECTION("Random Atom Selection")
567
+    {
568
+        // create hash map
569
+        AtomHashMap map(100);
570
+        map.setTotalLength(1000);
571
+
572
+        // average entry equal to 57
573
+        for (unsigned i = 0; i < 9; ++i)
574
+        {
575
+            for (unsigned j = 1; j <= i + 1; ++j)
576
+            {
577
+                map.insert(10 * i + j, static_cast<float>(10 * i + j));
578
+            }
579
+        }
580
+
581
+        // random selection of single atom
582
+        float sum = 0.f;
583
+        for (unsigned i = 0; i < 1000; ++i)
584
+        {
585
+            REQUIRE(map.randomAtom()->pos >= 1ull);
586
+            REQUIRE(map.randomAtom()->pos <= 89ull);
587
+            REQUIRE(map.randomAtom()->mass >= 1.f);
588
+            REQUIRE(map.randomAtom()->mass <= 89.f);
589
+            sum += map.randomAtom()->mass;
590
+        }
591
+        REQUIRE(sum > 0.95f * 57000.f);
592
+        REQUIRE(sum < 1.05f * 57000.f);
593
+
594
+        // random selection of atom and right neighbor
595
+        sum = 0.f;
596
+        for (unsigned i = 0; i < 1000; ++i)
597
+        {
598
+            AtomNeighborhood hood = map.randomAtomWithRightNeighbor();
599
+            REQUIRE(hood.center->pos >= 1ull);
600
+            REQUIRE(hood.center->pos <= 89ull);
601
+            REQUIRE(hood.center->mass >= 1.f);
602
+            REQUIRE(hood.center->mass <= 89.f);
603
+            sum += hood.center->mass;
604
+
605
+            REQUIRE(!hood.hasLeft());
606
+            if (!hood.hasRight())
607
+            {
608
+                REQUIRE(hood.center->pos == 89);
609
+            }
610
+            else
611
+            {
612
+                REQUIRE(hood.right->pos > hood.center->pos);
613
+                REQUIRE(hood.right->mass > hood.center->mass);
614
+                REQUIRE(hood.right->pos - hood.center->pos <= 10);
615
+            }
616
+        }
617
+        REQUIRE(sum > 0.95f * 57000.f);
618
+        REQUIRE(sum < 1.05f * 57000.f);
619
+
620
+        // random selection of atom and both neighbors
621
+        sum = 0.f;
622
+        for (unsigned i = 0; i < 1000; ++i)
623
+        {
624
+            AtomNeighborhood hood = map.randomAtomWithNeighbors();
625
+            REQUIRE(hood.center->pos >= 1ull);
626
+            REQUIRE(hood.center->pos <= 89ull);
627
+            REQUIRE(hood.center->mass >= 1.f);
628
+            REQUIRE(hood.center->mass <= 89.f);
629
+            sum += hood.center->mass;
630
+
631
+            if (!hood.hasLeft())
632
+            {
633
+                REQUIRE(hood.center->pos == 1);
634
+            }
635
+            else
636
+            {
637
+                REQUIRE(hood.center->pos > hood.left->pos);
638
+                REQUIRE(hood.center->mass > hood.left->mass);
639
+                REQUIRE(hood.center->pos - hood.left->pos <= 10);
640
+            }
641
+
642
+            if (!hood.hasRight())
643
+            {
644
+                REQUIRE(hood.center->pos == 89);
645
+            }
646
+            else
647
+            {
648
+                REQUIRE(hood.right->pos > hood.center->pos);
649
+                REQUIRE(hood.right->mass > hood.center->mass);
650
+                REQUIRE(hood.right->pos - hood.center->pos <= 10);
651
+            }
652
+        }
653
+        REQUIRE(sum > 0.95f * 57000.f);
654
+        REQUIRE(sum < 1.05f * 57000.f);
655
+    }
656
+}
3 657
\ No newline at end of file