pax_global_header00006660000000000000000000000064143402371060014512gustar00rootroot0000000000000052 comment=ce57df5c8e51ab8c3bedab6808e0d08e204c1e24 bintree-1.2.1/000077500000000000000000000000001434023710600131435ustar00rootroot00000000000000bintree-1.2.1/LICENSE000066400000000000000000000261351434023710600141570ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. bintree-1.2.1/README.md000066400000000000000000000000411434023710600144150ustar00rootroot00000000000000# bintree Binary trees and tries bintree-1.2.1/go.mod000066400000000000000000000012061434023710600142500ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // module github.com/seancfoley/bintree go 1.18bintree-1.2.1/tree/000077500000000000000000000000001434023710600141025ustar00rootroot00000000000000bintree-1.2.1/tree/bintree.go000066400000000000000000000107471434023710600160720ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tree import ( "fmt" "strconv" "strings" ) type binTree[E Key, V any] struct { root *binTreeNode[E, V] } // GetRoot returns the root node of this trie, which can be nil for a zero-valued uninitialized trie, but not for any other trie func (tree *binTree[E, V]) GetRoot() *binTreeNode[E, V] { return tree.root } // Size returns the number of elements in the tree. // Only nodes for which IsAdded() returns true are counted. // When zero is returned, IsEmpty() returns true. func (tree *binTree[E, V]) Size() int { if tree == nil { return 0 } return tree.GetRoot().Size() } // NodeSize returns the number of nodes in the tree, which is always more than the number of elements. func (tree *binTree[E, V]) NodeSize() int { if tree == nil { return 0 } return tree.GetRoot().NodeSize() } // Clear removes all added nodes from the tree, after which IsEmpty() will return true func (tree *binTree[E, V]) Clear() { if root := tree.GetRoot(); root != nil { root.Clear() } } // IsEmpty returns true if there are not any added nodes within this tree func (tree *binTree[E, V]) IsEmpty() bool { return tree.Size() == 0 } func (tree binTree[E, V]) format(state fmt.State, verb rune) { switch verb { case 's', 'v': _, _ = state.Write([]byte(tree.String())) return } // In default fmt handling (see printValue), we write all the fields of each struct inside curlies {} // When a pointer is encountered, the pointer is printed unless the nesting depth is 0 // How that pointer is printed varies a lot depending on the verb and flags. // So, in the case of unsupported flags, let's print { rootPointer } where rootPointer is printed according to the flags and verb. s := flagsFromState(state, verb) rootStr := fmt.Sprintf(s, binTreeNodePtr[E, V](tree.root)) bytes := make([]byte, len(rootStr)+2) bytes[0] = '{' shifted := bytes[1:] copy(shifted, rootStr) shifted[len(rootStr)] = '}' _, _ = state.Write(bytes) } // String returns a visual representation of the tree with one node per line. func (tree *binTree[E, V]) String() string { return tree.TreeString(true) } // TreeString returns a visual representation of the tree with one node per line, with or without the non-added keys. func (tree *binTree[E, V]) TreeString(withNonAddedKeys bool) string { return tree.GetRoot().TreeString(withNonAddedKeys, true) } func (tree *binTree[E, V]) printTree(builder *strings.Builder, inds indents, withNonAddedKeys bool) { if tree == nil { builder.WriteString(inds.nodeIndent) builder.WriteString(nilString()) builder.WriteByte('\n') } else { tree.GetRoot().printTree(builder, inds, withNonAddedKeys, true) } } const treeKeyWildcard = '*' // Produces a visual representation of the given tries joined by a single root node, with one node per line. func treesString[E Key, V any](withNonAddedKeys bool, trees ...*binTree[E, V]) string { totalEntrySize := 0 for _, tree := range trees { totalEntrySize += tree.Size() } builder := strings.Builder{} builder.Grow(totalEntrySize * 120) // 2 labels 60 chars each builder.WriteByte('\n') builder.WriteString(nonAddedNodeCircle) isEmpty := len(trees) == 0 if !isEmpty { totalSize := 0 for _, tree := range trees { totalSize += tree.Size() } if withNonAddedKeys { builder.WriteByte(' ') builder.WriteByte(treeKeyWildcard) builder.WriteString(" (") builder.WriteString(strconv.Itoa(totalSize)) builder.WriteByte(')') } builder.WriteByte('\n') lastTreeIndex := len(trees) - 1 for i := 0; i < lastTreeIndex; i++ { trees[i].printTree(&builder, indents{ nodeIndent: leftElbow, subNodeInd: inBetweenElbows, }, withNonAddedKeys) } trees[lastTreeIndex].printTree(&builder, indents{ nodeIndent: rightElbow, subNodeInd: belowElbows, }, withNonAddedKeys) } else { if withNonAddedKeys { builder.WriteByte(' ') builder.WriteByte(treeKeyWildcard) builder.WriteString(" (0)") } builder.WriteByte('\n') } return builder.String() } bintree-1.2.1/tree/bintreeiterator.go000066400000000000000000000465631434023710600176510ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tree import ( "container/heap" ) type HasNext interface { HasNext() bool } type keyIterator[E Key] interface { HasNext Next() E // Remove removes the last iterated element from the underlying trie, and returns that element. // If there is no such element, it returns nil. Remove() E } type nodeIteratorRem[E Key, V any] interface { nodeIterator[E, V] // Remove removes the last iterated element from the underlying trie, and returns that element. // If there is no such element, it returns nil. Remove() *binTreeNode[E, V] } type nodeIterator[E Key, V any] interface { HasNext Next() *binTreeNode[E, V] } type binTreeKeyIterator[E Key, V any] struct { nodeIteratorRem[E, V] } func (iter binTreeKeyIterator[E, V]) Next() E { return iter.nodeIteratorRem.Next().GetKey() } func (iter binTreeKeyIterator[E, V]) Remove() E { return iter.nodeIteratorRem.Remove().GetKey() } func newNodeIterator[E Key, V any](forward, addedOnly bool, start, end *binTreeNode[E, V], ctracker *changeTracker) nodeIteratorRem[E, V] { var nextOperator func(current *binTreeNode[E, V], end *binTreeNode[E, V]) *binTreeNode[E, V] if forward { nextOperator = (*binTreeNode[E, V]).nextNodeBounded } else { nextOperator = (*binTreeNode[E, V]).previousNodeBounded } if addedOnly { wrappedOp := nextOperator nextOperator = func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { return currentNode.nextAdded(endNode, wrappedOp) } } res := binTreeNodeIterator[E, V]{end: end} res.setChangeTracker(ctracker) res.operator = nextOperator res.next = res.getStart(start, end, nil, addedOnly) return &res } type binTreeNodeIterator[E Key, V any] struct { // takes current node and end as args operator func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) (nextNode *binTreeNode[E, V]) end *binTreeNode[E, V] // a non-nil node that denotes the end, possibly parent of the starting node cTracker *changeTracker currentChange change current, next *binTreeNode[E, V] } func (iter *binTreeNodeIterator[E, V]) getStart( start, end *binTreeNode[E, V], bounds *bounds[E], addedOnly bool) *binTreeNode[E, V] { if start == end || start == nil { return nil } if !addedOnly || start.IsAdded() { if bounds == nil || bounds.isInBounds(start.GetKey()) { return start } } return iter.toNext(start) } func (iter *binTreeNodeIterator[E, V]) setChangeTracker(ctracker *changeTracker) { if ctracker != nil { iter.cTracker, iter.currentChange = ctracker, ctracker.getCurrent() } } func (iter *binTreeNodeIterator[E, V]) HasNext() bool { return iter.next != nil } func (iter *binTreeNodeIterator[E, V]) Next() *binTreeNode[E, V] { if !iter.HasNext() { return nil } cTracker := iter.cTracker if cTracker != nil && cTracker.changedSince(iter.currentChange) { panic("the tree has been modified since the iterator was created") } iter.current = iter.next iter.next = iter.toNext(iter.next) return iter.current } func (iter *binTreeNodeIterator[E, V]) toNext(current *binTreeNode[E, V]) *binTreeNode[E, V] { return iter.operator(current, iter.end) } func (iter *binTreeNodeIterator[E, V]) Remove() *binTreeNode[E, V] { if iter.current == nil { return nil } cTracker := iter.cTracker if cTracker != nil && cTracker.changedSince(iter.currentChange) { panic("the tree has been modified since the iterator was created") } result := iter.current result.Remove() iter.current = nil if cTracker != nil { iter.currentChange = cTracker.getCurrent() } return result } type CachingIterator interface { // GetCached returns an object previously cached with the current iterated node. // After Next has returned a node, // if an object was cached by a call to CacheWithLowerSubNode or CacheWithUpperSubNode // was called when that node's parent was previously returned by Next, // then this returns that cached object. GetCached() C // CacheWithLowerSubNode caches an object with the lower sub-node of the current iterated node. // After Next has returned a node, // calling this method caches the provided object with the lower sub-node so that it can // be retrieved with GetCached when the lower sub-node is visited later. // // Returns false if it could not be cached, either because the node has since been removed with a call to Remove, // because Next has not been called yet, or because there is no lower sub node for the node previously returned by Next. // // The caching and retrieval is done in constant time. CacheWithLowerSubNode(C) bool // CacheWithUpperSubNode caches an object with the upper sub-node of the current iterated node. // After Next has returned a node, // calling this method caches the provided object with the upper sub-node so that it can // be retrieved with GetCached when the upper sub-node is visited later. // // Returns false if it could not be cached, either because the node has since been removed with a call to Remove, // because Next has not been called yet, or because there is no upper sub node for the node previously returned by Next. // // The caching and retrieval is done in constant time. CacheWithUpperSubNode(C) bool } type cachingNodeIterator[E Key, V any] interface { nodeIteratorRem[E, V] CachingIterator } type queueType = any // see https://pkg.go.dev/container/heap type nodePriorityQueue struct { queue []queueType comparator func(one, two queueType) int // -1, 0 or 1 if one is <, == or > two } func (prioQueue nodePriorityQueue) Len() int { return len(prioQueue.queue) } func (prioQueue nodePriorityQueue) Less(i, j int) bool { queue := prioQueue.queue return prioQueue.comparator(queue[i], queue[j]) < 0 } func (prioQueue nodePriorityQueue) Swap(i, j int) { queue := prioQueue.queue queue[i], queue[j] = queue[j], queue[i] } func (prioQueue *nodePriorityQueue) Push(x queueType) { prioQueue.queue = append(prioQueue.queue, x) } func (prioQueue *nodePriorityQueue) Pop() queueType { current := prioQueue.queue queueLen := len(current) topNode := current[queueLen-1] current[queueLen-1] = nil prioQueue.queue = current[:queueLen-1] return topNode } func newPriorityNodeIterator[E Key, V any]( treeSize int, addedOnly bool, start *binTreeNode[E, V], comparator func(E, E) int, ) binTreeNodeIterator[E, V] { return newPriorityNodeIteratorBounded( nil, treeSize, addedOnly, start, comparator) } func newPriorityNodeIteratorBounded[E Key, V any]( bnds *bounds[E], treeSize int, addedOnly bool, start *binTreeNode[E, V], comparator func(E, E) int) binTreeNodeIterator[E, V] { comp := func(one, two queueType) int { node1, node2 := one.(*binTreeNode[E, V]), two.(*binTreeNode[E, V]) addr1, addr2 := node1.GetKey(), node2.GetKey() return comparator(addr1, addr2) } queue := &nodePriorityQueue{comparator: comp} if treeSize > 0 { queue.queue = make([]queueType, 0, (treeSize+2)>>1) } op := func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { lower := currentNode.getLowerSubNode() if lower != nil { heap.Push(queue, lower) } upper := currentNode.getUpperSubNode() if upper != nil { heap.Push(queue, upper) } var node *binTreeNode[E, V] if queue.Len() > 0 { node = heap.Pop(queue).(*binTreeNode[E, V]) } if node == endNode { return nil } return node } if addedOnly { wrappedOp := op op = func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { return currentNode.nextAdded(endNode, wrappedOp) } } if bnds != nil { wrappedOp := op op = func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { return currentNode.nextInBounds(endNode, wrappedOp, bnds) } } res := binTreeNodeIterator[E, V]{operator: op} start = res.getStart(start, nil, bnds, addedOnly) if start != nil { res.next = start res.setChangeTracker(start.cTracker) } return res } func newCachingPriorityNodeIterator[E Key, V any]( start *binTreeNode[E, V], comparator func(E, E) int, ) cachingPriorityNodeIterator[E, V] { return newCachingPriorityNodeIteratorSized( 0, start, comparator) } func newCachingPriorityNodeIteratorSized[E Key, V any]( treeSize int, start *binTreeNode[E, V], comparator func(E, E) int) cachingPriorityNodeIterator[E, V] { comp := func(one, two queueType) int { cached1, cached2 := one.(*cached[E, V]), two.(*cached[E, V]) node1, node2 := cached1.node, cached2.node addr1, addr2 := node1.GetKey(), node2.GetKey() return comparator(addr1, addr2) } queue := &nodePriorityQueue{comparator: comp} if treeSize > 0 { queue.queue = make([]queueType, 0, (treeSize+2)>>1) } res := cachingPriorityNodeIterator[E, V]{cached: &cachedObjs[E, V]{}} res.operator = res.getNextOperation(queue) start = res.getStart(start, nil, nil, false) if start != nil { res.next = start res.setChangeTracker(start.cTracker) } return res } type cachedObjs[E Key, V any] struct { cacheItem C nextCachedItem *cached[E, V] lowerCacheObj, upperCacheObj *cached[E, V] } type cachingPriorityNodeIterator[E Key, V any] struct { binTreeNodeIterator[E, V] cached *cachedObjs[E, V] } func (iter *cachingPriorityNodeIterator[E, V]) getNextOperation(queue *nodePriorityQueue) func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { return func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { lower := currentNode.getLowerSubNode() cacheObjs := iter.cached if lower != nil { cachd := &cached[E, V]{ node: lower, } cacheObjs.lowerCacheObj = cachd heap.Push(queue, cachd) } else { cacheObjs.lowerCacheObj = nil } upper := currentNode.getUpperSubNode() if upper != nil { cachd := &cached[E, V]{ node: upper, } cacheObjs.upperCacheObj = cachd heap.Push(queue, cachd) } else { cacheObjs.upperCacheObj = nil } if cacheObjs.nextCachedItem != nil { cacheObjs.cacheItem = cacheObjs.nextCachedItem.cached } var item queueType if queue.Len() > 0 { item = heap.Pop(queue) } if item != nil { cachd := item.(*cached[E, V]) node := cachd.node if node != endNode { cacheObjs.nextCachedItem = cachd return node } } cacheObjs.nextCachedItem = nil return nil } } func (iter *cachingPriorityNodeIterator[E, V]) GetCached() C { return iter.cached.cacheItem } func (iter *cachingPriorityNodeIterator[E, V]) CacheWithLowerSubNode(object C) bool { cached := iter.cached if cached.lowerCacheObj != nil { cached.lowerCacheObj.cached = object return true } return false } func (iter *cachingPriorityNodeIterator[E, V]) CacheWithUpperSubNode(object C) bool { cached := iter.cached if cached.upperCacheObj != nil { cached.upperCacheObj.cached = object return true } return false } type cached[E Key, V any] struct { node *binTreeNode[E, V] cached C } // The caching only useful when in reverse order, since you have to visit parent nodes first for it to be useful. func newPostOrderNodeIterator[E Key, V any]( forward, addedOnly bool, start, end *binTreeNode[E, V], ctracker *changeTracker, ) subNodeCachingIterator[E, V] { return newPostOrderNodeIteratorBounded( nil, forward, addedOnly, start, end, ctracker) } func newPostOrderNodeIteratorBounded[E Key, V any]( bnds *bounds[E], forward, addedOnly bool, start, end *binTreeNode[E, V], ctracker *changeTracker) subNodeCachingIterator[E, V] { var op func(current *binTreeNode[E, V], end *binTreeNode[E, V]) *binTreeNode[E, V] if forward { op = (*binTreeNode[E, V]).nextPostOrderNode } else { op = (*(binTreeNode[E, V])).previousPostOrderNode } // do the added-only filter first, because it is simpler if addedOnly { wrappedOp := op op = func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { return currentNode.nextAdded(endNode, wrappedOp) } } if bnds != nil { wrappedOp := op op = func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { return currentNode.nextInBounds(endNode, wrappedOp, bnds) } } return newSubNodeCachingIterator[E, V]( bnds, forward, addedOnly, start, end, ctracker, op, !forward, !forward || addedOnly) } // The caching only useful when in forward order, since you have to visit parent nodes first for it to be useful. func newPreOrderNodeIterator[E Key, V any]( forward, addedOnly bool, start, end *binTreeNode[E, V], ctracker *changeTracker) subNodeCachingIterator[E, V] { return newPreOrderNodeIteratorBounded( nil, forward, addedOnly, start, end, ctracker) } func newPreOrderNodeIteratorBounded[E Key, V any]( bnds *bounds[E], forward, addedOnly bool, start, end *binTreeNode[E, V], ctracker *changeTracker) subNodeCachingIterator[E, V] { var op func(current *binTreeNode[E, V], end *binTreeNode[E, V]) *binTreeNode[E, V] if forward { op = (*binTreeNode[E, V]).nextPreOrderNode } else { op = (*binTreeNode[E, V]).previousPreOrderNode } // do the added-only filter first, because it is simpler if addedOnly { wrappedOp := op op = func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { return currentNode.nextAdded(endNode, wrappedOp) } } if bnds != nil { wrappedOp := op op = func(currentNode *binTreeNode[E, V], endNode *binTreeNode[E, V]) *binTreeNode[E, V] { return currentNode.nextInBounds(endNode, wrappedOp, bnds) } } return newSubNodeCachingIterator( bnds, forward, addedOnly, start, end, ctracker, op, forward, forward || addedOnly) } func newSubNodeCachingIterator[E Key, V any]( bnds *bounds[E], forward, addedOnly bool, start, end *binTreeNode[E, V], ctracker *changeTracker, nextOperator func(current *binTreeNode[E, V], end *binTreeNode[E, V]) *binTreeNode[E, V], allowCaching, allowRemove bool, ) subNodeCachingIterator[E, V] { res := subNodeCachingIterator[E, V]{ allowCaching: allowCaching, allowRemove: allowRemove, stackIndex: -1, bnds: bnds, isForward: forward, addedOnly: addedOnly, binTreeNodeIterator: binTreeNodeIterator[E, V]{end: end}, } res.setChangeTracker(ctracker) res.operator = nextOperator res.next = res.getStart(start, end, bnds, addedOnly) return res } const ipv6BitCount = 128 const stackSize = ipv6BitCount + 2 // 129 for prefixes /0 to /128 and also 1 more for non-prefixed type subNodeCachingIterator[E Key, V any] struct { binTreeNodeIterator[E, V] cacheItem C nextKey E nextCached C stack []C stackIndex int bnds *bounds[E] addedOnly, isForward bool // Both these fields are not really necessary because // the caching and removal functionality should not be exposed when it is not usable. // The interfaces will not include the caching and Remove() methods in the cases where they are not usable. // So these fields are both runtime checks for coding errors. allowCaching, allowRemove bool } func (iter *subNodeCachingIterator[E, V]) Next() *binTreeNode[E, V] { result := iter.binTreeNodeIterator.Next() if result != nil && iter.allowCaching { iter.populateCacheItem(result) } return result } func (iter *subNodeCachingIterator[E, V]) GetCached() C { if !iter.allowCaching { panic("no caching allowed, this code path should not be accessible") } return iter.cacheItem } func (iter *subNodeCachingIterator[E, V]) populateCacheItem(current *binTreeNode[E, V]) { nextKey := iter.nextKey if current.GetKey() == nextKey { iter.cacheItem = iter.nextCached iter.nextCached = nil } else { stack := iter.stack if stack != nil { stackIndex := iter.stackIndex if stackIndex >= 0 && stack[stackIndex] == current.GetKey() { iter.cacheItem = stack[stackIndex+stackSize].(C) stack[stackIndex+stackSize] = nil stack[stackIndex] = nil iter.stackIndex-- } else { iter.cacheItem = nil } } else { iter.cacheItem = nil } } } func (iter *subNodeCachingIterator[E, V]) Remove() *binTreeNode[E, V] { if !iter.allowRemove { // Example: // Suppose we are at right sub-node, just visited left. Next node is parent, but not added. // When right is removed, so is the parent, so that the left takes its place. // But parent is our next node. Now our next node is invalid. So we are lost. // This is avoided for iterators that are "added" only. panic("no removal allowed, this code path should not be accessible") return nil } return iter.binTreeNodeIterator.Remove() } func (iter *subNodeCachingIterator[E, V]) checkCaching() { if !iter.allowCaching { panic("no caching allowed, this code path should not be accessible") } } func (iter *subNodeCachingIterator[E, V]) CacheWithLowerSubNode(object C) bool { iter.checkCaching() if iter.isForward { return iter.cacheWithFirstSubNode(object) } return iter.cacheWithSecondSubNode(object) } func (iter *subNodeCachingIterator[E, V]) CacheWithUpperSubNode(object C) bool { iter.checkCaching() if iter.isForward { return iter.cacheWithSecondSubNode(object) } return iter.cacheWithFirstSubNode(object) } // the sub-node will be the next visited node func (iter *subNodeCachingIterator[E, V]) cacheWithFirstSubNode(object C) bool { iter.checkCaching() if iter.current != nil { var firstNode *binTreeNode[E, V] if iter.isForward { firstNode = iter.current.getLowerSubNode() } else { firstNode = iter.current.getUpperSubNode() } if firstNode != nil { if (iter.addedOnly && !firstNode.IsAdded()) || (iter.bnds != nil && !iter.bnds.isInBounds(firstNode.GetKey())) { firstNode = iter.operator(firstNode, iter.current) } if firstNode != nil { // the lower sub-node is always next if it exists iter.nextKey = firstNode.GetKey() iter.nextCached = object return true } } } return false } // the sub-node will only be the next visited node if there is no other sub-node, // otherwise it might not be visited for a while func (iter *subNodeCachingIterator[E, V]) cacheWithSecondSubNode(object C) bool { iter.checkCaching() if iter.current != nil { var secondNode *binTreeNode[E, V] if iter.isForward { secondNode = iter.current.getUpperSubNode() } else { secondNode = iter.current.getLowerSubNode() } if secondNode != nil { if (iter.addedOnly && !secondNode.IsAdded()) || (iter.bnds != nil && !iter.bnds.isInBounds(secondNode.GetKey())) { secondNode = iter.operator(secondNode, iter.current) } if secondNode != nil { // if there is no lower node, we can use the nextCached field since upper is next when no lower sub-node var firstNode *binTreeNode[E, V] if iter.isForward { firstNode = iter.current.getLowerSubNode() } else { firstNode = iter.current.getUpperSubNode() } if firstNode == nil { iter.nextKey = secondNode.GetKey() iter.nextCached = object } else { if iter.stack == nil { iter.stack = make([]C, stackSize<<1) } iter.stackIndex++ iter.stack[iter.stackIndex] = secondNode.GetKey() iter.stack[iter.stackIndex+stackSize] = object } return true } } } return false } bintree-1.2.1/tree/bintreenode.go000066400000000000000000001002231434023710600167250ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tree import ( "fmt" "math/big" "reflect" "strconv" "strings" "unsafe" ) var freezeRoot = true func bigOne() *big.Int { return big.NewInt(1) } var one = bigOne() type change struct { big *big.Int small uint64 } func (c change) Equal(c2 change) bool { if c.small == c2.small { if c.big == nil { return c2.big == nil } else if c2.big != nil { return c.big.Cmp(c2.big) == 0 } } return false } func (c *change) increment() { val := c.small val++ if val == 0 { if c.big == nil { c.big = bigOne() } else { c.big.Add(c.big, one) } } c.small = val } func (c change) String() string { return c.big.String() + " " + strconv.FormatUint(c.small, 10) } type changeTracker struct { currentChange change watched bool } func (c *changeTracker) changed() { if c.watched { c.watched = false c.currentChange.increment() } // else nobody is watching the current change, so no need to do anything } func (c *changeTracker) changedSince(otherChange change) bool { return !c.currentChange.Equal(otherChange) } func (c *changeTracker) getCurrent() change { c.watched = true return c.currentChange } func (c *changeTracker) String() string { return "current change: " + c.currentChange.String() } type bounds[E Key] struct { } func (b *bounds[E]) isInBounds(item E) bool { return true } func (b *bounds[E]) isWithinLowerBound(item E) bool { return true } func (b *bounds[E]) isBelowLowerBound(item E) bool { return true } func (b *bounds[E]) isWithinUpperBound(item E) bool { return true } func (b *bounds[E]) isAboveUpperBound(item E) bool { return true } type Key interface { comparable // needed by populateCacheItem } // C represents cached values in iterators type C any const sizeUnknown = -1 type binTreeNode[E Key, V any] struct { // the key for the node item E // only for associative trie nodes value V parent, lower, upper *binTreeNode[E, V] storedSize int cTracker *changeTracker // some nodes represent elements added to the tree and others are nodes generated internally when other nodes are added added bool self *binTreeNode[E, V] } // This hideptr trick is used in strings.Builder to trick escape analysis to ensure that this self-referential pointer does not cause automatic heap allocation // cannot hurt to use it // https://github.com/golang/go/issues/23382 // https://github.com/golang/go/issues/7921 // https://cs.opensource.google/go/go/+/refs/tags/go1.17.6:src/strings/builder.go;l=28 //go:nosplit //go:nocheckptr func hideptr(p unsafe.Pointer) unsafe.Pointer { ptr := uintptr(p) return unsafe.Pointer(ptr ^ 0) } func (node *binTreeNode[E, V]) setAddr() { node.self = (*binTreeNode[E, V])(hideptr(unsafe.Pointer(node))) } func (node *binTreeNode[E, V]) checkCopy() { if node != nil && node.self != nil && node.self != node { panic("attempting to modify trie with a copied node") } } func (node *binTreeNode[E, V]) getChangeTracker() *changeTracker { if node == nil { return nil } return node.cTracker } func toTrieNode[E TrieKey[E], V any](node *binTreeNode[E, V]) *BinTrieNode[E, V] { return (*BinTrieNode[E, V])(unsafe.Pointer(node)) } // when FREEZE_ROOT is true, this is never called (and FREEZE_ROOT is always true) func (node *binTreeNode[E, V]) setKey(item E) { node.item = item } // Gets the key used for placing the node in the tree. func (node *binTreeNode[E, V]) GetKey() (key E) { if node != nil { key = node.item } return } // SetValue assigns a value to the node, overwriting any previous value func (node *binTreeNode[E, V]) SetValue(val V) { // new value assignment node.value = val } // GetValue returns the value assigned to the node func (node *binTreeNode[E, V]) GetValue() (val V) { if node != nil { val = node.value } return } func (node *binTreeNode[E, V]) ClearValue() { var v V // new value assignment node.value = v //if node != nil { // node.value = nil //} } // Returns whether this is the root of the backing tree. func (node *binTreeNode[E, V]) IsRoot() bool { return node != nil && node.parent == nil } // Gets the node from which this node is a direct child node, or nil if this is the root. func (node *binTreeNode[E, V]) getParent() (parent *binTreeNode[E, V]) { if node != nil { parent = node.parent } return } func (node *binTreeNode[E, V]) setParent(parent *binTreeNode[E, V]) { node.parent = parent } // Gets the direct child node whose key is largest in value func (node *binTreeNode[E, V]) getUpperSubNode() (upper *binTreeNode[E, V]) { if node != nil { upper = node.upper } return } // Gets the direct child node whose key is smallest in value func (node *binTreeNode[E, V]) getLowerSubNode() (lower *binTreeNode[E, V]) { if node != nil { lower = node.lower } return } func (node *binTreeNode[E, V]) setUpper(upper *binTreeNode[E, V]) { node.upper = upper if upper != nil { upper.setParent(node) } } func (node *binTreeNode[E, V]) setLower(lower *binTreeNode[E, V]) { node.lower = lower if lower != nil { lower.setParent(node) } } // IsAdded returns whether the node was "added". // Some binary tree nodes are considered "added" and others are not. // Those nodes created for key elements added to the tree are "added" nodes. // Those that are not added are those nodes created to serve as junctions for the added nodes. // Only added elements contribute to the size of a tree. // When removing nodes, non-added nodes are removed automatically whenever they are no longer needed, // which is when an added node has less than two added sub-nodes. func (node *binTreeNode[E, V]) IsAdded() bool { return node != nil && node.added } // SetAdded makes this node an added node, which is equivalent to adding the corresponding key to the tree. // If the node is already an added node, this method has no effect. // You cannot set an added node to non-added, for that you should Remove the node from the tree by calling Remove. // A non-added node will only remain in the tree if it needs to in the tree. func (node *binTreeNode[E, V]) SetAdded() { if !node.added { node.setNodeAdded(true) node.adjustCount(1) } } func (node *binTreeNode[E, V]) setNodeAdded(added bool) { node.added = added } // Size returns the count of nodes added to the sub-tree starting from this node as root and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node and adjusted with each add and Remove operation in the sub-tree. func (node *binTreeNode[E, V]) Size() (storedSize int) { if node != nil { storedSize = node.storedSize if storedSize == sizeUnknown { iterator := node.containedFirstAllNodeIterator(true) for next := iterator.Next(); next != nil; next = iterator.Next() { var nodeSize int if next.IsAdded() { nodeSize = 1 } lower := next.getLowerSubNode() if lower != nil { nodeSize += lower.storedSize } upper := next.getUpperSubNode() if upper != nil { nodeSize += upper.storedSize } next.storedSize = nodeSize } storedSize = node.storedSize } } return } // NodeSize returns the count of all nodes in the tree starting from this node and extending to all sub-nodes. // Unlike for the Size method, this is not a constant-time operation and must visit all sub-nodes of this node. func (node *binTreeNode[E, V]) NodeSize() int { totalCount := 0 iterator := node.allNodeIterator(false) next := iterator.Next() for next != nil { totalCount++ next = iterator.Next() } return totalCount } func (node *binTreeNode[E, V]) adjustCount(delta int) { if delta != 0 { thisNode := node for { thisNode.storedSize += delta thisNode = thisNode.getParent() if thisNode == nil { break } } } } // Remove removes this node from the collection of added nodes, // and also removes from the tree if possible. // If it has two sub-nodes, it cannot be removed from the tree, in which case it is marked as not "added", // nor is it counted in the tree size. // Only added nodes can be removed from the tree. If this node is not added, this method does nothing. func (node *binTreeNode[E, V]) Remove() { node.checkCopy() if !node.IsAdded() { return } else if freezeRoot && node.IsRoot() { node.removed() } else if node.getUpperSubNode() == nil { node.replaceThis(node.getLowerSubNode()) // also handles case of lower == nil } else if node.getLowerSubNode() == nil { node.replaceThis(node.getUpperSubNode()) } else { // has two sub-nodes node.removed() } } func (node *binTreeNode[E, V]) removed() { node.adjustCount(-1) node.setNodeAdded(false) node.cTracker.changed() node.ClearValue() } // Makes the parent of this point to something else, thus removing this and all sub-nodes from the tree func (node *binTreeNode[E, V]) replaceThis(replacement *binTreeNode[E, V]) { node.replaceThisRecursive(replacement, 0) node.cTracker.changed() } func (node *binTreeNode[E, V]) replaceThisRecursive(replacement *binTreeNode[E, V], additionalSizeAdjustment int) { if node.IsRoot() { node.replaceThisRoot(replacement) return } parent := node.getParent() if parent.getUpperSubNode() == node { // we adjust parents first, using the size and other characteristics of ourselves, // before the parent severs the link to ourselves with the call to setUpper, // since the setUpper call is allowed to change the characteristics of the child, // and in some cases this does adjust the size of the child. node.adjustTree(parent, replacement, additionalSizeAdjustment, true) parent.setUpper(replacement) } else if parent.getLowerSubNode() == node { node.adjustTree(parent, replacement, additionalSizeAdjustment, false) parent.setLower(replacement) } else { panic("corrupted trie") // will never reach here } } func (node *binTreeNode[E, V]) adjustTree(parent, replacement *binTreeNode[E, V], additionalSizeAdjustment int, replacedUpper bool) { sizeAdjustment := -node.storedSize if replacement == nil { if !parent.IsAdded() && (!freezeRoot || !parent.IsRoot()) { parent.storedSize += sizeAdjustment var parentReplacement *binTreeNode[E, V] if replacedUpper { parentReplacement = parent.getLowerSubNode() } else { parentReplacement = parent.getUpperSubNode() } parent.replaceThisRecursive(parentReplacement, sizeAdjustment) } else { parent.adjustCount(sizeAdjustment + additionalSizeAdjustment) } } else { parent.adjustCount(replacement.storedSize + sizeAdjustment + additionalSizeAdjustment) } node.setParent(nil) } func (node *binTreeNode[E, V]) replaceThisRoot(replacement *binTreeNode[E, V]) { if replacement == nil { node.setNodeAdded(false) node.setUpper(nil) node.setLower(nil) if !freezeRoot { var e E node.setKey(e) //node.setKey(nil) // here we'd need to replace with the default root (ie call setKey with key of 0.0.0.0/0 or ::/0 or 0:0:0:0:0:0) } node.storedSize = 0 node.ClearValue() } else { // We never go here when FREEZE_ROOT is true node.setNodeAdded(replacement.IsAdded()) node.setUpper(replacement.getUpperSubNode()) node.setLower(replacement.getLowerSubNode()) node.setKey(replacement.GetKey()) node.storedSize = replacement.storedSize node.SetValue(replacement.GetValue()) } } // Clear removes this node and all sub-nodes from the sub-tree with this node as the root, after which isEmpty() will return true. func (node *binTreeNode[E, V]) Clear() { node.checkCopy() if node != nil { node.replaceThis(nil) } } // IsEmpty returns where there are not any elements in the sub-tree with this node as the root. func (node *binTreeNode[E, V]) IsEmpty() bool { return !node.IsAdded() && node.getUpperSubNode() == nil && node.getLowerSubNode() == nil } // IsLeaf returns whether this node is in the tree (a node for which IsAdded() is true) // and there are no elements in the sub-tree with this node as the root. func (node *binTreeNode[E, V]) IsLeaf() bool { return node.IsAdded() && node.getUpperSubNode() == nil && node.getLowerSubNode() == nil } // Returns the first (lowest valued) node in the sub-tree originating from this node. func (node *binTreeNode[E, V]) firstNode() *binTreeNode[E, V] { first := node for { lower := first.getLowerSubNode() if lower == nil { return first } first = lower } } // Returns the first (lowest valued) added node in the sub-tree originating from this node, // or nil if there are no added entries in this tree or sub-tree func (node *binTreeNode[E, V]) firstAddedNode() *binTreeNode[E, V] { first := node.firstNode() if first.IsAdded() { return first } return first.nextAddedNode() } // Returns the last (highest valued) node in the sub-tree originating from this node. func (node *binTreeNode[E, V]) lastNode() *binTreeNode[E, V] { last := node for { upper := last.getUpperSubNode() if upper == nil { return last } last = upper } } // Returns the last (highest valued) added node in the sub-tree originating from this node, // or nil if there are no added entries in this tree or sub-tree func (node *binTreeNode[E, V]) lastAddedNode() *binTreeNode[E, V] { last := node.lastNode() if last.IsAdded() { return last } return last.previousAddedNode() } func (node *binTreeNode[E, V]) firstPostOrderNode() *binTreeNode[E, V] { next := node var nextNext *binTreeNode[E, V] for { nextNext = next.getLowerSubNode() if nextNext == nil { nextNext = next.getUpperSubNode() if nextNext == nil { return next } } next = nextNext } } func (node *binTreeNode[E, V]) lastPreOrderNode() *binTreeNode[E, V] { next := node var nextNext *binTreeNode[E, V] for { nextNext = next.getUpperSubNode() if nextNext == nil { nextNext = next.getLowerSubNode() if nextNext == nil { return next } } next = nextNext } } // Returns the node that follows this node following the tree order func (node *binTreeNode[E, V]) nextNode() *binTreeNode[E, V] { return node.nextNodeBounded(nil) } // in-order // // 8x // 4x 12x // 2x 6x 10x 14x // // 1x 3x 5x 7x 9x 11x 13x 15x func (node *binTreeNode[E, V]) nextNodeBounded(bound *binTreeNode[E, V]) *binTreeNode[E, V] { next := node.getUpperSubNode() if next != nil { for { nextLower := next.getLowerSubNode() if nextLower == nil { return next } next = nextLower } } else { next = node.getParent() if next == bound { return nil } current := node for next != nil && current == next.getUpperSubNode() { current = next next = next.getParent() if next == bound { return nil } } } return next } // Returns the node that precedes this node following the tree order. func (node *binTreeNode[E, V]) previousNode() *binTreeNode[E, V] { return node.previousNodeBounded(nil) } // reverse order // // 8x // 12x 4x // 14x 10x 6x 2x // // 15x 13x 11x 9x 7x 5x 3x 1x func (node *binTreeNode[E, V]) previousNodeBounded(bound *binTreeNode[E, V]) *binTreeNode[E, V] { previous := node.getLowerSubNode() if previous != nil { for { previousUpper := previous.getUpperSubNode() if previousUpper == nil { break } previous = previousUpper } } else { previous = node.getParent() if previous == bound { return nil } current := node for previous != nil && current == previous.getLowerSubNode() { current = previous previous = previous.getParent() if previous == bound { return nil } } } return previous } // pre order // 1x // 2x 9x // // 3x 6x 10x 13x // 4x 5x 7x 8x 11x 12x 14x 15x // this one starts from root, ends at last node, all the way right func (node *binTreeNode[E, V]) nextPreOrderNode(end *binTreeNode[E, V]) *binTreeNode[E, V] { next := node.getLowerSubNode() if next == nil { // cannot go left/lower next = node.getUpperSubNode() if next == nil { // cannot go right/upper current := node next = node.getParent() // so instead, keep going up until we can go right for next != nil { if next == end { return nil } if current == next.getLowerSubNode() { // parent is higher nextNext := next.getUpperSubNode() if nextNext != nil { return nextNext } } current = next next = next.getParent() } } } return next } // reverse post order // 1x // 9x 2x // 13x 10x 6x 3x // // 15x 14x 12x 11x 8x 7x 5x 4x // this one starts from root, ends at first node, all the way left // this is the mirror image of nextPreOrderNode, so no comments func (node *binTreeNode[E, V]) previousPostOrderNode(end *binTreeNode[E, V]) *binTreeNode[E, V] { next := node.getUpperSubNode() if next == nil { next = node.getLowerSubNode() if next == nil { current := node next = node.getParent() for next != nil { if next == end { return nil } if current == next.getUpperSubNode() { nextNext := next.getLowerSubNode() if nextNext != nil { next = nextNext break } } current = next next = next.getParent() } } } return next } // reverse pre order // // 15x // 14x 7x // 13x 10x 6x 3x //12x 11x 9x 8x 5x 4x 2x 1x // this one starts from last node, all the way right, ends at root // this is the mirror image of nextPostOrderNode, so no comments func (node *binTreeNode[E, V]) previousPreOrderNode(end *binTreeNode[E, V]) *binTreeNode[E, V] { next := node.getParent() if next == nil || next == end { return nil } if next.getLowerSubNode() == node { return next } nextNext := next.getLowerSubNode() if nextNext == nil { return next } next = nextNext for { nextNext = next.getUpperSubNode() if nextNext == nil { nextNext = next.getLowerSubNode() if nextNext == nil { return next } } next = nextNext } } // post order // 15x // 7x 14x // 3x 6x 10x 13x // // 1x 2x 4x 5x 8x 9x 11x 12x // this one starts from first node, all the way left, ends at root func (node *binTreeNode[E, V]) nextPostOrderNode(end *binTreeNode[E, V]) *binTreeNode[E, V] { next := node.getParent() if next == nil || next == end { return nil } if next.getUpperSubNode() == node { // we are the upper sub-node, so parent is next return next } // we are the lower sub-node nextNext := next.getUpperSubNode() if nextNext == nil { // parent has no upper sub-node, so parent is next return next } // go to parent's upper sub-node next = nextNext // now go all the way down until we can go no further, favoring left/lower turns over right/upper for { nextNext = next.getLowerSubNode() if nextNext == nil { nextNext = next.getUpperSubNode() if nextNext == nil { return next } } next = nextNext } } // Returns the next node in the tree that is an added node, following the tree order, // or nil if there is no such node. func (node *binTreeNode[E, V]) nextAddedNode() *binTreeNode[E, V] { return node.nextAdded(nil, (*binTreeNode[E, V]).nextNodeBounded) } // Returns the previous node in the tree that is an added node, following the tree order in reverse, // or nil if there is no such node. func (node *binTreeNode[E, V]) previousAddedNode() *binTreeNode[E, V] { return node.nextAdded(nil, (*binTreeNode[E, V]).previousNodeBounded) } // The generic method pointers are fine. The parser errors are just a Goland problem. Try it out in playground: https://go.dev/play/p/lf8zJtGCKYI func nextTest[E Key, V any](current, end *binTreeNode[E, V], nextOperator func(current *binTreeNode[E, V], end *binTreeNode[E, V]) *binTreeNode[E, V], tester func(current *binTreeNode[E, V]) bool) *binTreeNode[E, V] { for { current = nextOperator(current, end) if current == end || current == nil { return nil } if tester(current) { break } } return current } func (node *binTreeNode[E, V]) nextAdded(end *binTreeNode[E, V], nextOperator func(current *binTreeNode[E, V], end *binTreeNode[E, V]) *binTreeNode[E, V]) *binTreeNode[E, V] { return nextTest(node, end, nextOperator, (*binTreeNode[E, V]).IsAdded) } func (node *binTreeNode[E, V]) nextInBounds(end *binTreeNode[E, V], nextOperator func(current *binTreeNode[E, V], end *binTreeNode[E, V]) *binTreeNode[E, V], bnds *bounds[E]) *binTreeNode[E, V] { return nextTest(node, end, nextOperator, func(current *binTreeNode[E, V]) bool { return bnds.isInBounds(current.GetKey()) }) } // Returns an iterator that iterates through the elements of the sub-tree with this node as the root. // The iteration is in sorted element order. func (node *binTreeNode[E, V]) iterator() keyIterator[E] { return binTreeKeyIterator[E, V]{node.nodeIterator(true)} } // Returns an iterator that iterates through the elements of the subtrie with this node as the root. // The iteration is in reverse sorted element order. func (node *binTreeNode[E, V]) descendingIterator() keyIterator[E] { return binTreeKeyIterator[E, V]{node.nodeIterator(false)} } // Iterates through the added nodes of the sub-tree with this node as the root, in forward or reverse tree order. func (node *binTreeNode[E, V]) nodeIterator(forward bool) nodeIteratorRem[E, V] { return node.configuredNodeIterator(forward, true) } // Iterates through all the nodes of the sub-tree with this node as the root, in forward or reverse tree order. func (node *binTreeNode[E, V]) allNodeIterator(forward bool) nodeIteratorRem[E, V] { return node.configuredNodeIterator(forward, false) } func (node *binTreeNode[E, V]) containingFirstIterator(forwardSubNodeOrder bool) cachingNodeIterator[E, V] { return node.containingFirstNodeIterator(forwardSubNodeOrder, true) } func (node *binTreeNode[E, V]) containingFirstAllNodeIterator(forwardSubNodeOrder bool) cachingNodeIterator[E, V] { return node.containingFirstNodeIterator(forwardSubNodeOrder, false) } func (node *binTreeNode[E, V]) containingFirstNodeIterator(forwardSubNodeOrder, addedNodesOnly bool) cachingNodeIterator[E, V] { var iter subNodeCachingIterator[E, V] if forwardSubNodeOrder { iter = newPreOrderNodeIterator[E, V]( // remove is allowed true, // forward addedNodesOnly, // added only node, node.getParent(), node.getChangeTracker()) } else { iter = newPostOrderNodeIterator[E, V]( // remove is allowed false, // forward addedNodesOnly, // added only node, node.getParent(), node.getChangeTracker()) } return &iter } func (node *binTreeNode[E, V]) containedFirstIterator(forwardSubNodeOrder bool) nodeIteratorRem[E, V] { return node.containedFirstNodeIterator(forwardSubNodeOrder, true) } func (node *binTreeNode[E, V]) containedFirstAllNodeIterator(forwardSubNodeOrder bool) nodeIterator[E, V] { return node.containedFirstNodeIterator(forwardSubNodeOrder, false) } func (node *binTreeNode[E, V]) containedFirstNodeIterator(forwardSubNodeOrder, addedNodesOnly bool) nodeIteratorRem[E, V] { var iter subNodeCachingIterator[E, V] if forwardSubNodeOrder { iter = newPostOrderNodeIterator[E, V]( // Remove is allowed if and only if added only true, addedNodesOnly, // added only node.firstPostOrderNode(), node.getParent(), node.getChangeTracker()) } else { iter = newPreOrderNodeIterator[E, V]( // Remove is allowed if and only if added only false, addedNodesOnly, // added only node.lastPreOrderNode(), node.getParent(), node.getChangeTracker()) } return &iter } func (node *binTreeNode[E, V]) configuredNodeIterator(forward, addedOnly bool) nodeIteratorRem[E, V] { var startNode *binTreeNode[E, V] if forward { startNode = node.firstNode() } else { startNode = node.lastNode() } return newNodeIterator[E, V]( forward, addedOnly, startNode, node.getParent(), node.getChangeTracker()) } // https://jrgraphix.net/r/Unicode/2500-257F // https://jrgraphix.net/r/Unicode/25A0-25FF const ( nonAddedNodeCircle = "\u25cb" addedNodeCircle = "\u25cf" leftElbow = "\u251C\u2500" // |- inBetweenElbows = "\u2502 " // | rightElbow = "\u2514\u2500" // -- belowElbows = " " ) type nodePrinter[E Key, V any] interface { GetKey() E GetValue() V IsAdded() bool } func isNil[V any](v V) bool { valueType := reflect.ValueOf(&v).Elem() switch valueType.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice: return valueType.IsNil() } return false } // NodeString returns a visual representation of the given node including the key, with an open circle indicating this node is not an added node, // a closed circle indicating this node is an added node. func NodeString[E Key, V any](node nodePrinter[E, V]) string { if node == nil { return nilString() } key := node.GetKey() val := node.GetValue() if _, ok := any(val).(EmptyValueType); ok || isNil(val) { if node.IsAdded() { return fmt.Sprint(addedNodeCircle, " ", key) } return fmt.Sprint(nonAddedNodeCircle, " ", key) } if node.IsAdded() { return fmt.Sprint(addedNodeCircle, " ", key, " = ", val) } return fmt.Sprint(nonAddedNodeCircle, " ", key, " = ", val) } type indents struct { nodeIndent, subNodeInd string } // TreeString returns a visual representation of the sub-tree with this node as root, with one node per line. // // withNonAddedKeys: whether to show nodes that are not added nodes // withSizes: whether to include the counts of added nodes in each sub-tree func (node *binTreeNode[E, V]) TreeString(withNonAddedKeys, withSizes bool) string { builder := strings.Builder{} builder.WriteByte('\n') node.printTree(&builder, indents{}, withNonAddedKeys, withSizes) return builder.String() } func (node *binTreeNode[E, V]) printTree(builder *strings.Builder, initialIndents indents, withNonAdded, withSizes bool) { if node == nil { builder.WriteString(initialIndents.nodeIndent) builder.WriteString(nilString()) builder.WriteByte('\n') return } iterator := node.containingFirstAllNodeIterator(true) next := iterator.Next() for next != nil { cached := iterator.GetCached() var nodeIndent, subNodeIndent string if cached == nil { nodeIndent = initialIndents.nodeIndent subNodeIndent = initialIndents.subNodeInd } else { cachedi := cached.(indents) nodeIndent = cachedi.nodeIndent subNodeIndent = cachedi.subNodeInd } if withNonAdded || next.IsAdded() { builder.WriteString(nodeIndent) builder.WriteString(next.String()) if withSizes { builder.WriteString(" (") builder.WriteString(strconv.Itoa(next.Size())) builder.WriteByte(')') } builder.WriteByte('\n') } else { builder.WriteString(nodeIndent) builder.WriteString(nonAddedNodeCircle) builder.WriteByte('\n') } upper, lower := next.getUpperSubNode(), next.getLowerSubNode() if upper != nil { if lower != nil { lowerIndents := indents{ nodeIndent: subNodeIndent + leftElbow, subNodeInd: subNodeIndent + inBetweenElbows, } iterator.CacheWithLowerSubNode(lowerIndents) } upperIndents := indents{ nodeIndent: subNodeIndent + rightElbow, subNodeInd: subNodeIndent + belowElbows, } iterator.CacheWithUpperSubNode(upperIndents) } else if lower != nil { lowerIndents := indents{ nodeIndent: subNodeIndent + rightElbow, subNodeInd: subNodeIndent + belowElbows, } iterator.CacheWithLowerSubNode(lowerIndents) } next = iterator.Next() } } func nilString() string { return "" } // Returns a visual representation of this node including the key, with an open circle indicating this node is not an added node, // a closed circle indicating this node is an added node. func (node *binTreeNode[E, V]) String() string { if node == nil { return NodeString[E, V](nil) } return NodeString[E, V](node) } func (node binTreeNode[E, V]) format(state fmt.State, verb rune) { switch verb { case 's', 'v': _, _ = state.Write([]byte(node.String())) return } s := flagsFromState(state, verb) _, _ = state.Write([]byte(fmt.Sprintf(s, binTreeNodePtr[E, V](node.self)))) } // only used to eliminate the method set of *binTreeNode type binTreeNodePtr[E Key, V any] *binTreeNode[E, V] func flagsFromState(state fmt.State, verb rune) string { flags := "# +-0" vals := make([]rune, 0, len(flags)+5) // %, flags, width, '.', precision, verb vals = append(vals, '%') for i := 0; i < len(flags); i++ { b := flags[i] if state.Flag(int(b)) { vals = append(vals, rune(b)) } } width, widthOK := state.Width() precision, precisionOK := state.Precision() if widthOK || precisionOK { var wpv string if widthOK && precisionOK { wpv = fmt.Sprintf("%d.%d%c", width, precision, verb) } else if widthOK { wpv = fmt.Sprintf("%d%c", width, verb) } else { wpv = fmt.Sprintf(".%d%c", precision, verb) } return string(vals) + wpv } vals = append(vals, verb) return string(vals) } // Clones the node. // Keys remain the same, but the parent node and the lower and upper sub-nodes are all set to nil. func (node *binTreeNode[E, V]) clone() *binTreeNode[E, V] { if node == nil { return nil } result := *node // maintains same key and value which are not copied result.setParent(nil) result.setLower(nil) result.setUpper(nil) if node.IsAdded() { result.storedSize = 1 } else { result.storedSize = 0 } // it is ok to have no change tracker, because the parent, lower and upper are nil // so any attempt to remove or clear will do nothing, so no calls to the change tracker result.cTracker = nil result.setAddr() return &result } func (node *binTreeNode[E, V]) cloneTreeNode(cTracker *changeTracker) *binTreeNode[E, V] { if node == nil { return nil } result := *node // maintains same key and value which are not copied result.setParent(nil) result.cTracker = cTracker result.setAddr() return &result } func (node *binTreeNode[E, V]) cloneTreeTrackerBounds(ctracker *changeTracker, bnds *bounds[E]) *binTreeNode[E, V] { if node == nil { return nil } rootClone := node.cloneTreeNode(ctracker) clonedNode := rootClone iterator := clonedNode.containingFirstAllNodeIterator(true).(*subNodeCachingIterator[E, V]) recalculateSize := false for { lower := clonedNode.getLowerSubNode() if bnds != nil { for { if lower == nil { break } else if bnds.isWithinLowerBound(lower.GetKey()) { if !lower.IsAdded() { next := lower.getLowerSubNode() for bnds.isBelowLowerBound(next.GetKey()) { next = next.getUpperSubNode() if next == nil { lower = lower.getUpperSubNode() recalculateSize = true break } } } break } recalculateSize = true // outside bounds, try again lower = lower.getUpperSubNode() } } if lower != nil { clonedNode.setLower(lower.cloneTreeNode(ctracker)) } else { clonedNode.setLower(nil) } upper := clonedNode.getUpperSubNode() if bnds != nil { for { if upper == nil { break } else if bnds.isWithinUpperBound(upper.GetKey()) { if !upper.IsAdded() { next := upper.getUpperSubNode() for bnds.isAboveUpperBound(next.GetKey()) { next = next.getLowerSubNode() if next == nil { upper = upper.getLowerSubNode() recalculateSize = true break } } } break } recalculateSize = true // outside bounds, try again upper = upper.getLowerSubNode() } } if upper != nil { clonedNode.setUpper(upper.cloneTreeNode(ctracker)) } else { clonedNode.setUpper(nil) } iterator.Next() // returns current clonedNode clonedNode = iterator.next if !iterator.HasNext() { /* basically this checks clonedNode != nil */ break } } if !rootClone.IsAdded() && !node.IsRoot() { lower := rootClone.getLowerSubNode() if lower == nil { rootClone = rootClone.getUpperSubNode() } else if rootClone.getUpperSubNode() == nil { rootClone = lower } } if recalculateSize && rootClone != nil { rootClone.storedSize = sizeUnknown rootClone.Size() } return rootClone } func (node *binTreeNode[E, V]) cloneTreeBounds(bnds *bounds[E]) *binTreeNode[E, V] { return node.cloneTreeTrackerBounds(&changeTracker{}, bnds) } // Clones the sub-tree starting with this node as root. // The nodes are cloned, but their keys and values are not cloned. func (node *binTreeNode[E, V]) cloneTree() *binTreeNode[E, V] { return node.cloneTreeBounds(nil) } bintree-1.2.1/tree/bits.go000066400000000000000000000032771434023710600154030ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tree // A PrefixLen indicates the length of the prefix for an address, section, division grouping, segment, or division. // The zero value, which is nil, indicates that there is no prefix length. type PrefixLen = *PrefixBitCount // Len returns the length of the prefix. If the receiver is nil, representing the absence of a prefix length, returns 0. // It will also return 0 if the receiver is a prefix with length of 0. To distinguish the two, compare the receiver with nil. func (p *PrefixBitCount) Len() BitCount { if p == nil { return 0 } return p.bitCount() } func (p *PrefixBitCount) bitCount() BitCount { return BitCount(*p) } // A PrefixBitCount is the count of bits in a non-nil PrefixLen. type PrefixBitCount uint8 // A BitCount represents a count of bits in an address, section, grouping, segment, or division. // Using signed integers allows for easier arithmetic, avoiding bugs. // However, all methods adjust bit counts to match address size, // so negative bit counts or bit counts larger than address size are meaningless. type BitCount = int // using signed integers allows for easier arithmetic bintree-1.2.1/tree/path.go000066400000000000000000000143661434023710600153770ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tree import ( "strconv" "strings" ) // Path is a list of nodes derived from following a path in a tree. // Each node in the list corresponds to a node in the tree. // Each node in the list corresponds to a tree node that is a direct or indirect sub-node of the tree node corresponding to the previous node in the list. // Not all nodes in the pathway through the tree need to be included in the linked list. // // In other words, a path follows a pathway through a tree from root to leaf, but not necessarily including all nodes encountered along the way. type Path[E Key, V any] struct { root, leaf *PathNode[E, V] } // GetRoot returns the beginning of the Path, which may or may not match the tree root of the originating tree. func (path *Path[E, V]) GetRoot() *PathNode[E, V] { return path.root } // GetLeaf returns the end of the Path, which may or may not match a leaf in the originating tree. func (path *Path[E, V]) GetLeaf() *PathNode[E, V] { return path.leaf } // String returns a visual representation of the Path with one node per line. func (path *Path[E, V]) String() string { return path.ListString(true) } // ListString returns a visual representation of the tree with one node per line, with or without the non-added keys. func (path *Path[E, V]) ListString(withNonAddedKeys bool) string { if path.Size() == 0 { builder := strings.Builder{} builder.WriteByte('\n') if path == nil || !withNonAddedKeys { builder.WriteString(nilString()) } else { builder.WriteString(nonAddedNodeCircle) } builder.WriteByte('\n') return builder.String() } return path.GetRoot().ListString(withNonAddedKeys, true) } // Size returns the count of nodes in the list // This is a constant-time operation since the size is maintained in each node. func (path *Path[E, V]) Size() (storedSize int) { if path == nil || path.root == nil { return 0 } return path.root.Size() } // PathNode is an element in the list of a Path type PathNode[E Key, V any] struct { previous, next *PathNode[E, V] // the key for the node item E // only for associative trie nodes value V // the number of added nodes below this one, including this one if added storedSize int added bool } // Next returns the next node in the path func (node *PathNode[E, V]) Next() *PathNode[E, V] { return node.next } // Previous returns the previous node in the path func (node *PathNode[E, V]) Previous() *PathNode[E, V] { return node.previous } // GetKey gets the key used for placing the node in the tree. func (node *PathNode[E, V]) GetKey() (key E) { //return node.getKey() if node != nil { return node.item } return } // GetValue returns the value assigned to the node func (node *PathNode[E, V]) GetValue() (val V) { if node != nil { val = node.value } return } // IsAdded returns whether the node was "added". // Some binary tree nodes are considered "added" and others are not. // Those nodes created for key elements added to the tree are "added" nodes. // Those that are not added are those nodes created to serve as junctions for the added nodes. // Only added elements contribute to the size of a tree. // When removing nodes, non-added nodes are removed automatically whenever they are no longer needed, // which is when an added node has less than two added sub-nodes. func (node *PathNode[E, V]) IsAdded() bool { return node != nil && node.added } // Size returns the count of nodes added to the sub-tree starting from this node as root and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node. func (node *PathNode[E, V]) Size() (storedSize int) { if node != nil { storedSize = node.storedSize if storedSize == sizeUnknown { prev, next := node, node.next for ; next != nil && next.storedSize == sizeUnknown; prev, next = next, next.next { } var nodeSize int for { if prev.IsAdded() { nodeSize++ } if next != nil { nodeSize += next.storedSize } prev.storedSize = nodeSize if prev == node { break } prev = node.previous } storedSize = node.storedSize } } return } // Returns a visual representation of this node including the key, with an open circle indicating this node is not an added node, // a closed circle indicating this node is an added node. func (node *PathNode[E, V]) String() string { if node == nil { return NodeString[E, V](nil) } return NodeString[E, V](node) } // ListString returns a visual representation of the sub-list with this node as root, with one node per line. // // withNonAddedKeys: whether to show nodes that are not added nodes // withSizes: whether to include the counts of added nodes in each sub-list func (node *PathNode[E, V]) ListString(withNonAddedKeys, withSizes bool) string { builder := strings.Builder{} builder.WriteByte('\n') node.printList(&builder, indents{}, withNonAddedKeys, withSizes) return builder.String() } func (node *PathNode[E, V]) printList(builder *strings.Builder, indents indents, withNonAdded, withSizes bool) { if node == nil { builder.WriteString(indents.nodeIndent) builder.WriteString(nilString()) builder.WriteByte('\n') return } next := node for { if withNonAdded || next.IsAdded() { builder.WriteString(indents.nodeIndent) builder.WriteString(next.String()) if withSizes { builder.WriteString(" (") builder.WriteString(strconv.Itoa(next.Size())) builder.WriteByte(')') } builder.WriteByte('\n') } else { builder.WriteString(indents.nodeIndent) builder.WriteString(nonAddedNodeCircle) builder.WriteByte('\n') } indents.nodeIndent = indents.subNodeInd + rightElbow indents.subNodeInd = indents.subNodeInd + belowElbows if next = next.next; next == nil { break } } } bintree-1.2.1/tree/trie.go000066400000000000000000000550631434023710600154050ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tree import ( "fmt" "strings" "unsafe" ) // BinTrie is a binary trie. // // To use BinTrie, your keys implement TrieKey. // // All keys are either fixed, in which the key value does not change, // or comprising of a prefix in which an initial sequence of bits does not change, and the the remaining bits represent all bit values. // The length of the initial fixed sequence of bits is the prefix length. // The total bit length is the same for all keys. // // A key with a prefix is also known as a prefix block, and represents all bit sequences with the same prefix. // // The zero value for BinTrie is a binary trie ready for use. // // Each node can be associated with a value, making BinTrie an associative binary trie. // If you do not wish to associate values to nodes, then use the type EmptyValueType, // in which case the value will be ignored in functions that print node strings. type BinTrie[E TrieKey[E], V any] struct { binTree[E, V] } type EmptyValueType struct{} func (trie *BinTrie[E, V]) toBinTree() *binTree[E, V] { return (*binTree[E, V])(unsafe.Pointer(trie)) } // GetRoot returns the root of this trie (in the case of bounded tries, this would be the bounded root) func (trie *BinTrie[E, V]) GetRoot() (root *BinTrieNode[E, V]) { if trie != nil { root = toTrieNode(trie.root) } return } // Returns the root of this trie (in the case of bounded tries, the absolute root ignores the bounds) func (trie *BinTrie[E, V]) absoluteRoot() (root *BinTrieNode[E, V]) { if trie != nil { root = toTrieNode(trie.root) } return } // Size returns the number of elements in the tree. // Only nodes for which IsAdded() returns true are counted. // When zero is returned, IsEmpty() returns true. func (trie *BinTrie[E, V]) Size() int { return trie.toBinTree().Size() } // NodeSize returns the number of nodes in the tree, which is always more than the number of elements. func (trie *BinTrie[E, V]) NodeSize() int { return trie.toBinTree().NodeSize() } func (trie *BinTrie[E, V]) ensureRoot(key E) *BinTrieNode[E, V] { root := trie.root if root == nil { root = trie.setRoot(key.ToPrefixBlockLen(0)) } return toTrieNode(root) } func (trie *BinTrie[E, V]) setRoot(key E) *binTreeNode[E, V] { root := &binTreeNode[E, V]{ item: key, cTracker: &changeTracker{}, } root.setAddr() trie.root = root return root } // Iterator returns an iterator that iterates through the elements of the sub-tree with this node as the root. // The iteration is in sorted element order. func (trie *BinTrie[E, V]) Iterator() TrieKeyIterator[E] { return trie.GetRoot().Iterator() } // DescendingIterator returns an iterator that iterates through the elements of the subtrie with this node as the root. // The iteration is in reverse sorted element order. func (trie *BinTrie[E, V]) DescendingIterator() TrieKeyIterator[E] { return trie.GetRoot().DescendingIterator() } // String returns a visual representation of the tree with one node per line. func (trie *BinTrie[E, V]) String() string { if trie == nil { return nilString() } return trie.binTree.String() } // TreeString returns a visual representation of the tree with one node per line, with or without the non-added keys. func (trie *BinTrie[E, V]) TreeString(withNonAddedKeys bool) string { if trie == nil { return "\n" + nilString() } return trie.binTree.TreeString(withNonAddedKeys) } // Add adds the given key to the trie, returning true if not there already. func (trie *BinTrie[E, V]) Add(key E) bool { root := trie.ensureRoot(key) result := &opResult[E, V]{ key: key, op: insert, } root.matchBits(result) return !result.exists } // AddNode is similar to Add but returns the new or existing node. func (trie *BinTrie[E, V]) AddNode(key E) *BinTrieNode[E, V] { root := trie.ensureRoot(key) result := &opResult[E, V]{ key: key, op: insert, } root.matchBits(result) node := result.existingNode if node == nil { node = result.inserted } return node } func (trie *BinTrie[E, V]) addNode(result *opResult[E, V], fromNode *BinTrieNode[E, V]) *BinTrieNode[E, V] { fromNode.matchBitsFromIndex(fromNode.GetKey().GetPrefixLen().Len(), result) node := result.existingNode if node == nil { return result.inserted } return node } func (trie *BinTrie[E, V]) addTrie(addedTreeNode *BinTrieNode[E, V], withValues bool) *BinTrieNode[E, V] { if addedTreeNode == nil { // addedTreeNode can be nil when the root of a zero-valued trie return nil } iterator := addedTreeNode.ContainingFirstAllNodeIterator(true) toAdd := iterator.Next() firstKey := toAdd.GetKey() result := &opResult[E, V]{ key: firstKey, op: insert, } var firstNode *BinTrieNode[E, V] root := trie.ensureRoot(firstKey) firstAdded := toAdd.IsAdded() if firstAdded { if withValues { result.newValue = toAdd.GetValue() // new value assignment } firstNode = trie.addNode(result, root) } else { firstNode = root } lastAddedNode := firstNode for iterator.HasNext() { iterator.CacheWithLowerSubNode(lastAddedNode) iterator.CacheWithUpperSubNode(lastAddedNode) toAdd = iterator.Next() cachedNode := iterator.GetCached().(*BinTrieNode[E, V]) if toAdd.IsAdded() { addrNext := toAdd.GetKey() result.key = addrNext result.existingNode = nil result.inserted = nil if withValues { result.newValue = toAdd.GetValue() // new value assignment } lastAddedNode = trie.addNode(result, cachedNode) } else { lastAddedNode = cachedNode } } if !firstAdded { firstNode = trie.GetNode(addedTreeNode.GetKey()) } return firstNode } // AddTrieKeys copies the trie node structure of addedTrie into trie, but does not copy node mapped values func AddTrieKeys[E TrieKey[E], V1 any, V2 any](trie *BinTrie[E, V1], addedTreeNode *BinTrieNode[E, V2]) *BinTrieNode[E, V1] { if addedTreeNode == nil { // addedTreeNode can be nil when the root of a zero-valued trie return nil } iterator := addedTreeNode.ContainingFirstAllNodeIterator(true) toAdd := iterator.Next() firstKey := toAdd.GetKey() result := &opResult[E, V1]{ key: firstKey, op: insert, } var firstNode *BinTrieNode[E, V1] root := trie.ensureRoot(firstKey) firstAdded := toAdd.IsAdded() if firstAdded { firstNode = trie.addNode(result, root) } else { firstNode = root } lastAddedNode := firstNode for iterator.HasNext() { iterator.CacheWithLowerSubNode(lastAddedNode) iterator.CacheWithUpperSubNode(lastAddedNode) toAdd = iterator.Next() cachedNode := iterator.GetCached().(*BinTrieNode[E, V1]) if toAdd.IsAdded() { result.key = toAdd.GetKey() result.existingNode = nil result.inserted = nil lastAddedNode = trie.addNode(result, cachedNode) } else { lastAddedNode = cachedNode } } if !firstAdded { firstNode = trie.GetNode(addedTreeNode.GetKey()) } return firstNode } func (trie *BinTrie[E, V]) AddTrie(trieNode *BinTrieNode[E, V]) *BinTrieNode[E, V] { if trieNode == nil { return nil } trie.ensureRoot(trieNode.GetKey()) return trie.addTrie(trieNode, false) } func (trie *BinTrie[E, V]) Contains(key E) bool { return trie.absoluteRoot().Contains(key) } func (trie *BinTrie[E, V]) Remove(key E) bool { return trie.absoluteRoot().RemoveNode(key) } func (trie *BinTrie[E, V]) RemoveElementsContainedBy(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().RemoveElementsContainedBy(key) } func (trie *BinTrie[E, V]) ElementsContainedBy(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().ElementsContainedBy(key) } func (trie *BinTrie[E, V]) ElementsContaining(key E) *Path[E, V] { return trie.absoluteRoot().ElementsContaining(key) } // LongestPrefixMatch finds the key with the longest matching prefix. func (trie *BinTrie[E, V]) LongestPrefixMatch(key E) (E, bool) { return trie.absoluteRoot().LongestPrefixMatch(key) } // LongestPrefixMatchNode finds the node with the longest matching prefix. func (trie *BinTrie[E, V]) LongestPrefixMatchNode(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().LongestPrefixMatchNode(key) } func (trie *BinTrie[E, V]) ElementContains(key E) bool { return trie.absoluteRoot().ElementContains(key) } // GetNode gets the node in the sub-trie corresponding to the given address, // or returns nil if not such element exists. // // It returns any node, whether added or not, // including any prefix block node that was not added. func (trie *BinTrie[E, V]) GetNode(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().GetNode(key) } // GetAddedNode gets trie nodes representing added elements. // // Use Contains to check for the existence of a given address in the trie, // as well as GetNode to search for all nodes including those not-added but also auto-generated nodes for subnet blocks. func (trie *BinTrie[E, V]) GetAddedNode(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().GetAddedNode(key) } // Put associates the specified value with the specified key in this map. // // If the argument is not a single address nor prefix block, this method will panic. // The Partition type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // If this map previously contained a mapping for a key, // the old value is replaced by the specified value, and false is returned along with the old value, which may be the zero value. // If this map did not previously contain a mapping for the key, true is returned along with the zero value. func (trie *BinTrie[E, V]) Put(key E, value V) (V, bool) { root := trie.ensureRoot(key) result := &opResult[E, V]{ key: key, op: insert, newValue: value, // new value assignment } root.matchBits(result) return result.existingValue, !result.exists } func (trie *BinTrie[E, V]) PutTrie(trieNode *BinTrieNode[E, V]) *BinTrieNode[E, V] { if trieNode == nil { return nil } trie.ensureRoot(trieNode.GetKey()) return trie.addTrie(trieNode, true) } func (trie *BinTrie[E, V]) PutNode(key E, value V) *BinTrieNode[E, V] { root := trie.ensureRoot(key) result := &opResult[E, V]{ key: key, op: insert, newValue: value, // new value assignment } root.matchBits(result) resultNode := result.existingNode if resultNode == nil { resultNode = result.inserted } return resultNode } func (trie *BinTrie[E, V]) Remap(key E, remapper func(existing V, found bool) (mapped V, mapIt bool)) *BinTrieNode[E, V] { return trie.remapImpl(key, func(existingVal V, exists bool) (V, remapAction) { result, mapIt := remapper(existingVal, exists) if mapIt { return result, remapValue } var v V return v, removeNode }) } func (trie *BinTrie[E, V]) RemapIfAbsent(key E, supplier func() V) *BinTrieNode[E, V] { return trie.remapImpl(key, func(existingVal V, exists bool) (V, remapAction) { if !exists { return supplier(), remapValue } var v V return v, doNothing }) } func (trie *BinTrie[E, V]) remapImpl(key E, remapper func(val V, exists bool) (V, remapAction)) *BinTrieNode[E, V] { root := trie.ensureRoot(key) result := &opResult[E, V]{ key: key, op: remap, remapper: remapper, } root.matchBits(result) resultNode := result.existingNode if resultNode == nil { resultNode = result.inserted } return resultNode } func (trie *BinTrie[E, V]) Get(key E) (V, bool) { return trie.absoluteRoot().Get(key) } // NodeIterator returns an iterator that iterates through the added nodes of the trie in forward or reverse tree order. func (trie *BinTrie[E, V]) NodeIterator(forward bool) TrieNodeIteratorRem[E, V] { return trie.absoluteRoot().NodeIterator(forward) } // AllNodeIterator returns an iterator that iterates through all the nodes of the trie in forward or reverse tree order. func (trie *BinTrie[E, V]) AllNodeIterator(forward bool) TrieNodeIteratorRem[E, V] { return trie.absoluteRoot().AllNodeIterator(forward) } // BlockSizeNodeIterator returns an iterator that iterates the added nodes in the trie, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (trie *BinTrie[E, V]) BlockSizeNodeIterator(lowerSubNodeFirst bool) TrieNodeIteratorRem[E, V] { return trie.absoluteRoot().BlockSizeNodeIterator(lowerSubNodeFirst) } // BlockSizeAllNodeIterator returns an iterator that iterates all nodes in the trie, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (trie *BinTrie[E, V]) BlockSizeAllNodeIterator(lowerSubNodeFirst bool) TrieNodeIteratorRem[E, V] { return trie.absoluteRoot().BlockSizeAllNodeIterator(lowerSubNodeFirst) } // BlockSizeCachingAllNodeIterator returns an iterator that iterates all nodes, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. func (trie *BinTrie[E, V]) BlockSizeCachingAllNodeIterator() CachingTrieNodeIterator[E, V] { return trie.absoluteRoot().BlockSizeCachingAllNodeIterator() } func (trie *BinTrie[E, V]) ContainingFirstIterator(forwardSubNodeOrder bool) CachingTrieNodeIterator[E, V] { return trie.absoluteRoot().ContainingFirstIterator(forwardSubNodeOrder) } func (trie *BinTrie[E, V]) ContainingFirstAllNodeIterator(forwardSubNodeOrder bool) CachingTrieNodeIterator[E, V] { return trie.absoluteRoot().ContainingFirstAllNodeIterator(forwardSubNodeOrder) } func (trie *BinTrie[E, V]) ContainedFirstIterator(forwardSubNodeOrder bool) TrieNodeIteratorRem[E, V] { return trie.absoluteRoot().ContainedFirstIterator(forwardSubNodeOrder) } func (trie *BinTrie[E, V]) ContainedFirstAllNodeIterator(forwardSubNodeOrder bool) TrieNodeIterator[E, V] { return trie.absoluteRoot().ContainedFirstAllNodeIterator(forwardSubNodeOrder) } func (trie *BinTrie[E, V]) FirstNode() *BinTrieNode[E, V] { return trie.absoluteRoot().FirstNode() } func (trie *BinTrie[E, V]) FirstAddedNode() *BinTrieNode[E, V] { return trie.absoluteRoot().FirstAddedNode() } func (trie *BinTrie[E, V]) LastNode() *BinTrieNode[E, V] { return trie.absoluteRoot().LastNode() } func (trie *BinTrie[E, V]) LastAddedNode() *BinTrieNode[E, V] { return trie.absoluteRoot().LastAddedNode() } func (trie *BinTrie[E, V]) LowerAddedNode(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().LowerAddedNode(key) } func (trie *BinTrie[E, V]) FloorAddedNode(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().FloorAddedNode(key) } func (trie *BinTrie[E, V]) HigherAddedNode(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().HigherAddedNode(key) } func (trie *BinTrie[E, V]) CeilingAddedNode(key E) *BinTrieNode[E, V] { return trie.absoluteRoot().CeilingAddedNode(key) } func (trie *BinTrie[E, V]) Clone() *BinTrie[E, V] { if trie == nil { return nil } return &BinTrie[E, V]{binTree[E, V]{root: trie.absoluteRoot().CloneTree().toBinTreeNode()}} } // DeepEqual returns whether the given argument is a trie with a set of nodes with the same keys as in this trie according to the Compare method, // and the same values according to the reflect.DeepEqual method func (trie *BinTrie[E, V]) DeepEqual(other *BinTrie[E, V]) bool { return trie.absoluteRoot().TreeDeepEqual(other.absoluteRoot()) } // Equal returns whether the given argument is a trie with a set of nodes with the same keys as in this trie according to the Compare method func (trie *BinTrie[E, V]) Equal(other *BinTrie[E, V]) bool { return trie.absoluteRoot().TreeEqual(other.absoluteRoot()) } // For some reason Format must be here and not in addressTrieNode for nil node. // It panics in fmt code either way, but if in here then it is handled by a recover() call in fmt properly. // Seems to be a problem only in the debugger. // Format implements the fmt.Formatter interface func (trie BinTrie[E, V]) Format(state fmt.State, verb rune) { trie.format(state, verb) } // NewBinTrie creates a new trie with root key.ToPrefixBlockLen(0). // If the key argument is not Equal to its zero-length prefix block, then the key will be added as well. func NewBinTrie[E TrieKey[E], V any](key E) BinTrie[E, V] { trie := BinTrie[E, V]{binTree[E, V]{}} root := key.ToPrefixBlockLen(0) trie.setRoot(root) if key.Compare(root) != 0 { trie.Add(key) } return trie } func TreesString[E TrieKey[E], V any](withNonAddedKeys bool, tries ...*BinTrie[E, V]) string { binTrees := make([]*binTree[E, V], 0, len(tries)) for _, trie := range tries { binTrees = append(binTrees, tobinTree(trie)) } return treesString(withNonAddedKeys, binTrees...) } func tobinTree[E TrieKey[E], V any](trie *BinTrie[E, V]) *binTree[E, V] { return (*binTree[E, V])(unsafe.Pointer(trie)) } // ConstructAddedNodesTree provides an associative trie in which the root and each added node of this trie are mapped to a list of their respective direct added sub-nodes. // This trie provides an alternative non-binary tree structure of the added nodes. // It is used by ToAddedNodesTreeString to produce a string showing the alternative structure. // If there are no non-added nodes in this trie, // then the alternative tree structure provided by this method is the same as the original trie. // The trie values of this trie are of type []*BinTrieNode func (trie *BinTrie[E, V]) ConstructAddedNodesTree() BinTrie[E, AddedSubnodeMapping] { var newRoot *binTreeNode[E, AddedSubnodeMapping] existingRoot := trie.GetRoot() if existingRoot != nil { newRoot := &binTreeNode[E, AddedSubnodeMapping]{ item: trie.root.item, cTracker: &changeTracker{}, } newRoot.setAddr() if trie.root.IsAdded() { newRoot.SetAdded() } } newTrie := BinTrie[E, AddedSubnodeMapping]{binTree[E, AddedSubnodeMapping]{newRoot}} // populate the keys from the original trie into the new trie AddTrieKeys(&newTrie, existingRoot) // now, as we iterate, // we find our parent and add ourselves to that parent's list of subnodes var cachingIterator CachingTrieNodeIterator[E, AddedSubnodeMapping] cachingIterator = newTrie.ContainingFirstAllNodeIterator(true) thisIterator := trie.ContainingFirstAllNodeIterator(true) var newNext *BinTrieNode[E, AddedSubnodeMapping] var thisNext *BinTrieNode[E, V] for newNext, thisNext = cachingIterator.Next(), thisIterator.Next(); newNext != nil; newNext, thisNext = cachingIterator.Next(), thisIterator.Next() { // populate the values from the original trie into the new trie newNext.SetValue(SubNodesMapping[E, V]{Value: thisNext.GetValue()}) cachingIterator.CacheWithLowerSubNode(newNext) cachingIterator.CacheWithUpperSubNode(newNext) // the cached object is our parent if newNext.IsAdded() { var parent *BinTrieNode[E, AddedSubnodeMapping] parent = cachingIterator.GetCached().(*BinTrieNode[E, AddedSubnodeMapping]) if parent != nil { // find added parent, or the root if no added parent for !parent.IsAdded() { parentParent := parent.GetParent() if parentParent == nil { break } parent = parentParent } // store ourselves with that added parent or root var val SubNodesMapping[E, V] val = parent.GetValue().(SubNodesMapping[E, V]) var list []*BinTrieNode[E, AddedSubnodeMapping] if val.SubNodes == nil { list = make([]*BinTrieNode[E, AddedSubnodeMapping], 0, 3) } else { list = val.SubNodes } val.SubNodes = append(list, newNext) parent.SetValue(val) } // else root } } return newTrie } // AddedNodesTreeString provides a flattened version of the trie showing only the contained added nodes and their containment structure, which is non-binary. // The root node is included, which may or may not be added. func (trie *BinTrie[E, V]) AddedNodesTreeString() string { if trie == nil { return "\n" + nilString() } addedTree := trie.ConstructAddedNodesTree() return AddedNodesTreeString[E, V](addedTree.GetRoot()) } // AddedNodesTreeString provides a flattened version of the trie showing only the contained added nodes and their containment structure, which is non-binary. // The root node is included, which may or may not be added. func AddedNodesTreeString[E TrieKey[E], V any](addedTree *BinTrieNode[E, AddedSubnodeMapping]) string { var stack []indentsNode[E] builder := strings.Builder{} builder.WriteByte('\n') nodeIndent, subNodeIndent := "", "" nextNode := addedTree for { builder.WriteString(nodeIndent) builder.WriteString(NodeString[E, V](printWrapper[E, V]{nextNode})) builder.WriteByte('\n') var nextVal AddedSubnodeMapping // SubNodesMapping[E, V] nextVal = nextNode.GetValue() var nextNodes []*BinTrieNode[E, AddedSubnodeMapping] if nextVal != nil { mapping := nextVal.(SubNodesMapping[E, V]) if mapping.SubNodes != nil { nextNodes = mapping.SubNodes } } if len(nextNodes) > 0 { i := len(nextNodes) - 1 lastIndents := indents{ nodeIndent: subNodeIndent + rightElbow, subNodeInd: subNodeIndent + belowElbows, } var nNode *BinTrieNode[E, AddedSubnodeMapping] // SubNodesMapping[E, V] nNode = nextNodes[i] if stack == nil { stack = make([]indentsNode[E], 0, addedTree.Size()) } stack = append(stack, indentsNode[E]{lastIndents, nNode}) if len(nextNodes) > 1 { firstIndents := indents{ nodeIndent: subNodeIndent + leftElbow, subNodeInd: subNodeIndent + inBetweenElbows, } for i--; i >= 0; i-- { nNode = nextNodes[i] stack = append(stack, indentsNode[E]{firstIndents, nNode}) } } } stackLen := len(stack) if stackLen == 0 { break } newLen := stackLen - 1 nextItem := stack[newLen] stack = stack[:newLen] nextNode = nextItem.node nextIndents := nextItem.inds nodeIndent = nextIndents.nodeIndent subNodeIndent = nextIndents.subNodeInd } return builder.String() } type SubNodesMapping[E TrieKey[E], V any] struct { Value V // subNodes is the list of direct and indirect added subnodes in the original trie SubNodes []*BinTrieNode[E, AddedSubnodeMapping] } type AddedSubnodeMapping any // AddedSubnodeMapping / any is always SubNodesMapping[E,V] type printWrapper[E TrieKey[E], V any] struct { *BinTrieNode[E, AddedSubnodeMapping] } func (p printWrapper[E, V]) GetValue() V { var nodeValue AddedSubnodeMapping = p.BinTrieNode.GetValue() if nodeValue == nil { var v V return v } return nodeValue.(SubNodesMapping[E, V]).Value } type indentsNode[E TrieKey[E]] struct { inds indents node *BinTrieNode[E, AddedSubnodeMapping] } bintree-1.2.1/tree/trienode.go000066400000000000000000001335221434023710600162500ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tree import ( "fmt" "reflect" "unsafe" ) type operation int const ( // Given a key E insert operation = iota // add node for E if not already there remap // alters nodes based on the existing nodes and their values lookup // find node for E, traversing all containing elements along the way near // closest match, going down trie to get element considered closest. Whether one thing is closer than another is determined by the sorted order. containing // list the nodes whose keys contain E insertedDelete // Remove node for E subtreeDelete // Remove nodes whose keys are contained by E ) type opResult[E TrieKey[E], V any] struct { key E // whether near is searching for a floor or ceiling // a floor is greatest element below addr // a ceiling is lowest element above addr nearestFloor, // whether near cannot be an exact match nearExclusive bool op operation // lookups: // an inserted tree element matches the supplied argument // exists is set to true only for "added" nodes exists bool // the matching tree element, when doing a lookup operation, or the pre-existing node for an insert operation // existingNode is set for both added and not added nodes existingNode, // the closest tree element, when doing a near operation nearestNode, // if searching for a floor/lower, and the nearest node is above addr, then we must backtrack to get below // if searching for a ceiling/higher, and the nearest node is below addr, then we must backtrack to get above backtrackNode, // contained by: // this tree is contained by the supplied argument containedBy, // deletions: // this tree was deleted deleted *BinTrieNode[E, V] // contains: // A linked list of the tree elements, from largest to smallest, // that contain the supplied argument, and the end of the list containing, containingEnd *PathNode[E, V] // The tree node with the smallest subnet or address containing the supplied argument smallestContaining *BinTrieNode[E, V] // adds and puts: // new and existing values for add, put and remap operations newValue, existingValue V // this added tree node was newly created for an add inserted, // this added tree node previously existed but had not been added yet added, // this added tree node was already added to the trie addedAlready *BinTrieNode[E, V] // remaps: // remaps values based on their current contents remapper func(val V, exists bool) (V, remapAction) } func (result *opResult[E, V]) getContaining() *Path[E, V] { containing := result.containing if containing == nil { return &Path[E, V]{} } return &Path[E, V]{ root: containing, leaf: result.containingEnd, } } // add to the list of tree elements that contain the supplied argument func (result *opResult[E, V]) addContaining(containingSub *BinTrieNode[E, V]) { if containingSub.IsAdded() { node := &PathNode[E, V]{ item: containingSub.item, value: containingSub.value, storedSize: 1, added: true, } if result.containing == nil { result.containing = node } else { last := result.containingEnd last.next = node node.previous = last last.storedSize++ for next := last.previous; next != nil; next = next.previous { next.storedSize++ } } result.containingEnd = node } } type TrieKeyIterator[E TrieKey[E]] interface { HasNext Next() E // Remove removes the last iterated element from the underlying trie, and returns that element. // If there is no such element, it returns the zero value. Remove() E } type trieKeyIterator[E TrieKey[E]] struct { keyIterator[E] } func (iter trieKeyIterator[E]) Next() E { return iter.keyIterator.Next() } func (iter trieKeyIterator[E]) Remove() E { return iter.keyIterator.Remove() } type TrieNodeIterator[E TrieKey[E], V any] interface { HasNext Next() *BinTrieNode[E, V] } type TrieNodeIteratorRem[E TrieKey[E], V any] interface { TrieNodeIterator[E, V] // Remove removes the last iterated element from the underlying trie, and returns that element. // If there is no such element, it returns the zero value. Remove() *BinTrieNode[E, V] } type trieNodeIteratorRem[E TrieKey[E], V any] struct { nodeIteratorRem[E, V] } func (iter trieNodeIteratorRem[E, V]) Next() *BinTrieNode[E, V] { return toTrieNode(iter.nodeIteratorRem.Next()) } func (iter trieNodeIteratorRem[E, V]) Remove() *BinTrieNode[E, V] { return toTrieNode(iter.nodeIteratorRem.Remove()) } type trieNodeIterator[E TrieKey[E], V any] struct { nodeIterator[E, V] } func (iter trieNodeIterator[E, V]) Next() *BinTrieNode[E, V] { return toTrieNode(iter.nodeIterator.Next()) } type CachingTrieNodeIterator[E TrieKey[E], V any] interface { TrieNodeIteratorRem[E, V] CachingIterator } type cachingTrieNodeIterator[E TrieKey[E], V any] struct { cachingNodeIterator[E, V] // an interface } func (iter *cachingTrieNodeIterator[E, V]) Next() *BinTrieNode[E, V] { return toTrieNode(iter.cachingNodeIterator.Next()) } func (iter *cachingTrieNodeIterator[E, V]) Remove() *BinTrieNode[E, V] { return toTrieNode(iter.cachingNodeIterator.Remove()) } // KeyCompareResult has callbacks for a key comparison of a new key with a key pre-existing in the trie. // At most one of the two methods should be called when comparing keys. // If existing key is shorter, and the new key matches all bits in the existing key, then neither method should be called. type KeyCompareResult interface { // BitsMatch should be called when the existing key is the same size or large as the new key and the new key bits match the exiting key bits. BitsMatch() // BitsDoNotMatch should be called when at least one bit in the new key does not match the same bit in the existing key. BitsDoNotMatch(matchedBits BitCount) } // TrieKey represents a key for a trie. // // All trie keys represent a sequence of bits. // The bit count, which is the same for all keys, is the total number of bits in the key. // // Some trie keys represent a fixed sequence of bits. The bits have a single value. // // The remaining trie keys have an initial sequence of bits, the prefix, within which the bits are fixed, // and the remaining bits beyond the prefix are not fixed and represent all potential bit values. // Such keys represent all values with the same prefix. // // When all bits in a given key are fixed, the key has no prefix or prefix length. // // When not all bits are fixed, the prefix length is the number of bits in the initial fixed sequence. // A key with a prefix length is also known as a prefix block. // // A key should never change. // For keys with a prefix length, the prefix length must remain constance, and the prefix bits must remain constant. // For keys with no prefix length, all the key bits must remain constant. type TrieKey[E any] interface { comparable // MatchBits matches the bits in this key to the bits in the given key, starting from the given bit index. // Only the remaining bits in the prefix can be compared for either key. // If the prefix length of a key is nil, all the remaining bits can be compared. // // MatchBits returns true on a successful match or mismatch, and false if only a partial match. // // MatchBits calls BitsMatch in handleMatch when the given key matches all the bits in this key (even if this key has a shorter prefix), // or calls BitsDoNotMatch in handleMatch when there is a mismatch of bits, returning true in both cases. // // If the given key has a shorter prefix length, so not all bits in this key can be compared to the given key, // but the bits that can be compared are a match, then that is a partial match. // MatchBits calls neither method in handleMatch and returns false in that case. MatchBits(key E, bitIndex BitCount, handleMatch KeyCompareResult) bool // Compare returns a negative integer, zero, or a positive integer if this instance is less than, equal, or greater than the give item. // When comparing, the first mismatched bit determines the result. // If either key is prefixed, you compare only the bits up until the minumum prefix length. // If those bits are equal, and both have the same prefix length, they are equal. // Otherwise, the next bit in the key with the longer prefix (or no prefix at all) determines the result. // If that bit is 1, that key is larger, if it is 0, then smaller. Compare(E) int // GetBitCount returns the bit count for the key, which is a fixed value for any and all keys in the trie. GetBitCount() BitCount // GetPrefixLen returns the prefix length if this key has a prefix length (ie it is a prefix block). // It returns nil if not a prefix block. GetPrefixLen() PrefixLen // IsOneBit returns whether a given bit in the prefix is 1. // If the key is a prefix block, the operation is undefined if the bit index falls outside the prefix. // This method will never be called with a bit index that exceeds the prefix. IsOneBit(bitIndex BitCount) bool // ToPrefixBlockLen creates a new key with a prefix of the given length ToPrefixBlockLen(prefixLen BitCount) E // GetTrailingBitCount returns the number of trailing ones or zeros in the key. // If the key has a prefix length, GetTrailingBitCount is undefined. // This method will never be called on a key with a prefix length. GetTrailingBitCount(ones bool) BitCount // ToMaxLower returns a new key. If this key has a prefix length, it is converted to a key with a 0 as the first bit following the prefix, followed by all ones to the end, and with the prefix length then removed. // It returns this same key if it has no prefix length. // For instance, if this key is 1010**** with a prefix length of 4, the returned key is 10100111 with no prefix length. ToMaxLower() E // ToMinUpper returns a new key. If this key has a prefix length, it is converted to a key with a 1 as the first bit following the prefix, followed by all zeros to the end, and with the prefix length then removed. // It returns this same key if it has no prefix length. // For instance, if this key is 1010**** with a prefix length of 4, the returned key is 10101000 with no prefix length. ToMinUpper() E } type BinTrieNode[E TrieKey[E], V any] struct { binTreeNode[E, V] } // works with nil func (node *BinTrieNode[E, V]) toBinTreeNode() *binTreeNode[E, V] { return (*binTreeNode[E, V])(unsafe.Pointer(node)) } // setKey sets the key used for placing the node in the tree. // when freezeRoot is true, this is never called (and freezeRoot is always true) func (node *BinTrieNode[E, V]) setKey(item E) { node.binTreeNode.setKey(item) } // GetKey gets the key used for placing the node in the tree. func (node *BinTrieNode[E, V]) GetKey() E { return node.toBinTreeNode().GetKey() } // IsRoot returns whether this is the root of the backing tree. func (node *BinTrieNode[E, V]) IsRoot() bool { return node.toBinTreeNode().IsRoot() } // IsAdded returns whether the node was "added". // Some binary tree nodes are considered "added" and others are not. // Those nodes created for key elements added to the tree are "added" nodes. // Those that are not added are those nodes created to serve as junctions for the added nodes. // Only added elements contribute to the size of a tree. // When removing nodes, non-added nodes are removed automatically whenever they are no longer needed, // which is when an added node has less than two added sub-nodes. func (node *BinTrieNode[E, V]) IsAdded() bool { return node.toBinTreeNode().IsAdded() } // Clear removes this node and all sub-nodes from the tree, after which isEmpty() will return true. func (node *BinTrieNode[E, V]) Clear() { node.toBinTreeNode().Clear() } // IsEmpty returns where there are not any elements in the sub-tree with this node as the root. func (node *BinTrieNode[E, V]) IsEmpty() bool { return node.toBinTreeNode().IsEmpty() } // IsLeaf returns whether this node is in the tree (a node for which IsAdded() is true) // and there are no elements in the sub-tree with this node as the root. func (node *BinTrieNode[E, V]) IsLeaf() bool { return node.toBinTreeNode().IsLeaf() } func (node *BinTrieNode[E, V]) GetValue() (val V) { return node.toBinTreeNode().GetValue() } func (node *BinTrieNode[E, V]) ClearValue() { node.toBinTreeNode().ClearValue() } // Remove removes this node from the collection of added nodes, // and also removes from the tree if possible. // If it has two sub-nodes, it cannot be removed from the tree, in which case it is marked as not "added", // nor is it counted in the tree size. // Only added nodes can be removed from the tree. If this node is not added, this method does nothing. func (node *BinTrieNode[E, V]) Remove() { node.toBinTreeNode().Remove() } // NodeSize returns the count of all nodes in the tree starting from this node and extending to all sub-nodes. // Unlike for the Size method, this is not a constant-time operation and must visit all sub-nodes of this node. func (node *BinTrieNode[E, V]) NodeSize() int { return node.toBinTreeNode().NodeSize() } // Size returns the count of nodes added to the sub-tree starting from this node as root and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node and adjusted with each add and Remove operation in the sub-tree. func (node *BinTrieNode[E, V]) Size() int { return node.toBinTreeNode().Size() } // TreeString returns a visual representation of the sub-tree with this node as root, with one node per line. // // withNonAddedKeys: whether to show nodes that are not added nodes // withSizes: whether to include the counts of added nodes in each sub-tree func (node *BinTrieNode[E, V]) TreeString(withNonAddedKeys, withSizes bool) string { return node.toBinTreeNode().TreeString(withNonAddedKeys, withSizes) } // Returns a visual representation of this node including the key, with an open circle indicating this node is not an added node, // a closed circle indicating this node is an added node. func (node *BinTrieNode[E, V]) String() string { return node.toBinTreeNode().String() } func (node *BinTrieNode[E, V]) setUpper(upper *BinTrieNode[E, V]) { node.binTreeNode.setUpper(&upper.binTreeNode) } func (node *BinTrieNode[E, V]) setLower(lower *BinTrieNode[E, V]) { node.binTreeNode.setLower(&lower.binTreeNode) } // GetUpperSubNode gets the direct child node whose key is largest in value func (node *BinTrieNode[E, V]) GetUpperSubNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().getUpperSubNode()) //return node.toBinTreeNode().getUpperSubNode().toTrieNode() } // GetLowerSubNode gets the direct child node whose key is smallest in value func (node *BinTrieNode[E, V]) GetLowerSubNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().getLowerSubNode()) //return node.toBinTreeNode().getLowerSubNode().toTrieNode() } // GetParent gets the node from which this node is a direct child node, or nil if this is the root. func (node *BinTrieNode[E, V]) GetParent() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().getParent()) //return node.toBinTreeNode().getParent().toTrieNode() } func (node *BinTrieNode[E, V]) Contains(addr E) bool { result := node.doLookup(addr) return result.exists } func (node *BinTrieNode[E, V]) RemoveNode(key E) bool { result := &opResult[E, V]{ key: key, op: insertedDelete, } if node != nil { node.matchBits(result) } return result.exists } // GetNode gets the node in the trie corresponding to the given address, // or returns nil if not such element exists. // // It returns any node, whether added or not, // including any prefix block node that was not added. func (node *BinTrieNode[E, V]) GetNode(key E) *BinTrieNode[E, V] { result := node.doLookup(key) return result.existingNode } // GetAddedNode gets trie nodes representing added elements. // // Use Contains to check for the existence of a given address in the trie, // as well as GetNode to search for all nodes including those not-added but also auto-generated nodes for subnet blocks. func (node *BinTrieNode[E, V]) GetAddedNode(key E) *BinTrieNode[E, V] { if res := node.GetNode(key); res == nil || res.IsAdded() { return res } return nil } func (node *BinTrieNode[E, V]) Get(key E) (V, bool) { result := &opResult[E, V]{ key: key, op: lookup, } if node != nil { node.matchBits(result) } resultNode := result.existingNode if resultNode == nil { var v V return v, false } return resultNode.GetValue(), true } func (node *BinTrieNode[E, V]) RemoveElementsContainedBy(key E) *BinTrieNode[E, V] { result := &opResult[E, V]{ key: key, op: subtreeDelete, } if node != nil { node.matchBits(result) } return result.deleted } func (node *BinTrieNode[E, V]) ElementsContainedBy(key E) *BinTrieNode[E, V] { result := node.doLookup(key) return result.containedBy } // ElementsContaining finds the trie nodes containing the given key and returns them as a linked list // only added nodes are added to the linked list func (node *BinTrieNode[E, V]) ElementsContaining(key E) *Path[E, V] { result := &opResult[E, V]{ key: key, op: containing, } if node != nil { node.matchBits(result) } return result.getContaining() } // LongestPrefixMatch finds the longest matching prefix amongst keys added to the trie func (node *BinTrieNode[E, V]) LongestPrefixMatch(key E) (E, bool) { res := node.LongestPrefixMatchNode(key) if res == nil { var e E return e, false } return res.GetKey(), true } // LongestPrefixMatchNode finds the node with the longest matching prefix // only added nodes are added to the linked list func (node *BinTrieNode[E, V]) LongestPrefixMatchNode(key E) *BinTrieNode[E, V] { return node.doLookup(key).smallestContaining } func (node *BinTrieNode[E, V]) ElementContains(key E) bool { _, ok := node.LongestPrefixMatch(key) return ok } func (node *BinTrieNode[E, V]) doLookup(key E) *opResult[E, V] { result := &opResult[E, V]{ key: key, op: lookup, } if node != nil { node.matchBits(result) } return result } func (node *BinTrieNode[E, V]) removeSubtree(result *opResult[E, V]) { result.deleted = node node.Clear() } func (node *BinTrieNode[E, V]) removeOp(result *opResult[E, V]) { result.deleted = node node.binTreeNode.Remove() } func (node *BinTrieNode[E, V]) matchBits(result *opResult[E, V]) { node.matchBitsFromIndex(0, result) } // traverses the tree, matching bits with prefix block nodes, until we can match no longer, // at which point it completes the operation, whatever that operation is func (node *BinTrieNode[E, V]) matchBitsFromIndex(bitIndex int, result *opResult[E, V]) { matchNode := node for { bits := matchNode.matchNodeBits(bitIndex, result) if bits >= 0 { // matched all node bits up the given count, so move into sub-nodes matchNode = matchNode.matchSubNode(bits, result) if matchNode == nil { // reached the end of the line break } // Matched a sub-node. // The sub-node was chosen according to the next bit. // That bit is therefore now a match, // so increment the matched bits by 1, and keep going. bitIndex = bits + 1 } else { // reached the end of the line break } } } func (node *BinTrieNode[E, V]) matchNodeBits(bitIndex int, result *opResult[E, V]) BitCount { existingKey := node.GetKey() newKey := result.key if newKey.GetBitCount() != existingKey.GetBitCount() { panic("mismatched bit length between trie keys") } else if !newKey.MatchBits(existingKey, bitIndex, nodeCompare[E, V]{node: node, result: result}) { if node.IsAdded() { node.handleContains(result) } return existingKey.GetPrefixLen().Len() } return -1 } type nodeCompare[E TrieKey[E], V any] struct { result *opResult[E, V] node *BinTrieNode[E, V] } func (comp nodeCompare[E, V]) BitsMatch() { node := comp.node result := comp.result result.containedBy = node //result.containedBy = node.toTrieNode() existingKey := node.GetKey() existingPref := existingKey.GetPrefixLen() newKey := result.key newPrefixLen := newKey.GetPrefixLen() if existingPref == nil { if newPrefixLen == nil { // note that "added" is already true here, // we can only be here if explicitly inserted already // since it is a non-prefixed full address node.handleMatch(result) } else if newPrefixLen.Len() == newKey.GetBitCount() { node.handleMatch(result) } else { node.handleContained(result, newPrefixLen.Len()) } } else { // we know newPrefixLen != nil since we know all of the bits of newAddr match, // which is impossible if newPrefixLen is nil and existingPref not nil if newPrefixLen.Len() == existingPref.Len() { if node.IsAdded() { node.handleMatch(result) } else { node.handleNodeMatch(result) } } else if existingPref.Len() == existingKey.GetBitCount() { node.handleMatch(result) } else { // existing prefix > newPrefixLen node.handleContained(result, newPrefixLen.Len()) } } } func (comp nodeCompare[E, V]) BitsDoNotMatch(matchedBits BitCount) { comp.node.handleSplitNode(comp.result, matchedBits) } func (node *BinTrieNode[E, V]) handleContained(result *opResult[E, V], newPref int) { op := result.op if op == insert { // if we have 1.2.3.4 and 1.2.3.4/32, and we are looking at the last segment, // then there are no more bits to look at, and this makes the former a sub-node of the latter. // In most cases, however, there are more bits in existingAddr, the latter, to look at. node.replace(result, newPref) } else if op == subtreeDelete { node.removeSubtree(result) } else if op == near { node.findNearest(result, newPref) } else if op == remap { node.remapNonExistingReplace(result, newPref) } } func (node *BinTrieNode[E, V]) handleContains(result *opResult[E, V]) bool { result.smallestContaining = node if result.op == containing { result.addContaining(node) return true } return false } func (node *BinTrieNode[E, V]) handleSplitNode(result *opResult[E, V], totalMatchingBits BitCount) { op := result.op if op == insert { node.split(result, totalMatchingBits, node.createNew(result.key)) } else if op == near { node.findNearest(result, totalMatchingBits) } else if op == remap { node.remapNonExistingSplit(result, totalMatchingBits) } } // a node exists for the given key but the node is not added, // so not a match, but a split not required func (node *BinTrieNode[E, V]) handleNodeMatch(result *opResult[E, V]) { op := result.op if op == lookup { result.existingNode = node } else if op == insert { node.existingAdded(result) } else if op == subtreeDelete { node.removeSubtree(result) } else if op == near { node.findNearestFromMatch(result) } else if op == remap { node.remapNonAdded(result) } } func (node *BinTrieNode[E, V]) handleMatch(result *opResult[E, V]) { result.exists = true if !node.handleContains(result) { op := result.op if op == lookup { node.matched(result) } else if op == insert { node.matchedInserted(result) } else if op == insertedDelete { node.removeOp(result) } else if op == subtreeDelete { node.removeSubtree(result) } else if op == near { if result.nearExclusive { node.findNearestFromMatch(result) } else { node.matched(result) } } else if op == remap { node.remapMatch(result) } } } func (node *BinTrieNode[E, V]) remapNonExistingReplace(result *opResult[E, V], totalMatchingBits BitCount) { if node.remap(result, false) { node.replace(result, totalMatchingBits) } } func (node *BinTrieNode[E, V]) remapNonExistingSplit(result *opResult[E, V], totalMatchingBits BitCount) { if node.remap(result, false) { node.split(result, totalMatchingBits, node.createNew(result.key)) } } func (node *BinTrieNode[E, V]) remapNonExisting(result *opResult[E, V]) *BinTrieNode[E, V] { if node.remap(result, false) { return node.createNew(result.key) } return nil } func (node *BinTrieNode[E, V]) remapNonAdded(result *opResult[E, V]) { if node.remap(result, false) { node.existingAdded(result) } } func (node *BinTrieNode[E, V]) remapMatch(result *opResult[E, V]) { result.existingNode = node if node.remap(result, true) { node.matchedInserted(result) } } type remapAction int const ( doNothing remapAction = iota removeNode remapValue ) // Remaps the value for a node to a new value. // This operation works on mapped values // It returns true if a new node needs to be created (match is nil) or added (match is non-nil) func (node *BinTrieNode[E, V]) remap(result *opResult[E, V], isMatch bool) bool { remapper := result.remapper change := node.cTracker.getCurrent() var existingValue V if isMatch { existingValue = node.GetValue() } result.existingValue = existingValue newValue, action := remapper(existingValue, isMatch) if action == doNothing { return false } else if action == removeNode { if isMatch { cTracker := node.cTracker if cTracker != nil && cTracker.changedSince(change) { panic("the tree has been modified by the remapper") } node.ClearValue() node.removeOp(result) } return false } else { // action is remapValue cTracker := node.cTracker if cTracker != nil && cTracker.changedSince(change) { panic("the tree has been modified by the remapper") } result.newValue = newValue return true } } // this node matched when doing a lookup func (node *BinTrieNode[E, V]) matched(result *opResult[E, V]) { result.existingNode = node result.nearestNode = node } // similar to matched, but when inserting we see it already there. // this added node had already been added before func (node *BinTrieNode[E, V]) matchedInserted(result *opResult[E, V]) { result.existingNode = node result.addedAlready = node result.existingValue = node.GetValue() node.SetValue(result.newValue) } // this node previously existed but was not added til now func (node *BinTrieNode[E, V]) existingAdded(result *opResult[E, V]) { result.existingNode = node result.added = node node.added(result) } // this node is newly inserted and added func (node *BinTrieNode[E, V]) inserted(result *opResult[E, V]) { result.inserted = node node.added(result) } func (node *BinTrieNode[E, V]) added(result *opResult[E, V]) { node.setNodeAdded(true) node.adjustCount(1) node.SetValue(result.newValue) node.cTracker.changed() } // The current node and the new node both become sub-nodes of a new block node taking the position of the current node. func (node *BinTrieNode[E, V]) split(result *opResult[E, V], totalMatchingBits BitCount, newSubNode *BinTrieNode[E, V]) { newBlock := node.GetKey().ToPrefixBlockLen(totalMatchingBits) node.replaceToSub(newBlock, totalMatchingBits, newSubNode) newSubNode.inserted(result) } // The current node is replaced by the new node and becomes a sub-node of the new node. func (node *BinTrieNode[E, V]) replace(result *opResult[E, V], totalMatchingBits BitCount) { result.containedBy = node newNode := node.replaceToSub(result.key, totalMatchingBits, nil) newNode.inserted(result) } // The current node is replaced by a new block of the given key. // The current node and given node become sub-nodes. func (node *BinTrieNode[E, V]) replaceToSub(newAssignedKey E, totalMatchingBits BitCount, newSubNode *BinTrieNode[E, V]) *BinTrieNode[E, V] { newNode := node.createNew(newAssignedKey) newNode.storedSize = node.storedSize parent := node.GetParent() if parent.GetUpperSubNode() == node { parent.setUpper(newNode) } else if parent.GetLowerSubNode() == node { parent.setLower(newNode) } existingKey := node.GetKey() if totalMatchingBits < existingKey.GetBitCount() && existingKey.IsOneBit(totalMatchingBits) { if newSubNode != nil { newNode.setLower(newSubNode) } newNode.setUpper(node) } else { newNode.setLower(node) if newSubNode != nil { newNode.setUpper(newSubNode) } } return newNode } // only called when lower/higher and not floor/ceiling since for a match ends things for the latter func (node *BinTrieNode[E, V]) findNearestFromMatch(result *opResult[E, V]) { if result.nearestFloor { // looking for greatest element < queried address // since we have matched the address, we must go lower again, // and if we cannot, we must backtrack lower := node.GetLowerSubNode() if lower == nil { // no nearest node yet result.backtrackNode = node } else { var last *BinTrieNode[E, V] for { last = lower lower = lower.GetUpperSubNode() if lower == nil { break } } result.nearestNode = last } } else { // looking for smallest element > queried address upper := node.GetUpperSubNode() if upper == nil { // no nearest node yet result.backtrackNode = node } else { var last *BinTrieNode[E, V] for { last = upper upper = upper.GetLowerSubNode() if upper == nil { break } } result.nearestNode = last } } } func (node *BinTrieNode[E, V]) findNearest(result *opResult[E, V], differingBitIndex BitCount) { thisKey := node.GetKey() if differingBitIndex < thisKey.GetBitCount() && thisKey.IsOneBit(differingBitIndex) { // this element and all below are > than the query address if result.nearestFloor { // looking for greatest element < or <= queried address, so no need to go further // need to backtrack and find the last right turn to find node < than the query address again result.backtrackNode = node } else { // looking for smallest element > or >= queried address lower := node var last *BinTrieNode[E, V] for { last = lower lower = lower.GetLowerSubNode() if lower == nil { break } } result.nearestNode = last } } else { // this element and all below are < than the query address if result.nearestFloor { // looking for greatest element < or <= queried address upper := node var last *BinTrieNode[E, V] for { last = upper upper = upper.GetUpperSubNode() if upper == nil { break } } result.nearestNode = last } else { // looking for smallest element > or >= queried address, so no need to go further // need to backtrack and find the last left turn to find node > than the query address again result.backtrackNode = node } } } func (node *BinTrieNode[E, V]) matchSubNode(bitIndex BitCount, result *opResult[E, V]) *BinTrieNode[E, V] { newKey := result.key if !freezeRoot && node.IsEmpty() { if result.op == remap { node.remapNonAdded(result) } else if result.op == insert { node.setKey(newKey) node.existingAdded(result) } } else if bitIndex >= newKey.GetBitCount() { // we matched all bits, yet somehow we are still going // this can only happen when mishandling a match between 1.2.3.4/32 to 1.2.3.4 // which should never happen and so we do nothing, no match, no remap, no insert, no near } else if newKey.IsOneBit(bitIndex) { upper := node.GetUpperSubNode() if upper == nil { // no match op := result.op if op == insert { upper = node.createNew(newKey) node.setUpper(upper) upper.inserted(result) } else if op == near { if result.nearestFloor { // With only one sub-node at most, normally that would mean this node must be added. // But there is one exception, when we are the non-added root node. // So must check for added here. if node.IsAdded() { result.nearestNode = node } else { // check if our lower sub-node is there and added. It is underneath addr too. // find the highest node in that direction. lower := node.GetLowerSubNode() if lower != nil { res := lower next := res.GetUpperSubNode() for next != nil { res = next next = res.GetUpperSubNode() } result.nearestNode = res } } } else { result.backtrackNode = node } } else if op == remap { upper = node.remapNonExisting(result) if upper != nil { node.setUpper(upper) upper.inserted(result) } } } else { return upper } } else { // In most cases, however, there are more bits in newKey, the former, to look at. lower := node.GetLowerSubNode() if lower == nil { // no match op := result.op if op == insert { lower = node.createNew(newKey) node.setLower(lower) lower.inserted(result) } else if op == near { if result.nearestFloor { result.backtrackNode = node } else { // With only one sub-node at most, normally that would mean this node must be added. // But there is one exception, when we are the non-added root node. // So must check for added here. if node.IsAdded() { result.nearestNode = node } else { // check if our upper sub-node is there and added. It is above addr too. // find the highest node in that direction. upper := node.GetUpperSubNode() if upper != nil { res := upper next := res.GetLowerSubNode() for next != nil { res = next next = res.GetLowerSubNode() } result.nearestNode = res } } } } else if op == remap { lower = node.remapNonExisting(result) if lower != nil { node.setLower(lower) lower.inserted(result) } } } else { return lower } } return nil } func (node *BinTrieNode[E, V]) createNew(newKey E) *BinTrieNode[E, V] { res := &BinTrieNode[E, V]{ binTreeNode[E, V]{ item: newKey, cTracker: node.cTracker, }, } res.setAddr() return res } // PreviousAddedNode returns the previous node in the tree that is an added node, following the tree order in reverse, // or nil if there is no such node. func (node *BinTrieNode[E, V]) PreviousAddedNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().previousAddedNode()) } // NextAddedNode returns the next node in the tree that is an added node, following the tree order, // or nil if there is no such node. func (node *BinTrieNode[E, V]) NextAddedNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().nextAddedNode()) } // NextNode returns the node that follows this node following the tree order func (node *BinTrieNode[E, V]) NextNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().nextNode()) } // PreviousNode returns the node that precedes this node following the tree order. func (node *BinTrieNode[E, V]) PreviousNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().previousNode()) } func (node *BinTrieNode[E, V]) FirstNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().firstNode()) } func (node *BinTrieNode[E, V]) FirstAddedNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().firstAddedNode()) } func (node *BinTrieNode[E, V]) LastNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().lastNode()) } func (node *BinTrieNode[E, V]) LastAddedNode() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().lastAddedNode()) } func (node *BinTrieNode[E, V]) findNodeNear(key E, below, exclusive bool) *BinTrieNode[E, V] { result := &opResult[E, V]{ key: key, op: near, nearestFloor: below, nearExclusive: exclusive, } if node != nil { node.matchBits(result) } backtrack := result.backtrackNode if backtrack != nil { parent := backtrack.GetParent() for parent != nil { if below { if backtrack != parent.GetLowerSubNode() { break } } else { if backtrack != parent.GetUpperSubNode() { break } } backtrack = parent parent = backtrack.GetParent() } if parent != nil { if parent.IsAdded() { result.nearestNode = parent } else { if below { result.nearestNode = parent.PreviousAddedNode() } else { result.nearestNode = parent.NextAddedNode() } } } } return result.nearestNode } func (node *BinTrieNode[E, V]) LowerAddedNode(key E) *BinTrieNode[E, V] { return node.findNodeNear(key, true, true) } func (node *BinTrieNode[E, V]) FloorAddedNode(key E) *BinTrieNode[E, V] { return node.findNodeNear(key, true, false) } func (node *BinTrieNode[E, V]) HigherAddedNode(key E) *BinTrieNode[E, V] { return node.findNodeNear(key, false, true) } func (node *BinTrieNode[E, V]) CeilingAddedNode(key E) *BinTrieNode[E, V] { return node.findNodeNear(key, false, false) } // Iterator returns an iterator that iterates through the elements of the sub-tree with this node as the root. // The iteration is in sorted element order. func (node *BinTrieNode[E, V]) Iterator() TrieKeyIterator[E] { return trieKeyIterator[E]{node.toBinTreeNode().iterator()} } // DescendingIterator returns an iterator that iterates through the elements of the subtrie with this node as the root. // The iteration is in reverse sorted element order. func (node *BinTrieNode[E, V]) DescendingIterator() TrieKeyIterator[E] { return trieKeyIterator[E]{node.toBinTreeNode().descendingIterator()} } // NodeIterator returns an iterator that iterates through the added nodes of the sub-tree with this node as the root, in forward or reverse tree order. func (node *BinTrieNode[E, V]) NodeIterator(forward bool) TrieNodeIteratorRem[E, V] { return trieNodeIteratorRem[E, V]{node.toBinTreeNode().nodeIterator(forward)} } // AllNodeIterator returns an iterator that iterates through all the nodes of the sub-tree with this node as the root, in forward or reverse tree order. func (node *BinTrieNode[E, V]) AllNodeIterator(forward bool) TrieNodeIteratorRem[E, V] { return trieNodeIteratorRem[E, V]{node.toBinTreeNode().allNodeIterator(forward)} } // BlockSizeNodeIterator returns an iterator that iterates the added nodes, ordered by keys from largest prefix blocks (smallest prefix length) to smallest (largest prefix length) and then to individual addresses, // in the sub-trie with this node as the root. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order is taken. func (node *BinTrieNode[E, V]) BlockSizeNodeIterator(lowerSubNodeFirst bool) TrieNodeIteratorRem[E, V] { return node.blockSizeNodeIterator(lowerSubNodeFirst, true) } // BlockSizeAllNodeIterator returns an iterator that iterates all the nodes, ordered by keys from largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (node *BinTrieNode[E, V]) BlockSizeAllNodeIterator(lowerSubNodeFirst bool) TrieNodeIteratorRem[E, V] { return node.blockSizeNodeIterator(lowerSubNodeFirst, false) } // BlockSizeCompare compares keys by block size and then by prefix value if block sizes are equal func BlockSizeCompare[E TrieKey[E]](key1, key2 E, reverseBlocksEqualSize bool) int { if key2 == key1 { return 0 } pref2 := key2.GetPrefixLen() pref1 := key1.GetPrefixLen() if pref2 != nil { if pref1 != nil { val := pref2.Len() - pref1.Len() if val == 0 { compVal := key2.Compare(key1) if reverseBlocksEqualSize { compVal = -compVal } return compVal } return val } return -1 } if pref1 != nil { return 1 } compVal := key2.Compare(key1) if reverseBlocksEqualSize { compVal = -compVal } return compVal } func (node *BinTrieNode[E, V]) blockSizeNodeIterator(lowerSubNodeFirst, addedNodesOnly bool) TrieNodeIteratorRem[E, V] { reverseBlocksEqualSize := !lowerSubNodeFirst var size int if addedNodesOnly { size = node.Size() } iter := newPriorityNodeIterator( size, addedNodesOnly, node.toBinTreeNode(), func(one, two E) int { val := BlockSizeCompare(one, two, reverseBlocksEqualSize) return -val }) return trieNodeIteratorRem[E, V]{&iter} } // BlockSizeCachingAllNodeIterator returns an iterator of all nodes, ordered by keys from largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. // // This iterator allows you to cache an object with subnodes so that when those nodes are visited the cached object can be retrieved. func (node *BinTrieNode[E, V]) BlockSizeCachingAllNodeIterator() CachingTrieNodeIterator[E, V] { iter := newCachingPriorityNodeIterator( node.toBinTreeNode(), func(one, two E) int { val := BlockSizeCompare(one, two, false) return -val }) return &cachingTrieNodeIterator[E, V]{&iter} } func (node *BinTrieNode[E, V]) ContainingFirstIterator(forwardSubNodeOrder bool) CachingTrieNodeIterator[E, V] { return &cachingTrieNodeIterator[E, V]{node.toBinTreeNode().containingFirstIterator(forwardSubNodeOrder)} } func (node *BinTrieNode[E, V]) ContainingFirstAllNodeIterator(forwardSubNodeOrder bool) CachingTrieNodeIterator[E, V] { return &cachingTrieNodeIterator[E, V]{node.toBinTreeNode().containingFirstAllNodeIterator(forwardSubNodeOrder)} } func (node *BinTrieNode[E, V]) ContainedFirstIterator(forwardSubNodeOrder bool) TrieNodeIteratorRem[E, V] { return trieNodeIteratorRem[E, V]{node.toBinTreeNode().containedFirstIterator(forwardSubNodeOrder)} } func (node *BinTrieNode[E, V]) ContainedFirstAllNodeIterator(forwardSubNodeOrder bool) TrieNodeIterator[E, V] { return trieNodeIterator[E, V]{node.toBinTreeNode().containedFirstAllNodeIterator(forwardSubNodeOrder)} } // Clone clones the node. // Keys remain the same, but the parent node and the lower and upper sub-nodes are all set to nil. func (node *BinTrieNode[E, V]) Clone() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().clone()) } // CloneTree clones the sub-tree starting with this node as root. // The nodes are cloned, but their keys and values are not cloned. func (node *BinTrieNode[E, V]) CloneTree() *BinTrieNode[E, V] { return toTrieNode(node.toBinTreeNode().cloneTree()) } // AsNewTrie creates a new sub-trie, copying the nodes starting with this node as root. // The nodes are copies of the nodes in this sub-trie, but their keys and values are not copies. func (node *BinTrieNode[E, V]) AsNewTrie() *BinTrie[E, V] { // I suspect clone is faster - in Java I used AddTrie to add the bounded part of the trie if it was bounded // but AddTrie needs to insert nodes amongst existing nodes, clone does not // newTrie := NewBinTrie(key) // newTrie.AddTrie(node) key := node.GetKey() trie := &BinTrie[E, V]{binTree[E, V]{}} rootKey := key.ToPrefixBlockLen(0) trie.setRoot(rootKey) root := trie.root newNode := node.cloneTreeTrackerBounds(root.cTracker, nil) if rootKey.Compare(key) == 0 { root.setUpper(newNode.upper) root.setLower(newNode.lower) if node.IsAdded() { root.SetAdded() } root.SetValue(node.GetValue()) } else if key.IsOneBit(0) { root.setUpper(newNode) } else { root.setLower(newNode) } root.storedSize = sizeUnknown return trie } // Equal returns whether the key matches the key of the given node func (node *BinTrieNode[E, V]) Equal(other *BinTrieNode[E, V]) bool { if node == nil { return other == nil } else if other == nil { return false } return node == other || node.GetKey().Compare(other.GetKey()) == 0 } // DeepEqual returns whether the key matches the key of the given node using Compare, // and whether the value matches the other value using reflect.DeepEqual func (node *BinTrieNode[E, V]) DeepEqual(other *BinTrieNode[E, V]) bool { if node == nil { return other == nil } else if other == nil { return false } return node.GetKey().Compare(other.GetKey()) == 0 && reflect.DeepEqual(node.GetValue(), other.GetValue()) } // TreeEqual returns whether the sub-tree represented by this node as the root node matches the given sub-tree, matching the trie keys using the Compare method func (node *BinTrieNode[E, V]) TreeEqual(other *BinTrieNode[E, V]) bool { if other == node { return true } else if other.Size() != node.Size() { return false } these, others := node.Iterator(), other.Iterator() if these.HasNext() { for thisKey := these.Next(); these.HasNext(); thisKey = these.Next() { if thisKey.Compare(others.Next()) != 0 { return false } } } return true } // TreeDeepEqual returns whether the sub-tree represented by this node as the root node matches the given sub-tree, matching the nodes using DeepEqual func (node *BinTrieNode[E, V]) TreeDeepEqual(other *BinTrieNode[E, V]) bool { if other == node { return true } else if other.Size() != node.Size() { return false } these, others := node.NodeIterator(true), other.NodeIterator(true) thisNode := these.Next() for ; thisNode != nil; thisNode = these.Next() { if thisNode.DeepEqual(others.Next()) { return false } } return true } // Compare returns -1, 0 or 1 if this node is less than, equal, or greater than the other, according to the key and the trie order. func (node *BinTrieNode[E, V]) Compare(other *BinTrieNode[E, V]) int { if node == nil { if other == nil { return 0 } return -1 } else if other == nil { return 1 } return node.GetKey().Compare(other.GetKey()) } // For some reason Format must be here and not in addressTrieNode for nil node. // It panics in fmt code either way, but if in here then it is handled by a recover() call in fmt properly. // Seems to be a problem only in the debugger. // Format implements the fmt.Formatter interface func (node BinTrieNode[E, V]) Format(state fmt.State, verb rune) { node.format(state, verb) } // TrieIncrement returns the next key according to the trie ordering. // The zero value is returned when there is no next key. func TrieIncrement[E TrieKey[E]](key E) (next E, hasNext bool) { prefLen := key.GetPrefixLen() if prefLen != nil { return key.ToMinUpper(), true } bitCount := key.GetBitCount() trailingBits := key.GetTrailingBitCount(false) if trailingBits < bitCount { return key.ToPrefixBlockLen(bitCount - (trailingBits + 1)), true } return } // TrieDecrement returns the previous key according to the trie ordering // The zero value is returned when there is no previous key. func TrieDecrement[E TrieKey[E]](key E) (next E, hasNext bool) { prefLen := key.GetPrefixLen() if prefLen != nil { return key.ToMaxLower(), true } bitCount := key.GetBitCount() trailingBits := key.GetTrailingBitCount(true) if trailingBits < bitCount { return key.ToPrefixBlockLen(bitCount - (trailingBits + 1)), true } return }