@@ -21,8 +21,9 @@ extension _BTree: BidirectionalCollection {
2121 @inline ( __always)
2222 internal var isEmpty : Bool { self . count == 0 }
2323
24+ // TODO: look into O(1) implementation
2425 /// Locates the first element and returns a proper path to it, or nil if the BTree is empty.
25- /// - Complexity: O(1 )
26+ /// - Complexity: O(`log n` )
2627 @inlinable
2728 internal var startIndex : Index {
2829 if count == 0 { return Index ( nil , forTree: self ) }
@@ -156,7 +157,7 @@ extension _BTree: BidirectionalCollection {
156157 }
157158
158159 // Otherwise, re-seek
159- i = self . indexToElement ( at : newIndex)
160+ i = Index ( self . path ( atOffset : newIndex) , forTree : self )
160161 }
161162
162163 /// Returns an index that is the specified distance from the given index.
@@ -225,98 +226,126 @@ extension _BTree: BidirectionalCollection {
225226 @inline ( __always)
226227 internal subscript( index: Index ) -> Element {
227228 assert ( index. path != nil , " Attempt to subscript out of range index. " )
228- return index. path! . element
229+ return index. path. unsafelyUnwrapped . element
229230 }
230231}
231232
232233// MARK: Custom Indexing Operations
233234extension _BTree {
234- /// Returns the path corresponding to the first found instance of the key. This may
235- /// not be the first instance of the key. This is marginally more efficient for trees
236- /// that do not have duplicates.
237- ///
238- /// - Parameter key: The key to search for within the tree.
239- /// - Returns: If found, returns a path to the element. Otherwise, `nil`.
235+ /// Returns a path to the key at absolute offset `i`.
236+ /// - Parameter offset: 0-indexed offset within BTree bounds, else may panic.
237+ /// - Returns: the index of the appropriate element.
238+ /// - Complexity: O(`log n`)
239+
240+
241+ /// Obtains the start index for a key (or where it would exist).
240242 @inlinable
241- internal func anyIndex ( forKey key: Key ) -> Index ? {
243+ internal func startIndex ( forKey key: Key ) -> Index {
242244 var childSlots = UnsafePath . Offsets ( repeating: 0 )
243- var node : Node ? = self . root
245+ var targetSlot : Int = 0
246+ var offset = 0
244247
245- while let currentNode = node {
246- let path : UnsafePath ? = currentNode. read { handle in
247- let keySlot = handle. startSlot ( forKey: key)
248- if keySlot < handle. elementCount && handle [ keyAt: keySlot] == key {
249- return UnsafePath ( node: currentNode. storage, slot: keySlot, childSlots: childSlots, offset: 0 )
250- } else {
248+ func search( in node: Node ) -> Unmanaged < Node . Storage > ? {
249+ node. read ( { handle in
250+ let slot = handle. startSlot ( forKey: key)
251+ if slot < handle. elementCount {
251252 if handle. isLeaf {
252- node = nil
253+ offset += slot
254+ targetSlot = slot
255+ return . passUnretained( node. storage)
253256 } else {
254- childSlots. append ( UInt16 ( keySlot) )
255- node = handle [ childAt: keySlot]
257+ // Calculate offset by summing previous subtrees
258+ for i in 0 ... slot {
259+ offset += handle [ childAt: i] . read ( { $0. subtreeCount } )
260+ }
261+
262+ let currentOffset = offset
263+ let currentDepth = childSlots. depth
264+ childSlots. append ( UInt16 ( slot) )
265+
266+ if let foundEarlier = search ( in: handle [ childAt: slot] ) {
267+ return foundEarlier
268+ } else {
269+ childSlots. depth = currentDepth
270+ targetSlot = slot
271+ offset = currentOffset
272+
273+ return . passUnretained( node. storage)
274+ }
256275 }
257-
276+ } else {
277+ // Start index exceeds node and is therefore not in this.
258278 return nil
259279 }
260- }
261-
262- if let path = path {
263- return Index ( path, forTree: self )
264- }
280+ } )
265281 }
266282
267- return nil
283+ if let targetChild = search ( in: self . root) {
284+ return Index ( UnsafePath (
285+ node: targetChild,
286+ slot: targetSlot,
287+ childSlots: childSlots,
288+ offset: offset
289+ ) , forTree: self )
290+ } else {
291+ return Index ( nil , forTree: self )
292+ }
268293 }
269294
270- /// Returns a path to the key at absolute offset `i`.
271- /// - Parameter offset: 0-indexed offset within BTree bounds, else may panic.
272- /// - Returns: the index of the appropriate element.
273- /// - Complexity: O(`log n`)
295+ /// Obtains the last index at which a value less than or equal to the key appears.
274296 @inlinable
275- internal func indexToElement( at offset: Int ) -> Index {
276- assert ( offset <= self . count, " Index out of bounds. " )
277-
278- if offset == self . count {
279- return Index ( nil , forTree: self )
280- }
281-
297+ internal func lastIndex( forKey key: Key ) -> Index {
282298 var childSlots = UnsafePath . Offsets ( repeating: 0 )
299+ var targetSlot : Int = 0
300+ var offset = 0
283301
284- var node : _Node = self . root
285- var startIndex = 0
286-
287- while !node. read ( { $0. isLeaf } ) {
288- let internalPath : UnsafePath ? = node. read { handle in
289- for childSlot in 0 ..< handle. childCount {
290- let child = handle [ childAt: childSlot]
291- let endIndex = startIndex + child. read ( { $0. subtreeCount } )
302+ func search( in node: Node ) -> Unmanaged < Node . Storage > ? {
303+ node. read ( { handle in
304+ let slot = handle. endSlot ( forKey: key) - 1
305+ if slot > 0 {
306+ // Sanity Check
307+ assert ( slot < handle. elementCount, " Slot out of bounds. " )
292308
293- if offset < endIndex {
294- childSlots. append ( UInt16 ( childSlot) )
295- node = child
296- return nil
297- } else if offset == endIndex {
298- // We've found the node we want
299- return UnsafePath ( node: node, slot: childSlot, childSlots: childSlots, offset: offset)
309+ if handle. isLeaf {
310+ offset += slot
311+ targetSlot = slot
312+ return . passUnretained( node. storage)
300313 } else {
301- startIndex = endIndex + 1
314+ for i in 0 ... slot {
315+ offset += handle [ childAt: i] . read ( { $0. subtreeCount } )
316+ }
317+
318+ let currentOffset = offset
319+ let currentDepth = childSlots. depth
320+ childSlots. append ( UInt16 ( slot + 1 ) )
321+
322+ if let foundLater = search ( in: handle [ childAt: slot + 1 ] ) {
323+ return foundLater
324+ } else {
325+ childSlots. depth = currentDepth
326+ targetSlot = slot
327+ offset = currentOffset
328+
329+ return . passUnretained( node. storage)
330+ }
302331 }
332+ } else {
333+ // Start index exceeds node and is therefore not in this.
334+ return nil
303335 }
304-
305- // TODO: convert into debug-only preconditionFaliure
306- preconditionFailure ( " In-bounds index not found within tree. " )
307- }
308-
309- if let internalPath = internalPath { return Index ( internalPath, forTree: self ) }
336+ } )
310337 }
311338
312- let path : UnsafePath = UnsafePath (
313- node: node,
314- slot: offset - startIndex,
315- childSlots: childSlots,
316- offset: offset
317- )
318-
319- return Index ( path, forTree: self )
339+ if let targetChild = search ( in: self . root) {
340+ return Index ( UnsafePath (
341+ node: targetChild,
342+ slot: targetSlot,
343+ childSlots: childSlots,
344+ offset: offset
345+ ) , forTree: self )
346+ } else {
347+ return Index ( nil , forTree: self )
348+ }
320349 }
321-
350+
322351}
0 commit comments