├── go.mod ├── go.sum ├── LICENSE ├── README.md ├── celltree.go └── celltree_test.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tidwall/celltree 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/google/btree v1.0.0 7 | github.com/tidwall/lotsa v1.0.1 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 2 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 3 | github.com/tidwall/lotsa v1.0.1 h1:w4gpDvI7RdkgbMC0q5ndKqG2ffrwCgerUY/gM2TYkH4= 4 | github.com/tidwall/lotsa v1.0.1/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Joshua J Baker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `celltree` 2 | 3 | [![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/tidwall/celltree) 4 | 5 | A fast in-memory prefix tree that uses uint64 for keys, interface{} for data, and allows for duplicate entries. 6 | 7 | # Getting Started 8 | 9 | ### Installing 10 | 11 | To start using celltree, install Go and run `go get`: 12 | 13 | ```sh 14 | $ go get -u github.com/tidwall/celltree 15 | ``` 16 | 17 | ## Example 18 | 19 | ```go 20 | var tr celltree.Tree 21 | 22 | tr.Insert(10, nil) 23 | tr.Insert(5, nil) 24 | tr.Insert(31, nil) 25 | tr.Insert(16, nil) 26 | tr.Insert(9, nil) 27 | 28 | tr.Scan(func(cell uint64, data interface{}) bool { 29 | println(cell) 30 | return true 31 | }) 32 | ``` 33 | 34 | Outputs: 35 | 36 | ``` 37 | 5 38 | 9 39 | 10 40 | 16 41 | 31 42 | ``` 43 | 44 | ## Performance 45 | 46 | Single threaded performance comparing this package to 47 | [google/btree](https://github.com/google/btree). 48 | 49 | ``` 50 | $ go test 51 | 52 | -- celltree -- 53 | insert 1,048,576 ops in 318ms 3,296,579/sec 54 | scan 100 ops in 824ms 121/sec 55 | range 1,048,576 ops in 144ms 7,245,252/sec 56 | remove 1,048,576 ops in 244ms 4,281,322/sec 57 | memory 40,567,280 bytes 38/entry 58 | 59 | -- btree -- 60 | insert 1,048,576 ops in 1003ms 1,044,876/sec 61 | scan 100 ops in 1195ms 83/sec 62 | range 1,048,576 ops in 443ms 2,364,467/sec 63 | remove 1,048,576 ops in 1198ms 874,723/sec 64 | memory 49,034,992 bytes 46/entry 65 | ``` 66 | 67 | *These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.10* 68 | 69 | ## Contact 70 | 71 | Josh Baker [@tidwall](http://twitter.com/tidwall) 72 | 73 | ## License 74 | 75 | `celltree` source code is available under the MIT [License](/LICENSE). 76 | -------------------------------------------------------------------------------- /celltree.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Joshua J Baker. All rights reserved. 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package celltree 6 | 7 | const ( 8 | numBits = 7 // [1,2,3,4...8] match numNodes with the correct numBits 9 | numNodes = 128 // [2,4,8,16...256] match numNodes with the correct numBits 10 | ) 11 | const ( 12 | maxItems = 256 // max num of items in a leaf 13 | minItems = maxItems * 40 / 100 // min num of items in a branch 14 | ) 15 | 16 | type item struct { 17 | cell uint64 18 | data interface{} 19 | } 20 | 21 | type node struct { 22 | branch bool // is a branch (not a leaf) 23 | items []item // leaf items 24 | nodes []node // child nodes 25 | count int // count of all cells for this node and children 26 | } 27 | 28 | // Tree is a uint64 prefix tree 29 | type Tree struct { 30 | count int // number of items in tree 31 | root *node // root node 32 | } 33 | 34 | // Count returns the number of items in the tree. 35 | func (tr *Tree) Count() int { 36 | return tr.count 37 | } 38 | 39 | func cellIndex(cell uint64, bits uint) int { 40 | return int(cell >> bits & uint64(numNodes-1)) 41 | } 42 | 43 | // InsertOrReplace inserts an item into the tree. Items are ordered by it's 44 | // cell. The extra param is a simple user context value. The cond function is 45 | // used to allow for replacing an existing cell with a new cell. When the 46 | // 'replace' return value is set to false, then the original data is inserted. 47 | // When the 'replace' value is true the existing cell data is replace with 48 | // newData. 49 | func (tr *Tree) InsertOrReplace( 50 | cell uint64, data interface{}, 51 | cond func(data interface{}) (newData interface{}, replace bool), 52 | ) { 53 | if tr.root == nil { 54 | tr.root = new(node) 55 | } 56 | if tr.root.insert(cell, data, 64-numBits, cond) { 57 | tr.count++ 58 | } 59 | } 60 | 61 | // Insert inserts an item into the tree. Items are ordered by it's cell. 62 | // The extra param is a simple user context value. 63 | func (tr *Tree) Insert(cell uint64, data interface{}) { 64 | tr.InsertOrReplace(cell, data, nil) 65 | } 66 | 67 | func (n *node) splitLeaf(bits uint) { 68 | n.branch = true 69 | // reset the node count to zero 70 | n.count = 0 71 | // create space for all of the nodes 72 | n.nodes = make([]node, numNodes) 73 | // reinsert all of leaf items 74 | for i := 0; i < len(n.items); i++ { 75 | n.insert(n.items[i].cell, n.items[i].data, bits, nil) 76 | } 77 | // release the leaf items 78 | n.items = nil 79 | } 80 | 81 | func maxDepth(bits uint) bool { 82 | return bits < numBits 83 | } 84 | 85 | func (n *node) insert( 86 | cell uint64, data interface{}, bits uint, 87 | cond func(data interface{}) (newData interface{}, replace bool), 88 | ) (inserted bool) { 89 | if !n.branch { 90 | // leaf node 91 | atcap := !maxDepth(bits) && len(n.items) >= maxItems 92 | insertAgain: 93 | if atcap && cond == nil { 94 | // split leaf. it's at capacity 95 | n.splitLeaf(bits) 96 | // insert item again, but this time node is a branch 97 | n.insert(cell, data, bits, nil) 98 | // we need to deduct one item from the count, otherwise it'll be 99 | // the target cell will be counted twice 100 | n.count-- 101 | } else { 102 | // find the target index for the new cell 103 | if len(n.items) == 0 || n.items[len(n.items)-1].cell < cell { 104 | // the new cell is greater than the last cell in leaf, so 105 | // we can just append it 106 | if atcap { 107 | cond = nil 108 | goto insertAgain 109 | } 110 | n.items = append(n.items, item{cell: cell, data: data}) 111 | } else { 112 | // locate the index of the cell in the leaf 113 | index := n.findLeafItemSeqIns(cell) 114 | if cond != nil { 115 | // find a duplicate cell 116 | for i := index - 1; i >= 0; i-- { 117 | if n.items[i].cell != cell { 118 | // did not find 119 | break 120 | } 121 | // found a duplicate 122 | newData, replace := cond(n.items[i].data) 123 | if replace { 124 | // must replace the cell data instead of inserting 125 | // a new one. 126 | n.items[i].data = newData 127 | return false 128 | } 129 | } 130 | // condition func was not safisfied. this means that the 131 | // new item will be inserted/ 132 | if atcap { 133 | cond = nil 134 | goto insertAgain 135 | } 136 | } 137 | // create space for the new cell 138 | n.items = append(n.items, item{}) 139 | // move other cells over to make room for new cell 140 | copy(n.items[index+1:], n.items[index:len(n.items)-1]) 141 | // assign the new cell 142 | n.items[index] = item{cell: cell, data: data} 143 | } 144 | } 145 | } else { 146 | // branch node 147 | // locate the index of the child node in the leaf 148 | index := cellIndex(cell, bits) 149 | // insert the cell into the child node 150 | if !n.nodes[index].insert(cell, data, bits-numBits, cond) { 151 | return false 152 | } 153 | } 154 | // increment the node 155 | n.count++ 156 | return true 157 | } 158 | 159 | // findLeafItemSeqIns position where the return value is the index for 160 | // inserting a new cell into the items array. 161 | // Optimized for sequential inserts 162 | func (n *node) findLeafItemSeqIns(cell uint64) int { 163 | for i := len(n.items) - 1; i >= 0; i-- { 164 | if cell >= n.items[i].cell { 165 | return i + 1 166 | } 167 | } 168 | return 0 169 | } 170 | 171 | // findLeafItemBin position where the return value is the index for 172 | // inserting a new cell into the items array. 173 | // Optimized for binary searching 174 | func (n *node) findLeafItemBin(cell uint64) int { 175 | i, j := 0, len(n.items) 176 | for i < j { 177 | h := i + (j-i)/2 178 | if cell >= n.items[h].cell { 179 | i = h + 1 180 | } else { 181 | j = h 182 | } 183 | } 184 | return i 185 | } 186 | 187 | // Delete removes an item from the tree based on it's cell and data values. 188 | func (tr *Tree) Delete(cell uint64, data interface{}) { 189 | if tr.root == nil { 190 | return 191 | } 192 | if tr.root.nodeDelete(cell, data, 64-numBits, nil) { 193 | tr.count-- 194 | } 195 | } 196 | 197 | func (n *node) nodeDelete( 198 | cell uint64, data interface{}, bits uint, 199 | cond func(data interface{}) bool, 200 | ) (deleted bool) { 201 | if !n.branch { 202 | // leaf node 203 | i := n.findLeafItemBin(cell) - 1 204 | for ; i >= 0; i-- { 205 | if n.items[i].cell != cell { 206 | // did not find 207 | break 208 | } 209 | if (cond == nil && n.items[i].data == data) || 210 | (cond != nil && cond(n.items[i].data)) { 211 | // found the cell, remove it now 212 | // if the len of items has fallen below 40% of it's cap then 213 | // shrink the items 214 | if len(n.items) == 1 { 215 | // do not have non-nil leaves hanging around 216 | n.items = nil 217 | } else { 218 | min := cap(n.items) * 40 / 100 219 | if len(n.items)-1 <= min { 220 | // shrink and realloc the array 221 | items := make([]item, len(n.items)-1, cap(n.items)/2) 222 | copy(items[:i], n.items[:i]) 223 | copy(items[i:], n.items[i+1:len(n.items)]) 224 | n.items = items 225 | } else { 226 | // keep the same array 227 | n.items[i] = item{} 228 | copy(n.items[i:len(n.items)-1], n.items[i+1:]) 229 | n.items = n.items[:len(n.items)-1] 230 | } 231 | } 232 | deleted = true 233 | break 234 | } 235 | } 236 | } else { 237 | // branch node 238 | index := cellIndex(cell, bits) 239 | deleted = n.nodes[index].nodeDelete(cell, data, bits-numBits, cond) 240 | } 241 | if deleted { 242 | // an item was deleted from this node or a child node 243 | // decrement the counter 244 | n.count-- 245 | if n.branch && n.count <= minItems { 246 | // compact the branch into a leaf 247 | n.compactBranch() 248 | } 249 | } 250 | return deleted 251 | } 252 | 253 | // DeleteWhen removes an item from the tree based on it's cell and when the 254 | // cond func returns true. It will delete at most a maximum of one item. 255 | func (tr *Tree) DeleteWhen(cell uint64, cond func(data interface{}) bool) { 256 | if tr.root == nil { 257 | return 258 | } 259 | if tr.root.nodeDelete(cell, nil, 64-numBits, cond) { 260 | tr.count-- 261 | } 262 | } 263 | 264 | func (n *node) flatten(items []item) []item { 265 | if !n.branch { 266 | items = append(items, n.items...) 267 | } else { 268 | for _, child := range n.nodes { 269 | if child.count > 0 { 270 | items = child.flatten(items) 271 | } 272 | } 273 | } 274 | return items 275 | } 276 | 277 | func (n *node) compactBranch() { 278 | n.items = n.flatten(nil) 279 | n.branch = false 280 | n.nodes = nil 281 | n.count = len(n.items) 282 | } 283 | 284 | // Scan iterates over the entire tree. Return false from iter function to stop. 285 | func (tr *Tree) Scan(iter func(cell uint64, data interface{}) bool) { 286 | if tr.root == nil { 287 | return 288 | } 289 | tr.root.scan(iter) 290 | } 291 | 292 | func (n *node) scan(iter func(cell uint64, data interface{}) bool) bool { 293 | if !n.branch { 294 | for i := 0; i < len(n.items); i++ { 295 | if !iter(n.items[i].cell, n.items[i].data) { 296 | return false 297 | } 298 | } 299 | } else { 300 | for i := 0; i < len(n.nodes); i++ { 301 | if n.nodes[i].count > 0 { 302 | if !n.nodes[i].scan(iter) { 303 | return false 304 | } 305 | } 306 | } 307 | } 308 | return true 309 | } 310 | 311 | // Range iterates over the tree starting with the start param. 312 | func (tr *Tree) Range( 313 | start uint64, 314 | iter func(cell uint64, data interface{}) bool, 315 | ) { 316 | if tr.root != nil { 317 | tr.root.nodeRange(start, 64-numBits, false, iter) 318 | } 319 | } 320 | 321 | func (n *node) nodeRange( 322 | start uint64, bits uint, hit bool, 323 | iter func(cell uint64, data interface{}) bool, 324 | ) (hitout bool, ok bool) { 325 | if !n.branch { 326 | for _, item := range n.items { 327 | if item.cell < start { 328 | continue 329 | } 330 | if !iter(item.cell, item.data) { 331 | return false, false 332 | } 333 | } 334 | return true, true 335 | } 336 | var index int 337 | if hit { 338 | index = 0 339 | } else { 340 | index = cellIndex(start, bits) 341 | } 342 | for ; index < len(n.nodes); index++ { 343 | if n.nodes[index].count == 0 { 344 | hit = true 345 | } else { 346 | hit, ok = n.nodes[index].nodeRange(start, bits-numBits, hit, iter) 347 | if !ok { 348 | return false, false 349 | } 350 | } 351 | } 352 | return hit, true 353 | } 354 | 355 | // RangeDelete iterates over the tree starting with the start param and "asks" 356 | // the iterator if the item should be deleted. 357 | func (tr *Tree) RangeDelete( 358 | start, end uint64, 359 | iter func(cell uint64, data interface{}) (shouldDelete bool, ok bool), 360 | ) { 361 | if tr.root == nil { 362 | return 363 | } 364 | _, deleted, _ := tr.root.nodeRangeDelete( 365 | start, end, 64-numBits, 0, false, iter) 366 | tr.count -= deleted 367 | } 368 | 369 | func (n *node) nodeRangeDelete( 370 | start, end uint64, bits uint, base uint64, hit bool, 371 | iter func(cell uint64, data interface{}) (shouldDelete bool, ok bool), 372 | ) (hitout bool, deleted int, ok bool) { 373 | if !n.branch { 374 | ok = true 375 | var skipIterator bool 376 | if iter == nil && len(n.items) > 0 { 377 | if n.items[0].cell >= start && 378 | n.items[len(n.items)-1].cell <= end { 379 | // clear the entire leaf 380 | deleted = len(n.items) 381 | skipIterator = true 382 | } 383 | } 384 | for i := 0; !skipIterator && i < len(n.items); i++ { 385 | if n.items[i].cell < start { 386 | continue 387 | } 388 | var shouldDelete bool 389 | if ok { 390 | // ask if the current item should be deleted and/or if the 391 | // iterator should stop. 392 | if n.items[i].cell > end { 393 | // past the end, don't delete and don't continue 394 | ok = false 395 | } else { 396 | if iter == nil { 397 | shouldDelete = true 398 | } else { 399 | shouldDelete, ok = iter( 400 | n.items[i].cell, n.items[i].data) 401 | } 402 | } 403 | } else { 404 | // a previous iterator requested to stop, so do not delete 405 | // the current item. 406 | shouldDelete = false 407 | } 408 | if shouldDelete { 409 | // should delete item. increment the delete counter 410 | deleted++ 411 | } else { 412 | // should keep item. 413 | if deleted > 0 { 414 | // there's room in a previously deleted slot, move the 415 | // current item there. 416 | n.items[i-deleted] = n.items[i] 417 | n.items[i].data = nil 418 | } else if !ok { 419 | // the iterate requested a stop and since there's no 420 | // deleted items, we can immediately stop here. 421 | break 422 | } 423 | } 424 | } 425 | if deleted > 0 { 426 | // there was some deleted items so we need to adjust the length 427 | // of the items array to reflect the change 428 | n.items = n.items[:len(n.items)-deleted] 429 | if len(n.items) == 0 { 430 | n.items = nil 431 | } else { 432 | // check if the base array needs to be shrunk/reallocated. 433 | ncap := cap(n.items) 434 | min := ncap * 40 / 100 435 | if len(n.items) <= min { 436 | for len(n.items) <= min { 437 | ncap /= 2 438 | min = ncap * 40 / 100 439 | } 440 | // shrink and realloc the array 441 | items := make([]item, len(n.items), ncap) 442 | copy(items, n.items) 443 | n.items = items 444 | } 445 | } 446 | } 447 | // set the hit flag once a leaf is reached 448 | hit = true 449 | } else { 450 | var index int 451 | if hit { 452 | // target leaf node has been reached. this means we can just start at 453 | // index zero and expect that all of the following noes are candidates. 454 | index = 0 455 | } else { 456 | // target leaf node has not been reached yet so we need to determine 457 | // the best path to get to it. 458 | index = cellIndex(start, bits) 459 | } 460 | for ; index < len(n.nodes); index++ { 461 | if n.nodes[index].count == 0 { 462 | hit = true 463 | } else { 464 | var dropped bool 465 | if hit && iter == nil { 466 | cellStart := (base + uint64(index)) << bits 467 | cellEnd := ((base + uint64(index+1)) << bits) - 1 468 | // we've already hit a leaf and the iter is nil. It's 469 | // possible that this entire node can be deleted if it's 470 | // cell range fits within start/end. 471 | if cellStart >= start && cellEnd <= end { 472 | // drop the node altogether 473 | deleted += n.nodes[index].count 474 | n.nodes[index] = node{} 475 | dropped = true 476 | } 477 | } 478 | if !dropped { 479 | var ndeleted int 480 | hit, ndeleted, ok = n.nodes[index].nodeRangeDelete( 481 | start, end, bits-numBits, 482 | (base< 0 { 493 | // an item was deleted from this node or a child node 494 | // decrement the counter 495 | n.count -= deleted 496 | if n.branch && n.count <= minItems { 497 | // compact the branch into a leaf 498 | n.compactBranch() 499 | } 500 | } 501 | return hit, deleted, ok 502 | } 503 | -------------------------------------------------------------------------------- /celltree_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Joshua J Baker. All rights reserved. 2 | // Use of this source code is governed by an MIT-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package celltree 6 | 7 | import ( 8 | "fmt" 9 | "math" 10 | "math/rand" 11 | "os" 12 | "runtime" 13 | "sort" 14 | "testing" 15 | "time" 16 | 17 | "github.com/google/btree" 18 | "github.com/tidwall/lotsa" 19 | ) 20 | 21 | func init() { 22 | seed := (time.Now().UnixNano()) 23 | //seed = 1544374208106411000 24 | println("seed:", seed) 25 | rand.Seed(seed) 26 | } 27 | 28 | func random(N int, perm bool) []uint64 { 29 | ints := make([]uint64, N) 30 | if perm { 31 | for i, x := range rand.Perm(N) { 32 | ints[i] = uint64(x) 33 | } 34 | } else { 35 | m := make(map[uint64]bool) 36 | for len(m) < N { 37 | m[rand.Uint64()] = true 38 | } 39 | var i int 40 | for k := range m { 41 | ints[i] = k 42 | i++ 43 | } 44 | } 45 | return ints 46 | } 47 | 48 | func shuffle(ints []uint64) { 49 | for i := range ints { 50 | j := rand.Intn(i + 1) 51 | ints[i], ints[j] = ints[j], ints[i] 52 | } 53 | } 54 | 55 | func sortInts(ints []uint64) { 56 | sort.Slice(ints, func(i, j int) bool { 57 | return ints[i] < ints[j] 58 | }) 59 | } 60 | 61 | func testEquals(t *testing.T, random, sorted []uint64) { 62 | t.Helper() 63 | sortInts(random) 64 | if len(sorted) != len(random) { 65 | t.Fatal("not equal") 66 | } 67 | for i := 0; i < len(sorted); i++ { 68 | if sorted[i] != random[i] { 69 | t.Fatal("not equal") 70 | } 71 | } 72 | } 73 | 74 | // sane tests the sanity of the tree. Any problems will panic. 75 | func (tr *Tree) sane() { 76 | if tr.root == nil { 77 | if tr.count != 0 { 78 | panic(fmt.Sprintf("sane: expected %d, got %d", 0, tr.count)) 79 | } 80 | return 81 | } 82 | count, _ := tr.root.saneCount(0, 64-numBits) 83 | if tr.count != count { 84 | panic(fmt.Sprintf("sane: expected %d, got %d", count, tr.count)) 85 | } 86 | } 87 | 88 | func (n *node) saneCount(cell uint64, bits uint) (count int, cellout uint64) { 89 | if !n.branch { 90 | // all leaves count should match the number of items. 91 | if n.count != len(n.items) { 92 | panic(fmt.Sprintf("leaf has a count of %d, but %d items in array", 93 | n.count, len(n.items))) 94 | } 95 | // leaves should never go above max items unless they are at max depth. 96 | if n.count > maxItems && !maxDepth(bits) { 97 | panic(fmt.Sprintf("leaf has a count of %d, but maxItems is %d", 98 | n.count, maxItems)) 99 | } 100 | // all leaves should not have non-nil leaves 101 | if len(n.items) == 0 && n.items != nil { 102 | panic(fmt.Sprintf("leaf has zero items, but a non-nil items array")) 103 | } 104 | // test if the items cells are in order 105 | for i := 0; i < len(n.items); i++ { 106 | if n.items[i].cell < cell { 107 | panic(fmt.Sprintf("leaf out of order at index: %d", i)) 108 | } 109 | cell = n.items[i].cell 110 | } 111 | // all leaves should *not* fall below 40% capacity 112 | min := cap(n.items) * 40 / 100 113 | if len(n.items) <= min && len(n.items) > 0 { 114 | println(len(n.items), cap(n.items), min) 115 | panic(fmt.Sprintf("leaf is underfilled")) 116 | } 117 | return len(n.items), cell 118 | } 119 | // all branches should have a count of at least 1 120 | if n.count <= 0 { 121 | panic(fmt.Sprintf("branch has a count of %d", n.count)) 122 | } 123 | // all branches should have a nil items array 124 | if n.items != nil { 125 | panic(fmt.Sprintf("branch has non-nil items")) 126 | } 127 | // check each node 128 | for i := 0; i < len(n.nodes); i++ { 129 | ncount, ncell := n.nodes[i].saneCount(cell, bits-numBits) 130 | count += ncount 131 | if ncell < cell { 132 | panic(fmt.Sprintf("branch out of order at index: %d", i)) 133 | } 134 | cell = ncell 135 | } 136 | if n.count != count { 137 | panic(fmt.Sprintf("branch has wrong count, expected %d, got %d", 138 | count, n.count)) 139 | } 140 | return count, cell 141 | } 142 | 143 | func TestRandomSingleStep(t *testing.T) { 144 | testRandomStep(t) 145 | } 146 | 147 | func testRandomStep(t *testing.T) { 148 | N := (rand.Int() % 10000) 149 | if N%2 == 1 { 150 | N++ 151 | } 152 | ints := random(N, rand.Int()%2 == 0) 153 | var tr Tree 154 | for i := 0; i < N; i++ { 155 | tr.Insert(ints[i], nil) 156 | tr.sane() 157 | } 158 | if tr.Count() != N { 159 | t.Fatalf("expected %v, got %v", N, tr.Count()) 160 | } 161 | var all []uint64 162 | tr.Scan(func(cell uint64, data interface{}) bool { 163 | all = append(all, cell) 164 | return true 165 | }) 166 | testEquals(t, ints, all) 167 | if N > 0 { 168 | pivot := ints[len(ints)/2] 169 | var rangeCells []uint64 170 | tr.Range(pivot, func(cell uint64, data interface{}) bool { 171 | rangeCells = append(rangeCells, cell) 172 | return true 173 | }) 174 | 175 | var scanCells []uint64 176 | tr.Scan(func(cell uint64, data interface{}) bool { 177 | if cell >= pivot { 178 | scanCells = append(scanCells, cell) 179 | } 180 | return true 181 | }) 182 | testEquals(t, scanCells, rangeCells) 183 | } 184 | shuffle(ints) 185 | for i := 0; i < len(ints)/2; i++ { 186 | tr.Delete(ints[i], nil) 187 | tr.sane() 188 | } 189 | if tr.Count() != N/2 { 190 | t.Fatalf("expected %v, got %v", N/2, tr.Count()) 191 | } 192 | for i := len(ints) / 2; i < len(ints); i++ { 193 | tr.Delete(ints[i], nil) 194 | tr.sane() 195 | } 196 | if tr.Count() != 0 { 197 | t.Fatalf("expected %v, got %v", 0, tr.Count()) 198 | } 199 | } 200 | func TestRandom(t *testing.T) { 201 | start := time.Now() 202 | for time.Since(start) < time.Second { 203 | testRandomStep(t) 204 | } 205 | } 206 | 207 | func TestVarious(t *testing.T) { 208 | var tr Tree 209 | tr.Delete(0, nil) 210 | tr.DeleteWhen(0, nil) 211 | tr.Scan(nil) 212 | tr.Range(0, nil) 213 | tr.RangeDelete(0, math.MaxUint64, nil) 214 | 215 | N := 2000 216 | for i := 0; i < N; i++ { 217 | tr.Insert(uint64(i), i) 218 | tr.sane() 219 | } 220 | 221 | for i := 0; i < N; i++ { 222 | var j int 223 | tr.Scan(func(cell uint64, data interface{}) bool { 224 | if j == i { 225 | return false 226 | } 227 | j++ 228 | return true 229 | }) 230 | } 231 | 232 | for i := 0; i < N; i++ { 233 | var j int 234 | tr.Range(0, func(cell uint64, data interface{}) bool { 235 | if j == i { 236 | return false 237 | } 238 | j++ 239 | return true 240 | }) 241 | } 242 | 243 | } 244 | 245 | func TestDeleteWhen(t *testing.T) { 246 | var tr Tree 247 | tr.Insert(10, 0) 248 | tr.sane() 249 | tr.Insert(5, 1) 250 | tr.sane() 251 | tr.Insert(31, 2) 252 | tr.sane() 253 | tr.Insert(16, 3) 254 | tr.sane() 255 | tr.Insert(9, 4) 256 | tr.sane() 257 | tr.Insert(5, 5) 258 | tr.sane() 259 | tr.Insert(16, 6) 260 | tr.sane() 261 | var count int 262 | tr.DeleteWhen(16, func(data interface{}) bool { 263 | count++ 264 | return false 265 | }) 266 | if count != 2 { 267 | t.Fatalf("expected %v, got %v", 2, count) 268 | } 269 | if tr.Count() != 7 { 270 | t.Fatalf("expected %v, got %v", 7, tr.Count()) 271 | } 272 | tr.DeleteWhen(16, func(data interface{}) bool { 273 | if data.(int) == 3 { 274 | return true 275 | } 276 | return false 277 | }) 278 | if tr.Count() != 6 { 279 | t.Fatalf("expected %v, got %v", 6, tr.Count()) 280 | } 281 | } 282 | 283 | type perfCtx struct { 284 | _insert func(cell uint64) 285 | _count func() int 286 | _scan func() 287 | _range func(cell uint64, iter func(cell uint64) bool) 288 | _remove func(cell uint64) 289 | } 290 | 291 | type btreeItem uint64 292 | 293 | func (v btreeItem) Less(v2 btree.Item) bool { 294 | return v < v2.(btreeItem) 295 | } 296 | 297 | func printPerfLabel(label string, randomized, shuffled bool) { 298 | print("-- " + label + " (") 299 | if randomized { 300 | print("randomized") 301 | } else { 302 | print("sequential") 303 | } 304 | if shuffled { 305 | print(",shuffled") 306 | } else { 307 | print(",ordered") 308 | } 309 | println(") --") 310 | } 311 | func TestPerf(t *testing.T) { 312 | if os.Getenv("BASICPERF") != "1" { 313 | fmt.Printf("TestPerf disabled (BASICPERF=1)\n") 314 | return 315 | } 316 | 317 | // CellTree 318 | for i := 0; i < 4; i++ { 319 | randomized := i/2 == 0 320 | shuffled := i%2 == 0 321 | t.Run("CellTree", func(t *testing.T) { 322 | printPerfLabel("celltree", randomized, shuffled) 323 | var tr Tree 324 | ctx := perfCtx{ 325 | _insert: func(cell uint64) { tr.Insert(cell, nil) }, 326 | _count: func() int { return tr.Count() }, 327 | _scan: func() { 328 | tr.Scan(func(cell uint64, data interface{}) bool { 329 | return true 330 | }) 331 | }, 332 | _range: func(cell uint64, iter func(cell uint64) bool) { 333 | tr.Range(cell, func(cell uint64, data interface{}) bool { 334 | return iter(cell) 335 | }) 336 | }, 337 | _remove: func(cell uint64) { tr.Delete(cell, nil) }, 338 | } 339 | testPerf(t, ctx, randomized, shuffled) 340 | }) 341 | } 342 | 343 | // BTree 344 | for i := 0; i < 4; i++ { 345 | randomized := i/2 == 0 346 | shuffled := i%2 == 0 347 | t.Run("BTree", func(t *testing.T) { 348 | printPerfLabel("btree", randomized, shuffled) 349 | tr := btree.New(16) 350 | ctx := perfCtx{ 351 | _insert: func(cell uint64) { tr.ReplaceOrInsert(btreeItem(cell)) }, 352 | _count: func() int { return tr.Len() }, 353 | _scan: func() { 354 | tr.Ascend(func(item btree.Item) bool { 355 | return true 356 | }) 357 | }, 358 | _range: func(cell uint64, iter func(cell uint64) bool) { 359 | tr.AscendGreaterOrEqual(btreeItem(cell), func(item btree.Item) bool { 360 | return iter(uint64(item.(btreeItem))) 361 | }) 362 | }, 363 | _remove: func(cell uint64) { tr.Delete(btreeItem(cell)) }, 364 | } 365 | testPerf(t, ctx, randomized, shuffled) 366 | }) 367 | } 368 | } 369 | 370 | func testPerf(t *testing.T, ctx perfCtx, randomozed, shuffled bool) { 371 | N := 1024 * 1024 372 | var ints []uint64 373 | if randomozed { 374 | ints = random(N, false) 375 | } else { 376 | start := rand.Uint64() 377 | for i := 0; i < N; i++ { 378 | ints = append(ints, start+uint64(i)) 379 | } 380 | } 381 | if shuffled { 382 | shuffle(ints) 383 | } else { 384 | sort.Slice(ints, func(i, j int) bool { 385 | return ints[i] < ints[j] 386 | }) 387 | } 388 | 389 | var ms1, ms2 runtime.MemStats 390 | defer func() { 391 | heapBytes := int(ms2.HeapAlloc - ms1.HeapAlloc) 392 | fmt.Printf("memory %13s bytes %s/entry \n", 393 | commaize(heapBytes), commaize(heapBytes/len(ints))) 394 | fmt.Printf("\n") 395 | }() 396 | runtime.GC() 397 | time.Sleep(time.Millisecond * 100) 398 | runtime.ReadMemStats(&ms1) 399 | 400 | var start time.Time 401 | var dur time.Duration 402 | output := func(tag string, N int) { 403 | dur = time.Since(start) 404 | fmt.Printf("%-8s %10s ops in %4dms %10s/sec\n", 405 | tag, commaize(N), int(dur.Seconds()*1000), 406 | commaize(int(float64(N)/dur.Seconds()))) 407 | } 408 | 409 | ///////////////////////////////////////////// 410 | start = time.Now() 411 | lotsa.Ops(N, 1, func(i, _ int) { 412 | ctx._insert(ints[i]) 413 | }) 414 | output("insert", N) 415 | runtime.GC() 416 | time.Sleep(time.Millisecond * 100) 417 | runtime.ReadMemStats(&ms2) 418 | 419 | if ctx._count() != N { 420 | t.Fatalf("expected %v, got %v", N, ctx._count()) 421 | } 422 | ///////////////////////////////////////////// 423 | shuffle(ints) 424 | start = time.Now() 425 | lotsa.Ops(100, 1, func(i, _ int) { ctx._scan() }) 426 | output("scan", 100) 427 | ///////////////////////////////////////////// 428 | sortInts(ints) 429 | start = time.Now() 430 | lotsa.Ops(N, 1, func(i, _ int) { 431 | var found bool 432 | ctx._range(ints[i], func(cell uint64) bool { 433 | if cell != ints[i] { 434 | t.Fatal("invalid") 435 | } 436 | found = true 437 | return false 438 | }) 439 | if !found { 440 | t.Fatal("not found") 441 | } 442 | }) 443 | output("range", N) 444 | ///////////////////////////////////////////// 445 | shuffle(ints) 446 | start = time.Now() 447 | lotsa.Ops(N, 1, func(i, _ int) { 448 | ctx._remove(ints[i]) 449 | }) 450 | output("remove", N) 451 | if ctx._count() != 0 { 452 | t.Fatalf("expected %v, got %v", 0, ctx._count()) 453 | } 454 | } 455 | 456 | func commaize(n int) string { 457 | s1, s2 := fmt.Sprintf("%d", n), "" 458 | for i, j := len(s1)-1, 0; i >= 0; i, j = i-1, j+1 { 459 | if j%3 == 0 && j != 0 { 460 | s2 = "," + s2 461 | } 462 | s2 = string(s1[i]) + s2 463 | } 464 | return s2 465 | } 466 | 467 | func TestPerfLongTime(t *testing.T) { 468 | if os.Getenv("PERFLONGTIME") != "1" { 469 | fmt.Printf("TestPerfLongTime disabled (PERFLONGTIME=1)\n") 470 | return 471 | } 472 | x := 0 473 | N := 1024 * 1024 474 | ints := random(N, true) 475 | var tr Tree 476 | var insops, remops int 477 | var ms1, ms2 runtime.MemStats 478 | runtime.GC() 479 | runtime.ReadMemStats(&ms1) 480 | start := time.Now() 481 | var insdur, remdur time.Duration 482 | var xstart time.Time 483 | 484 | // insert all items 485 | 486 | for i := 0; i < len(ints); i++ { 487 | tr.Insert(ints[i], nil) 488 | insops++ 489 | } 490 | insdur += time.Since(start) 491 | // now delete every 4th item and rerandomize 492 | for { 493 | opp := rand.Uint64() 494 | xstart = time.Now() 495 | for i := x; i < len(ints); i += 4 { 496 | tr.Delete(ints[i], nil) 497 | ints[i] ^= opp 498 | remops++ 499 | } 500 | remdur += time.Since(xstart) 501 | xstart = time.Now() 502 | for i := x; i < len(ints); i += 4 { 503 | tr.Insert(ints[i], nil) 504 | insops++ 505 | } 506 | insdur += time.Since(xstart) 507 | if tr.Count() != N { 508 | t.Fatal("shit") 509 | } 510 | runtime.GC() 511 | runtime.ReadMemStats(&ms2) 512 | heapBytes := int(ms2.HeapAlloc - ms1.HeapAlloc) 513 | x = (x + 1) % 4 514 | dur := time.Since(start) 515 | 516 | fmt.Printf("\r %10s ops %10s ins/sec %10s rem/sec (%s bytes/cell)\r", 517 | commaize(insops+remops), 518 | commaize(int(float64(insops)/insdur.Seconds())), 519 | commaize(int(float64(remops)/remdur.Seconds())), 520 | commaize(heapBytes/N), 521 | ) 522 | if dur > time.Minute { 523 | break 524 | } 525 | } 526 | fmt.Printf("\n") 527 | } 528 | 529 | func TestDupCells(t *testing.T) { 530 | N := 1000000 531 | cell := uint64(388098102398102938) 532 | var tr Tree 533 | for i := 0; i < N; i++ { 534 | tr.Insert(cell, i) 535 | } 536 | } 537 | 538 | func cellsEqual(cells1, cells2 []uint64) bool { 539 | if len(cells1) != len(cells2) { 540 | return false 541 | } 542 | for i := 0; i < len(cells1); i++ { 543 | if cells1[i] != cells2[i] { 544 | return false 545 | } 546 | } 547 | return true 548 | } 549 | 550 | func TestRange(t *testing.T) { 551 | N := 10000 552 | start := uint64(10767499590539539808) 553 | 554 | var tr Tree 555 | for i := 0; i < N; i++ { 556 | cell := start + uint64(i) 557 | tr.Insert(cell, cell) 558 | tr.sane() 559 | } 560 | var count int 561 | var cells1 []uint64 562 | tr.Scan(func(cell uint64, value interface{}) bool { 563 | cells1 = append(cells1, cell) 564 | count++ 565 | return true 566 | }) 567 | if count != N { 568 | t.Fatalf("expected %v, got %v", N, count) 569 | } 570 | count = 0 571 | var cells2 []uint64 572 | tr.Range(0, func(cell uint64, value interface{}) bool { 573 | cells2 = append(cells2, cell) 574 | count++ 575 | return true 576 | }) 577 | if count != N { 578 | t.Fatalf("expected %v, got %v", N, count) 579 | } 580 | if !cellsEqual(cells1, cells2) { 581 | t.Fatal("not equal") 582 | } 583 | 584 | // random ranges over some random data 585 | 586 | var all []uint64 587 | tr = Tree{} 588 | for i := 0; i < N; i++ { 589 | cell := rand.Uint64() 590 | all = append(all, cell) 591 | tr.Insert(cell, cell) 592 | tr.sane() 593 | } 594 | sortInts(all) 595 | 596 | for i := 0; i < 100; i++ { 597 | min := rand.Uint64() 598 | max := rand.Uint64() 599 | if min > max { 600 | min, max = max, min 601 | } 602 | var hits1 []uint64 603 | tr.Range(min, func(cell uint64, value interface{}) bool { 604 | if cell < min { 605 | t.Fatalf("cell %v is less than %v", cell, min) 606 | } 607 | if cell > max { 608 | return false 609 | } 610 | hits1 = append(hits1, cell) 611 | return true 612 | }) 613 | var hits2 []uint64 614 | for _, cell := range all { 615 | if cell >= min && cell <= max { 616 | hits2 = append(hits2, cell) 617 | } 618 | } 619 | if !cellsEqual(hits1, hits2) { 620 | t.Fatal("cells not equal") 621 | } 622 | } 623 | 624 | } 625 | 626 | func TestDuplicates(t *testing.T) { 627 | N := 10000 628 | var tr Tree 629 | for i := 0; i < N; i++ { 630 | tr.Insert(uint64(i), i) 631 | tr.sane() 632 | } 633 | tr.InsertOrReplace(5000, 5000, 634 | func(data interface{}) (newData interface{}, replace bool) { 635 | return nil, false 636 | }, 637 | ) 638 | tr.sane() 639 | if tr.Count() != N+1 { 640 | t.Fatalf("expected %v, got %v", N+1, tr.Count()) 641 | } 642 | tr.InsertOrReplace(2500, 2500, 643 | func(data interface{}) (newData interface{}, replace bool) { 644 | return "hello", true 645 | }, 646 | ) 647 | tr.sane() 648 | if tr.Count() != N+1 { 649 | t.Fatalf("expected %v, got %v", N+1, tr.Count()) 650 | } 651 | 652 | for i := 0; i < 500; i++ { 653 | tr.InsertOrReplace(uint64(i)+10000000, i, 654 | func(data interface{}) (newData interface{}, replace bool) { 655 | if i%2 == 0 { 656 | return nil, false 657 | } 658 | return nil, false 659 | }, 660 | ) 661 | tr.sane() 662 | } 663 | } 664 | 665 | func TestRangeDelete(t *testing.T) { 666 | N := 1000 667 | start := 5000 668 | var tr Tree 669 | var cells1 []uint64 670 | for i := 0; i < N; i++ { 671 | cell := uint64(start + i) 672 | tr.Insert(cell, i) 673 | tr.sane() 674 | cells1 = append(cells1, cell) 675 | } 676 | if tr.Count() != N { 677 | t.Fatalf("expected %v, got %v", N, tr.Count()) 678 | } 679 | 680 | // starting from the second half, do not delete any 681 | var count int 682 | tr.RangeDelete(uint64(start+N/2), math.MaxUint64, 683 | func(cell uint64, value interface{}) (shouldDelete, ok bool) { 684 | count++ 685 | return false, true 686 | }, 687 | ) 688 | if count != N/2 { 689 | t.Fatalf("expected %v, got %v", N/2, count) 690 | } 691 | if tr.Count() != N { 692 | t.Fatalf("expected %v, got %v", N, tr.Count()) 693 | } 694 | 695 | // delete the last half of the items 696 | count = 0 697 | tr.RangeDelete(uint64(start+N/2), math.MaxUint64, 698 | func(cell uint64, value interface{}) (shouldDelete, ok bool) { 699 | count++ 700 | return true, true 701 | }, 702 | ) 703 | if count != N/2 { 704 | t.Fatalf("expected %v, got %v", N/2, count) 705 | } 706 | if tr.Count() != N/2 { 707 | t.Fatalf("expected %v, got %v", N/2, tr.Count()) 708 | } 709 | 710 | var cells2 []uint64 711 | tr.Scan(func(cell uint64, value interface{}) bool { 712 | cells2 = append(cells2, cell) 713 | return true 714 | }) 715 | if !cellsEqual(cells1[:N/2], cells2) { 716 | t.Fatal("not equal") 717 | } 718 | } 719 | 720 | func TestRandomRangeDelete(t *testing.T) { 721 | var count int 722 | start := time.Now() 723 | for time.Since(start) < time.Second { 724 | TestSingleRandomRangeDelete(t) 725 | count++ 726 | } 727 | } 728 | func TestSingleRandomRangeDelete(t *testing.T) { 729 | // random ranges over some random data and randomly cells 730 | N := rand.Int() % 50000 731 | if N%2 == 1 { 732 | N++ 733 | } 734 | if N < 2 { 735 | N = 2 736 | } 737 | var all []uint64 738 | var tr Tree 739 | switch rand.Int() % 5 { 740 | case 0: 741 | // random and spread out 742 | for i := 0; i < N; i++ { 743 | cell := rand.Uint64() 744 | all = append(all, cell) 745 | tr.Insert(cell, cell) 746 | } 747 | case 1: 748 | // random and centered 749 | for i := 0; i < N; i++ { 750 | cell := rand.Uint64()/2 + math.MaxUint64/4 751 | all = append(all, cell) 752 | tr.Insert(cell, cell) 753 | } 754 | case 2: 755 | // random and left-aligned 756 | for i := 0; i < N; i++ { 757 | cell := rand.Uint64() / 2 758 | all = append(all, cell) 759 | tr.Insert(cell, cell) 760 | } 761 | case 3: 762 | // random and right-aligned 763 | for i := 0; i < N; i++ { 764 | cell := rand.Uint64()/2 + math.MaxInt64/2 765 | all = append(all, cell) 766 | tr.Insert(cell, cell) 767 | } 768 | case 4: 769 | // sequential and centered 770 | for i := 0; i < N; i++ { 771 | cell := math.MaxUint64/2 - uint64(N/2+i) 772 | all = append(all, cell) 773 | tr.Insert(cell, cell) 774 | } 775 | } 776 | sortInts(all) 777 | deletes := make(map[uint64]bool) 778 | for _, cell := range all { 779 | deletes[cell] = rand.Int()%2 == 0 780 | } 781 | var min, max uint64 782 | switch rand.Uint64() % 4 { 783 | case 0: 784 | // start at zero 785 | min = 0 786 | case 1: 787 | // start before first 788 | min = all[0] / 2 789 | case 2: 790 | // start on the first 791 | min = all[0] 792 | case 3: 793 | // start in random position 794 | min = rand.Uint64() 795 | } 796 | switch rand.Uint64() % 4 { 797 | case 0: 798 | // end at max uint64 799 | min = math.MaxUint64 800 | case 1: 801 | // end after last 802 | min = (math.MaxUint64-all[len(all)-1])/2 + all[len(all)-1] 803 | case 2: 804 | // end on the last 805 | min = all[len(all)-1] 806 | case 3: 807 | // end in random position 808 | min = rand.Uint64() 809 | } 810 | if min > max { 811 | min, max = max, min 812 | } 813 | 814 | // if min == 0 { 815 | // println(min == 0, tr.root.nodes[0].count) 816 | // } 817 | 818 | var hits1 []uint64 819 | tr.RangeDelete(min, max, 820 | func(cell uint64, value interface{}) (shouldDelete bool, ok bool) { 821 | if cell < min { 822 | t.Fatalf("cell %v is less than %v", cell, min) 823 | } 824 | var ok2 bool 825 | shouldDelete, ok2 = deletes[cell] 826 | if !ok2 { 827 | t.Fatal("missing cell in deletes map") 828 | } 829 | if shouldDelete { 830 | // delete this item 831 | hits1 = append(hits1, cell) 832 | } 833 | return shouldDelete, true 834 | }, 835 | ) 836 | tr.sane() 837 | var hits2 []uint64 838 | for _, cell := range all { 839 | if cell >= min && cell <= max && deletes[cell] { 840 | hits2 = append(hits2, cell) 841 | } 842 | } 843 | if !cellsEqual(hits1, hits2) { 844 | t.Fatal("cells not equal") 845 | } 846 | } 847 | 848 | func testRangeDeleteNoIterator(t *testing.T, N int) { 849 | var tr Tree 850 | var all []uint64 851 | for i := 0; i < N; i++ { 852 | cell := rand.Uint64() 853 | all = append(all, cell) 854 | tr.Insert(cell, nil) 855 | } 856 | sortInts(all) 857 | start := uint64(math.MaxUint64 / 4) 858 | end := start + math.MaxUint64/2 859 | tr.RangeDelete(start, end, nil) 860 | tr.sane() 861 | var cells1 []uint64 862 | tr.Scan(func(cell uint64, _ interface{}) bool { 863 | cells1 = append(cells1, cell) 864 | return true 865 | }) 866 | var cells2 []uint64 867 | for _, cell := range all { 868 | if cell < start || cell > end { 869 | cells2 = append(cells2, cell) 870 | } 871 | } 872 | if !cellsEqual(cells1, cells2) { 873 | t.Fatal("not equal") 874 | } 875 | } 876 | 877 | func TestRangeDeleteNoIterator(t *testing.T) { 878 | testRangeDeleteNoIterator(t, 0) 879 | testRangeDeleteNoIterator(t, maxItems/2) 880 | testRangeDeleteNoIterator(t, maxItems-1) 881 | testRangeDeleteNoIterator(t, maxItems) 882 | testRangeDeleteNoIterator(t, maxItems+1) 883 | testRangeDeleteNoIterator(t, 100000) 884 | } 885 | --------------------------------------------------------------------------------