| 1 | /*
 | 
| 2 |  * Souffle - A Datalog Compiler
 | 
| 3 |  * Copyright (c) 2018, Souffle Developers
 | 
| 4 |  * Licensed under the Universal Permissive License v 1.0 as shown at:
 | 
| 5 |  * - https://opensource.org/licenses/UPL
 | 
| 6 |  * - <souffle root>/licenses/SOUFFLE-UPL.txt
 | 
| 7 |  */
 | 
| 8 | 
 | 
| 9 | /************************************************************************
 | 
| 10 |  *
 | 
| 11 |  * @file LambdaBTree.h
 | 
| 12 |  *
 | 
| 13 |  * An implementation of a generic B-tree data structure including
 | 
| 14 |  * interfaces for utilizing instances as set or multiset containers.
 | 
| 15 |  * Allows the user to provide a function to execute on successful insert
 | 
| 16 |  * Be careful using this, it currently expects a pair as the key.
 | 
| 17 |  *
 | 
| 18 |  ***********************************************************************/
 | 
| 19 | 
 | 
| 20 | #pragma once
 | 
| 21 | 
 | 
| 22 | #include "souffle/datastructure/BTree.h"
 | 
| 23 | #include "souffle/utility/ContainerUtil.h"
 | 
| 24 | #include "souffle/utility/ParallelUtil.h"
 | 
| 25 | #include <atomic>
 | 
| 26 | #include <cassert>
 | 
| 27 | #include <typeinfo>
 | 
| 28 | #include <vector>
 | 
| 29 | 
 | 
| 30 | namespace souffle {
 | 
| 31 | 
 | 
| 32 | namespace detail {
 | 
| 33 | /**
 | 
| 34 |  * The actual implementation of a b-tree data structure.
 | 
| 35 |  *
 | 
| 36 |  * @tparam Key             .. the element type to be stored in this tree
 | 
| 37 |  * @tparam Comparator     .. a class defining an order on the stored elements
 | 
| 38 |  * @tparam Allocator     .. utilized for allocating memory for required nodes
 | 
| 39 |  * @tparam blockSize    .. determines the number of bytes/block utilized by leaf nodes
 | 
| 40 |  * @tparam SearchStrategy .. enables switching between linear, binary or any other search strategy
 | 
| 41 |  * @tparam isSet        .. true = set, false = multiset
 | 
| 42 |  * @tparam Functor      .. a std::function that is called on successful (new) insert
 | 
| 43 |  */
 | 
| 44 | template <typename Key, typename Comparator,
 | 
| 45 |         typename Allocator,  // is ignored so far - TODO: add support
 | 
| 46 |         unsigned blockSize, typename SearchStrategy, bool isSet, typename Functor,
 | 
| 47 |         typename WeakComparator = Comparator, typename Updater = detail::updater<Key>>
 | 
| 48 | class LambdaBTree : public btree<Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, WeakComparator,
 | 
| 49 |                             Updater> {
 | 
| 50 | public:
 | 
| 51 |     using parenttype =
 | 
| 52 |             btree<Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, WeakComparator, Updater>;
 | 
| 53 | 
 | 
| 54 |     LambdaBTree(const Comparator& comp = Comparator(), const WeakComparator& weak_comp = WeakComparator())
 | 
| 55 |             : parenttype(comp, weak_comp) {}
 | 
| 56 | 
 | 
| 57 |     /**
 | 
| 58 |      * Inserts the given key into this tree.
 | 
| 59 |      */
 | 
| 60 |     typename Functor::result_type insert(Key& k, const Functor& f) {
 | 
| 61 |         typename parenttype::operation_hints hints;
 | 
| 62 |         return insert(k, hints, f);
 | 
| 63 |     }
 | 
| 64 | 
 | 
| 65 |     // rewriting this because of david's changes
 | 
| 66 |     typename Functor::result_type insert(
 | 
| 67 |             Key& k, typename parenttype::operation_hints& hints, const Functor& f) {
 | 
| 68 | #ifdef IS_PARALLEL
 | 
| 69 | 
 | 
| 70 |         // special handling for inserting first element
 | 
| 71 |         while (this->root == nullptr) {
 | 
| 72 |             // try obtaining root-lock
 | 
| 73 |             if (!this->root_lock.try_start_write()) {
 | 
| 74 |                 // somebody else was faster => re-check
 | 
| 75 |                 continue;
 | 
| 76 |             }
 | 
| 77 | 
 | 
| 78 |             // check loop condition again
 | 
| 79 |             if (this->root != nullptr) {
 | 
| 80 |                 // somebody else was faster => normal insert
 | 
| 81 |                 this->root_lock.abort_write();
 | 
| 82 |                 break;
 | 
| 83 |             }
 | 
| 84 | 
 | 
| 85 |             // create new node
 | 
| 86 |             this->leftmost = new typename parenttype::leaf_node();
 | 
| 87 |             this->leftmost->numElements = 1;
 | 
| 88 |             // call the functor as we've successfully inserted
 | 
| 89 |             typename Functor::result_type res = f(k);
 | 
| 90 | 
 | 
| 91 |             this->leftmost->keys[0] = k;
 | 
| 92 |             this->root = this->leftmost;
 | 
| 93 | 
 | 
| 94 |             // operation complete => we can release the root lock
 | 
| 95 |             this->root_lock.end_write();
 | 
| 96 | 
 | 
| 97 |             hints.last_insert.access(this->leftmost);
 | 
| 98 | 
 | 
| 99 |             return res;
 | 
| 100 |         }
 | 
| 101 | 
 | 
| 102 |         // insert using iterative implementation
 | 
| 103 | 
 | 
| 104 |         typename parenttype::node* cur = nullptr;
 | 
| 105 | 
 | 
| 106 |         // test last insert hints
 | 
| 107 |         typename parenttype::lock_type::Lease cur_lease;
 | 
| 108 | 
 | 
| 109 |         auto checkHint = [&](typename parenttype::node* last_insert) {
 | 
| 110 |             // ignore null pointer
 | 
| 111 |             if (!last_insert) return false;
 | 
| 112 |             // get a read lease on indicated node
 | 
| 113 |             auto hint_lease = last_insert->lock.start_read();
 | 
| 114 |             // check whether it covers the key
 | 
| 115 |             if (!this->weak_covers(last_insert, k)) return false;
 | 
| 116 |             // and if there was no concurrent modification
 | 
| 117 |             if (!last_insert->lock.validate(hint_lease)) return false;
 | 
| 118 |             // use hinted location
 | 
| 119 |             cur = last_insert;
 | 
| 120 |             // and keep lease
 | 
| 121 |             cur_lease = hint_lease;
 | 
| 122 |             // we found a hit
 | 
| 123 |             return true;
 | 
| 124 |         };
 | 
| 125 | 
 | 
| 126 |         if (hints.last_insert.any(checkHint)) {
 | 
| 127 |             // register this as a hit
 | 
| 128 |             this->hint_stats.inserts.addHit();
 | 
| 129 |         } else {
 | 
| 130 |             // register this as a miss
 | 
| 131 |             this->hint_stats.inserts.addMiss();
 | 
| 132 |         }
 | 
| 133 | 
 | 
| 134 |         // if there is no valid hint ..
 | 
| 135 |         if (!cur) {
 | 
| 136 |             do {
 | 
| 137 |                 // get root - access lock
 | 
| 138 |                 auto root_lease = this->root_lock.start_read();
 | 
| 139 | 
 | 
| 140 |                 // start with root
 | 
| 141 |                 cur = this->root;
 | 
| 142 | 
 | 
| 143 |                 // get lease of the next node to be accessed
 | 
| 144 |                 cur_lease = cur->lock.start_read();
 | 
| 145 | 
 | 
| 146 |                 // check validity of root pointer
 | 
| 147 |                 if (this->root_lock.end_read(root_lease)) {
 | 
| 148 |                     break;
 | 
| 149 |                 }
 | 
| 150 | 
 | 
| 151 |             } while (true);
 | 
| 152 |         }
 | 
| 153 | 
 | 
| 154 |         while (true) {
 | 
| 155 |             // handle inner nodes
 | 
| 156 |             if (cur->inner) {
 | 
| 157 |                 auto a = &(cur->keys[0]);
 | 
| 158 |                 auto b = &(cur->keys[cur->numElements]);
 | 
| 159 | 
 | 
| 160 |                 auto pos = this->search.lower_bound(k, a, b, this->weak_comp);
 | 
| 161 |                 auto idx = pos - a;
 | 
| 162 | 
 | 
| 163 |                 // early exit for sets
 | 
| 164 |                 if (isSet && pos != b && this->weak_equal(*pos, k)) {
 | 
| 165 |                     // validate results
 | 
| 166 |                     if (!cur->lock.validate(cur_lease)) {
 | 
| 167 |                         // start over again
 | 
| 168 |                         return insert(k, hints, f);
 | 
| 169 |                     }
 | 
| 170 | 
 | 
| 171 |                     // update provenance information
 | 
| 172 |                     if (typeid(Comparator) != typeid(WeakComparator) && this->less(k, *pos)) {
 | 
| 173 |                         if (!cur->lock.try_upgrade_to_write(cur_lease)) {
 | 
| 174 |                             // start again
 | 
| 175 |                             return insert(k, hints, f);
 | 
| 176 |                         }
 | 
| 177 |                         this->update(*pos, k);
 | 
| 178 | 
 | 
| 179 |                         // get result before releasing lock
 | 
| 180 |                         auto res = (*pos).second;
 | 
| 181 | 
 | 
| 182 |                         cur->lock.end_write();
 | 
| 183 |                         return res;
 | 
| 184 |                     }
 | 
| 185 | 
 | 
| 186 |                     // get the result before releasing lock
 | 
| 187 |                     auto res = (*pos).second;
 | 
| 188 | 
 | 
| 189 |                     // check validity
 | 
| 190 |                     if (!cur->lock.validate(cur_lease)) {
 | 
| 191 |                         // start over again
 | 
| 192 |                         return insert(k, hints, f);
 | 
| 193 |                     }
 | 
| 194 | 
 | 
| 195 |                     // we found the element => return the result
 | 
| 196 |                     return res;
 | 
| 197 |                 }
 | 
| 198 | 
 | 
| 199 |                 // get next pointer
 | 
| 200 |                 auto next = cur->getChild(idx);
 | 
| 201 | 
 | 
| 202 |                 // get lease on next level
 | 
| 203 |                 auto next_lease = next->lock.start_read();
 | 
| 204 | 
 | 
| 205 |                 // check whether there was a write
 | 
| 206 |                 if (!cur->lock.end_read(cur_lease)) {
 | 
| 207 |                     // start over
 | 
| 208 |                     return insert(k, hints, f);
 | 
| 209 |                 }
 | 
| 210 | 
 | 
| 211 |                 // go to next
 | 
| 212 |                 cur = next;
 | 
| 213 | 
 | 
| 214 |                 // move on lease
 | 
| 215 |                 cur_lease = next_lease;
 | 
| 216 | 
 | 
| 217 |                 continue;
 | 
| 218 |             }
 | 
| 219 | 
 | 
| 220 |             // the rest is for leaf nodes
 | 
| 221 |             assert(!cur->inner);
 | 
| 222 | 
 | 
| 223 |             // -- insert node in leaf node --
 | 
| 224 | 
 | 
| 225 |             auto a = &(cur->keys[0]);
 | 
| 226 |             auto b = &(cur->keys[cur->numElements]);
 | 
| 227 | 
 | 
| 228 |             auto pos = this->search.upper_bound(k, a, b, this->weak_comp);
 | 
| 229 |             auto idx = pos - a;
 | 
| 230 | 
 | 
| 231 |             // early exit for sets
 | 
| 232 |             if (isSet && pos != a && this->weak_equal(*(pos - 1), k)) {
 | 
| 233 |                 // validate result
 | 
| 234 |                 if (!cur->lock.validate(cur_lease)) {
 | 
| 235 |                     // start over again
 | 
| 236 |                     return insert(k, hints, f);
 | 
| 237 |                 }
 | 
| 238 | 
 | 
| 239 |                 // TODO (pnappa): remove provenance from LambdaBTree - no use for it
 | 
| 240 |                 // update provenance information
 | 
| 241 |                 if (typeid(Comparator) != typeid(WeakComparator) && this->less(k, *(pos - 1))) {
 | 
| 242 |                     if (!cur->lock.try_upgrade_to_write(cur_lease)) {
 | 
| 243 |                         // start again
 | 
| 244 |                         return insert(k, hints, f);
 | 
| 245 |                     }
 | 
| 246 |                     this->update(*(pos - 1), k);
 | 
| 247 | 
 | 
| 248 |                     // retrieve result before releasing lock
 | 
| 249 |                     auto res = (*(pos - 1)).second;
 | 
| 250 | 
 | 
| 251 |                     cur->lock.end_write();
 | 
| 252 |                     return res;
 | 
| 253 |                 }
 | 
| 254 | 
 | 
| 255 |                 // read result (atomic) -- just as a proof of concept, this is actually not valid!!
 | 
| 256 |                 std::atomic<typename Functor::result_type>& loc =
 | 
| 257 |                         *reinterpret_cast<std::atomic<typename Functor::result_type>*>(&(*(pos - 1)).second);
 | 
| 258 |                 auto res = loc.load(std::memory_order_relaxed);
 | 
| 259 | 
 | 
| 260 |                 // check validity
 | 
| 261 |                 if (!cur->lock.validate(cur_lease)) {
 | 
| 262 |                     // start over again
 | 
| 263 |                     return insert(k, hints, f);
 | 
| 264 |                 }
 | 
| 265 | 
 | 
| 266 |                 // we found the element => done
 | 
| 267 |                 return res;
 | 
| 268 |             }
 | 
| 269 | 
 | 
| 270 |             // upgrade to write-permission
 | 
| 271 |             if (!cur->lock.try_upgrade_to_write(cur_lease)) {
 | 
| 272 |                 // something has changed => restart
 | 
| 273 |                 hints.last_insert.access(cur);
 | 
| 274 |                 return insert(k, hints, f);
 | 
| 275 |             }
 | 
| 276 | 
 | 
| 277 |             if (cur->numElements >= parenttype::node::maxKeys) {
 | 
| 278 |                 // -- lock parents --
 | 
| 279 |                 auto priv = cur;
 | 
| 280 |                 auto parent = priv->parent;
 | 
| 281 |                 std::vector<typename parenttype::node*> parents;
 | 
| 282 |                 do {
 | 
| 283 |                     if (parent) {
 | 
| 284 |                         parent->lock.start_write();
 | 
| 285 |                         while (true) {
 | 
| 286 |                             // check whether parent is correct
 | 
| 287 |                             if (parent == priv->parent) {
 | 
| 288 |                                 break;
 | 
| 289 |                             }
 | 
| 290 |                             // switch parent
 | 
| 291 |                             parent->lock.abort_write();
 | 
| 292 |                             parent = priv->parent;
 | 
| 293 |                             parent->lock.start_write();
 | 
| 294 |                         }
 | 
| 295 |                     } else {
 | 
| 296 |                         // lock root lock => since cur is root
 | 
| 297 |                         this->root_lock.start_write();
 | 
| 298 |                     }
 | 
| 299 | 
 | 
| 300 |                     // record locked node
 | 
| 301 |                     parents.push_back(parent);
 | 
| 302 | 
 | 
| 303 |                     // stop at "sphere of influence"
 | 
| 304 |                     if (!parent || !parent->isFull()) {
 | 
| 305 |                         break;
 | 
| 306 |                     }
 | 
| 307 | 
 | 
| 308 |                     // go one step higher
 | 
| 309 |                     priv = parent;
 | 
| 310 |                     parent = parent->parent;
 | 
| 311 | 
 | 
| 312 |                 } while (true);
 | 
| 313 | 
 | 
| 314 |                 // split this node
 | 
| 315 |                 auto old_root = this->root;
 | 
| 316 |                 idx -= cur->rebalance_or_split(const_cast<typename parenttype::node**>(&this->root),
 | 
| 317 |                         this->root_lock, static_cast<int>(idx), parents);
 | 
| 318 | 
 | 
| 319 |                 // release parent lock
 | 
| 320 |                 for (auto it = parents.rbegin(); it != parents.rend(); ++it) {
 | 
| 321 |                     auto parent = *it;
 | 
| 322 | 
 | 
| 323 |                     // release this lock
 | 
| 324 |                     if (parent) {
 | 
| 325 |                         parent->lock.end_write();
 | 
| 326 |                     } else {
 | 
| 327 |                         if (old_root != this->root) {
 | 
| 328 |                             this->root_lock.end_write();
 | 
| 329 |                         } else {
 | 
| 330 |                             this->root_lock.abort_write();
 | 
| 331 |                         }
 | 
| 332 |                     }
 | 
| 333 |                 }
 | 
| 334 | 
 | 
| 335 |                 // insert element in right fragment
 | 
| 336 |                 if (((typename parenttype::size_type)idx) > cur->numElements) {
 | 
| 337 |                     // release current lock
 | 
| 338 |                     cur->lock.end_write();
 | 
| 339 | 
 | 
| 340 |                     // insert in sibling
 | 
| 341 |                     return insert(k, hints, f);
 | 
| 342 |                 }
 | 
| 343 |             }
 | 
| 344 | 
 | 
| 345 |             // ok - no split necessary
 | 
| 346 |             assert(cur->numElements < parenttype::node::maxKeys && "Split required!");
 | 
| 347 | 
 | 
| 348 |             // move keys
 | 
| 349 |             for (int j = static_cast<int>(cur->numElements); j > idx; --j) {
 | 
| 350 |                 cur->keys[j] = cur->keys[j - 1];
 | 
| 351 |             }
 | 
| 352 | 
 | 
| 353 |             // insert new element
 | 
| 354 |             typename Functor::result_type res = f(k);
 | 
| 355 |             cur->keys[idx] = k;
 | 
| 356 |             cur->numElements++;
 | 
| 357 | 
 | 
| 358 |             // release lock on current node
 | 
| 359 |             cur->lock.end_write();
 | 
| 360 | 
 | 
| 361 |             // remember last insertion position
 | 
| 362 |             hints.last_insert.access(cur);
 | 
| 363 |             return res;
 | 
| 364 |         }
 | 
| 365 | 
 | 
| 366 | #else
 | 
| 367 |         // special handling for inserting first element
 | 
| 368 |         if (this->empty()) {
 | 
| 369 |             // create new node
 | 
| 370 |             this->leftmost = new typename parenttype::leaf_node();
 | 
| 371 |             this->leftmost->numElements = 1;
 | 
| 372 |             // call the functor as we've successfully inserted
 | 
| 373 |             typename Functor::result_type res = f(k);
 | 
| 374 |             this->leftmost->keys[0] = k;
 | 
| 375 |             this->root = this->leftmost;
 | 
| 376 | 
 | 
| 377 |             hints.last_insert.access(this->leftmost);
 | 
| 378 | 
 | 
| 379 |             return res;
 | 
| 380 |         }
 | 
| 381 | 
 | 
| 382 |         // insert using iterative implementation
 | 
| 383 |         typename parenttype::node* cur = this->root;
 | 
| 384 | 
 | 
| 385 |         auto checkHints = [&](typename parenttype::node* last_insert) {
 | 
| 386 |             if (!last_insert) return false;
 | 
| 387 |             if (!this->weak_covers(last_insert, k)) return false;
 | 
| 388 |             cur = last_insert;
 | 
| 389 |             return true;
 | 
| 390 |         };
 | 
| 391 | 
 | 
| 392 |         // test last insert
 | 
| 393 |         if (hints.last_insert.any(checkHints)) {
 | 
| 394 |             this->hint_stats.inserts.addHit();
 | 
| 395 |         } else {
 | 
| 396 |             this->hint_stats.inserts.addMiss();
 | 
| 397 |         }
 | 
| 398 | 
 | 
| 399 |         while (true) {
 | 
| 400 |             // handle inner nodes
 | 
| 401 |             if (cur->inner) {
 | 
| 402 |                 auto a = &(cur->keys[0]);
 | 
| 403 |                 auto b = &(cur->keys[cur->numElements]);
 | 
| 404 | 
 | 
| 405 |                 auto pos = this->search.lower_bound(k, a, b, this->weak_comp);
 | 
| 406 |                 auto idx = pos - a;
 | 
| 407 | 
 | 
| 408 |                 // early exit for sets
 | 
| 409 |                 if (isSet && pos != b && this->weak_equal(*pos, k)) {
 | 
| 410 |                     // update provenance information
 | 
| 411 |                     if (typeid(Comparator) != typeid(WeakComparator) && this->less(k, *pos)) {
 | 
| 412 |                         this->update(*pos, k);
 | 
| 413 |                         return (*pos).second;
 | 
| 414 |                     }
 | 
| 415 | 
 | 
| 416 |                     return (*pos).second;
 | 
| 417 |                 }
 | 
| 418 | 
 | 
| 419 |                 cur = cur->getChild(idx);
 | 
| 420 |                 continue;
 | 
| 421 |             }
 | 
| 422 | 
 | 
| 423 |             // the rest is for leaf nodes
 | 
| 424 |             assert(!cur->inner);
 | 
| 425 | 
 | 
| 426 |             // -- insert node in leaf node --
 | 
| 427 | 
 | 
| 428 |             auto a = &(cur->keys[0]);
 | 
| 429 |             auto b = &(cur->keys[cur->numElements]);
 | 
| 430 | 
 | 
| 431 |             auto pos = this->search.upper_bound(k, a, b, this->weak_comp);
 | 
| 432 |             auto idx = pos - a;
 | 
| 433 | 
 | 
| 434 |             // early exit for sets
 | 
| 435 |             if (isSet && pos != a && this->weak_equal(*(pos - 1), k)) {
 | 
| 436 |                 // update provenance information
 | 
| 437 |                 if (typeid(Comparator) != typeid(WeakComparator) && this->less(k, *(pos - 1))) {
 | 
| 438 |                     this->update(*(pos - 1), k);
 | 
| 439 |                     return (*(pos - 1)).second;
 | 
| 440 |                 }
 | 
| 441 | 
 | 
| 442 |                 return (*(pos - 1)).second;
 | 
| 443 |             }
 | 
| 444 | 
 | 
| 445 |             if (cur->numElements >= parenttype::node::maxKeys) {
 | 
| 446 |                 // split this node
 | 
| 447 |                 idx -= cur->rebalance_or_split(const_cast<typename parenttype::node**>(&this->root),
 | 
| 448 |                         this->root_lock, static_cast<int>(idx));
 | 
| 449 | 
 | 
| 450 |                 // insert element in right fragment
 | 
| 451 |                 if (((typename parenttype::size_type)idx) > cur->numElements) {
 | 
| 452 |                     idx -= cur->numElements + 1;
 | 
| 453 |                     cur = cur->parent->getChild(cur->position + 1);
 | 
| 454 |                 }
 | 
| 455 |             }
 | 
| 456 | 
 | 
| 457 |             // ok - no split necessary
 | 
| 458 |             assert(cur->numElements < parenttype::node::maxKeys && "Split required!");
 | 
| 459 | 
 | 
| 460 |             // move keys
 | 
| 461 |             for (int j = cur->numElements; j > idx; --j) {
 | 
| 462 |                 cur->keys[j] = cur->keys[j - 1];
 | 
| 463 |             }
 | 
| 464 | 
 | 
| 465 |             // call the functor as we've successfully inserted
 | 
| 466 |             typename Functor::result_type res = f(k);
 | 
| 467 |             // insert new element
 | 
| 468 |             cur->keys[idx] = k;
 | 
| 469 |             cur->numElements++;
 | 
| 470 | 
 | 
| 471 |             // remember last insertion position
 | 
| 472 |             hints.last_insert.access(cur);
 | 
| 473 |             return res;
 | 
| 474 |         }
 | 
| 475 | #endif
 | 
| 476 |     }
 | 
| 477 | 
 | 
| 478 |     /**
 | 
| 479 |      * Inserts the given range of elements into this tree.
 | 
| 480 |      */
 | 
| 481 |     template <typename Iter>
 | 
| 482 |     void insert(const Iter& a, const Iter& b) {
 | 
| 483 |         // TODO: improve this beyond a naive insert
 | 
| 484 |         typename parenttype::operation_hints hints;
 | 
| 485 |         // a naive insert so far .. seems to work fine
 | 
| 486 |         for (auto it = a; it != b; ++it) {
 | 
| 487 |             // use insert with hint
 | 
| 488 |             insert(*it, hints);
 | 
| 489 |         }
 | 
| 490 |     }
 | 
| 491 | 
 | 
| 492 |     /**
 | 
| 493 |      * Swaps the content of this tree with the given tree. This
 | 
| 494 |      * is a much more efficient operation than creating a copy and
 | 
| 495 |      * realizing the swap utilizing assignment operations.
 | 
| 496 |      */
 | 
| 497 |     void swap(LambdaBTree& other) {
 | 
| 498 |         // swap the content
 | 
| 499 |         std::swap(this->root, other.root);
 | 
| 500 |         std::swap(this->leftmost, other.leftmost);
 | 
| 501 |     }
 | 
| 502 | 
 | 
| 503 |     // Implementation of the assignment operation for trees.
 | 
| 504 |     LambdaBTree& operator=(const LambdaBTree& other) {
 | 
| 505 |         // check identity
 | 
| 506 |         if (this == &other) {
 | 
| 507 |             return *this;
 | 
| 508 |         }
 | 
| 509 | 
 | 
| 510 |         // create a deep-copy of the content of the other tree
 | 
| 511 |         // shortcut for empty sets
 | 
| 512 |         if (other.empty()) {
 | 
| 513 |             return *this;
 | 
| 514 |         }
 | 
| 515 | 
 | 
| 516 |         // clone content (deep copy)
 | 
| 517 |         this->root = other.root->clone();
 | 
| 518 | 
 | 
| 519 |         // update leftmost reference
 | 
| 520 |         auto tmp = this->root;
 | 
| 521 |         while (!tmp->isLeaf()) {
 | 
| 522 |             tmp = tmp->getChild(0);
 | 
| 523 |         }
 | 
| 524 |         this->leftmost = static_cast<typename parenttype::leaf_node*>(tmp);
 | 
| 525 | 
 | 
| 526 |         // done
 | 
| 527 |         return *this;
 | 
| 528 |     }
 | 
| 529 | 
 | 
| 530 |     // Implementation of an equality operation for trees.
 | 
| 531 |     bool operator==(const LambdaBTree& other) const {
 | 
| 532 |         // check identity
 | 
| 533 |         if (this == &other) {
 | 
| 534 |             return true;
 | 
| 535 |         }
 | 
| 536 | 
 | 
| 537 |         // check size
 | 
| 538 |         if (this->size() != other.size()) {
 | 
| 539 |             return false;
 | 
| 540 |         }
 | 
| 541 |         if (this->size() < other.size()) {
 | 
| 542 |             return other == *this;
 | 
| 543 |         }
 | 
| 544 | 
 | 
| 545 |         // check content
 | 
| 546 |         for (const auto& key : other) {
 | 
| 547 |             if (!contains(key)) {
 | 
| 548 |                 return false;
 | 
| 549 |             }
 | 
| 550 |         }
 | 
| 551 |         return true;
 | 
| 552 |     }
 | 
| 553 | 
 | 
| 554 |     // Implementation of an inequality operation for trees.
 | 
| 555 |     bool operator!=(const LambdaBTree& other) const {
 | 
| 556 |         return !(*this == other);
 | 
| 557 |     }
 | 
| 558 | };
 | 
| 559 | 
 | 
| 560 | }  // end namespace detail
 | 
| 561 | 
 | 
| 562 | /**
 | 
| 563 |  * A b-tree based set implementation.
 | 
| 564 |  *
 | 
| 565 |  * @tparam Key             .. the element type to be stored in this set
 | 
| 566 |  * @tparam Functor         .. a std::function that is invoked on successful insert
 | 
| 567 |  * @tparam Comparator     .. a class defining an order on the stored elements
 | 
| 568 |  * @tparam Allocator     .. utilized for allocating memory for required nodes
 | 
| 569 |  * @tparam blockSize    .. determines the number of bytes/block utilized by leaf nodes
 | 
| 570 |  * @tparam SearchStrategy .. enables switching between linear, binary or any other search strategy
 | 
| 571 |  */
 | 
| 572 | template <typename Key, typename Functor, typename Comparator = detail::comparator<Key>,
 | 
| 573 |         typename Allocator = std::allocator<Key>,  // is ignored so far
 | 
| 574 |         unsigned blockSize = 256, typename SearchStrategy = typename detail::default_strategy<Key>::type>
 | 
| 575 | class LambdaBTreeSet
 | 
| 576 |         : public detail::LambdaBTree<Key, Comparator, Allocator, blockSize, SearchStrategy, true, Functor> {
 | 
| 577 |     using super = detail::LambdaBTree<Key, Comparator, Allocator, blockSize, SearchStrategy, true, Functor>;
 | 
| 578 | 
 | 
| 579 |     friend class detail::LambdaBTree<Key, Comparator, Allocator, blockSize, SearchStrategy, true, Functor>;
 | 
| 580 | 
 | 
| 581 | public:
 | 
| 582 |     /**
 | 
| 583 |      * A default constructor creating an empty set.
 | 
| 584 |      */
 | 
| 585 |     LambdaBTreeSet(const Comparator& comp = Comparator()) : super(comp) {}
 | 
| 586 | 
 | 
| 587 |     /**
 | 
| 588 |      * A constructor creating a set based on the given range.
 | 
| 589 |      */
 | 
| 590 |     template <typename Iter>
 | 
| 591 |     LambdaBTreeSet(const Iter& a, const Iter& b) {
 | 
| 592 |         this->insert(a, b);
 | 
| 593 |     }
 | 
| 594 | 
 | 
| 595 |     // A copy constructor.
 | 
| 596 |     LambdaBTreeSet(const LambdaBTreeSet& other) : super(other) {}
 | 
| 597 | 
 | 
| 598 |     // A move constructor.
 | 
| 599 |     LambdaBTreeSet(LambdaBTreeSet&& other) : super(std::move(other)) {}
 | 
| 600 | 
 | 
| 601 | private:
 | 
| 602 |     // A constructor required by the bulk-load facility.
 | 
| 603 |     template <typename s, typename n, typename l>
 | 
| 604 |     LambdaBTreeSet(s size, n* root, l* leftmost) : super::parenttype(size, root, leftmost) {}
 | 
| 605 | 
 | 
| 606 | public:
 | 
| 607 |     // Support for the assignment operator.
 | 
| 608 |     LambdaBTreeSet& operator=(const LambdaBTreeSet& other) {
 | 
| 609 |         super::operator=(other);
 | 
| 610 |         return *this;
 | 
| 611 |     }
 | 
| 612 | 
 | 
| 613 |     // Support for the bulk-load operator.
 | 
| 614 |     template <typename Iter>
 | 
| 615 |     static LambdaBTreeSet load(const Iter& a, const Iter& b) {
 | 
| 616 |         return super::template load<LambdaBTreeSet>(a, b);
 | 
| 617 |     }
 | 
| 618 | };
 | 
| 619 | 
 | 
| 620 | }  // end of namespace souffle
 |