Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

node.gno

12.53 Kb · 491 lines
  1package avl
  2
  3//----------------------------------------
  4// Node
  5
  6// Node represents a node in an AVL tree.
  7type Node struct {
  8	key       string // key is the unique identifier for the node.
  9	value     any    // value is the data stored in the node.
 10	height    int8   // height is the height of the node in the tree.
 11	size      int    // size is the number of leaf nodes (key-value pairs) in the subtree rooted at this node.
 12	leftNode  *Node  // leftNode is the left child of the node.
 13	rightNode *Node  // rightNode is the right child of the node.
 14}
 15
 16// NewNode creates a new node with the given key and value.
 17func NewNode(key string, value any) *Node {
 18	return &Node{
 19		key:    key,
 20		value:  value,
 21		height: 0,
 22		size:   1,
 23	}
 24}
 25
 26// Size returns the size of the subtree rooted at the node.
 27func (node *Node) Size() int {
 28	if node == nil {
 29		return 0
 30	}
 31	return node.size
 32}
 33
 34// IsLeaf checks if the node is a leaf node (has no children).
 35func (node *Node) IsLeaf() bool {
 36	return node.height == 0
 37}
 38
 39// Key returns the key of the node.
 40func (node *Node) Key() string {
 41	return node.key
 42}
 43
 44// Value returns the value of the node.
 45func (node *Node) Value() any {
 46	return node.value
 47}
 48
 49func (node *Node) _copy() *Node {
 50	if node.height == 0 {
 51		panic("Why are you copying a value node?")
 52	}
 53	return &Node{
 54		key:       node.key,
 55		height:    node.height,
 56		size:      node.size,
 57		leftNode:  node.leftNode,
 58		rightNode: node.rightNode,
 59	}
 60}
 61
 62// Has checks if a node with the given key exists in the subtree rooted at the node.
 63func (node *Node) Has(key string) (has bool) {
 64	if node == nil {
 65		return false
 66	}
 67	if node.key == key {
 68		return true
 69	}
 70	if node.height == 0 {
 71		return false
 72	} else {
 73		if key < node.key {
 74			return node.getLeftNode().Has(key)
 75		} else {
 76			return node.getRightNode().Has(key)
 77		}
 78	}
 79}
 80
 81// Get searches for a node with the given key in the subtree rooted at the node
 82// and returns its index, value, and whether it exists.
 83func (node *Node) Get(key string) (index int, value any, exists bool) {
 84	if node == nil {
 85		return 0, nil, false
 86	}
 87
 88	if node.height == 0 {
 89		if node.key == key {
 90			return 0, node.value, true
 91		} else if node.key < key {
 92			return 1, nil, false
 93		} else {
 94			return 0, nil, false
 95		}
 96	} else {
 97		if key < node.key {
 98			return node.getLeftNode().Get(key)
 99		} else {
100			rightNode := node.getRightNode()
101			index, value, exists = rightNode.Get(key)
102			index += node.size - rightNode.size
103			return index, value, exists
104		}
105	}
106}
107
108// GetByIndex retrieves the key-value pair of the node at the given index
109// in the subtree rooted at the node.
110func (node *Node) GetByIndex(index int) (key string, value any) {
111	if index < 0 {
112		panic("GetByIndex: negative index not allowed")
113	}
114
115	if node.height == 0 {
116		if index != 0 {
117			panic("GetByIndex asked for invalid index")
118		}
119		return node.key, node.value
120	} else {
121		// TODO: could improve this by storing the sizes
122		leftNode := node.getLeftNode()
123		if index < leftNode.size {
124			return leftNode.GetByIndex(index)
125		} else {
126			return node.getRightNode().GetByIndex(index - leftNode.size)
127		}
128	}
129}
130
131// Set inserts a new node with the given key-value pair into the subtree rooted at the node,
132// and returns the new root of the subtree and whether an existing node was updated.
133//
134// XXX consider a better way to do this... perhaps split Node from Node.
135func (node *Node) Set(key string, value any) (newSelf *Node, updated bool) {
136	if node == nil {
137		return NewNode(key, value), false
138	}
139	if node.height == 0 {
140		if key < node.key {
141			return &Node{
142				key:       node.key,
143				height:    1,
144				size:      2,
145				leftNode:  NewNode(key, value),
146				rightNode: node,
147			}, false
148		} else if key == node.key {
149			return NewNode(key, value), true
150		} else {
151			return &Node{
152				key:       key,
153				height:    1,
154				size:      2,
155				leftNode:  node,
156				rightNode: NewNode(key, value),
157			}, false
158		}
159	} else {
160		node = node._copy()
161		if key < node.key {
162			node.leftNode, updated = node.getLeftNode().Set(key, value)
163		} else {
164			node.rightNode, updated = node.getRightNode().Set(key, value)
165		}
166		if updated {
167			return node, updated
168		} else {
169			node.calcHeightAndSize()
170			return node.balance(), updated
171		}
172	}
173}
174
175// Remove deletes the node with the given key from the subtree rooted at the node.
176// returns the new root of the subtree, the new leftmost leaf key (if changed),
177// the removed value and the removal was successful.
178func (node *Node) Remove(key string) (
179	newNode *Node, newKey string, value any, removed bool,
180) {
181	if node == nil {
182		return nil, "", nil, false
183	}
184	if node.height == 0 {
185		if key == node.key {
186			return nil, "", node.value, true
187		} else {
188			return node, "", nil, false
189		}
190	} else {
191		if key < node.key {
192			var newLeftNode *Node
193			newLeftNode, newKey, value, removed = node.getLeftNode().Remove(key)
194			if !removed {
195				return node, "", value, false
196			} else if newLeftNode == nil { // left node held value, was removed
197				return node.rightNode, node.key, value, true
198			}
199			node = node._copy()
200			node.leftNode = newLeftNode
201			node.calcHeightAndSize()
202			node = node.balance()
203			return node, newKey, value, true
204		} else {
205			var newRightNode *Node
206			newRightNode, newKey, value, removed = node.getRightNode().Remove(key)
207			if !removed {
208				return node, "", value, false
209			} else if newRightNode == nil { // right node held value, was removed
210				return node.leftNode, "", value, true
211			}
212			node = node._copy()
213			node.rightNode = newRightNode
214			if newKey != "" {
215				node.key = newKey
216			}
217			node.calcHeightAndSize()
218			node = node.balance()
219			return node, "", value, true
220		}
221	}
222}
223
224func (node *Node) getLeftNode() *Node {
225	return node.leftNode
226}
227
228func (node *Node) getRightNode() *Node {
229	return node.rightNode
230}
231
232// rotateRight performs a right rotation on the node and returns the new root.
233// NOTE: overwrites node
234// TODO: optimize balance & rotate
235func (node *Node) rotateRight() *Node {
236	node = node._copy()
237	l := node.getLeftNode()
238	_l := l._copy()
239
240	_lrCached := _l.rightNode
241	_l.rightNode = node
242	node.leftNode = _lrCached
243
244	node.calcHeightAndSize()
245	_l.calcHeightAndSize()
246
247	return _l
248}
249
250// rotateLeft performs a left rotation on the node and returns the new root.
251// NOTE: overwrites node
252// TODO: optimize balance & rotate
253func (node *Node) rotateLeft() *Node {
254	node = node._copy()
255	r := node.getRightNode()
256	_r := r._copy()
257
258	_rlCached := _r.leftNode
259	_r.leftNode = node
260	node.rightNode = _rlCached
261
262	node.calcHeightAndSize()
263	_r.calcHeightAndSize()
264
265	return _r
266}
267
268// calcHeightAndSize updates the height and size of the node based on its children.
269// NOTE: mutates height and size
270func (node *Node) calcHeightAndSize() {
271	node.height = maxInt8(node.getLeftNode().height, node.getRightNode().height) + 1
272	node.size = node.getLeftNode().size + node.getRightNode().size
273}
274
275// calcBalance calculates the balance factor of the node.
276func (node *Node) calcBalance() int {
277	return int(node.getLeftNode().height) - int(node.getRightNode().height)
278}
279
280// balance balances the subtree rooted at the node and returns the new root.
281// NOTE: assumes that node can be modified
282// TODO: optimize balance & rotate
283func (node *Node) balance() (newSelf *Node) {
284	balance := node.calcBalance()
285	if balance > 1 {
286		if node.getLeftNode().calcBalance() >= 0 {
287			// Left Left Case
288			return node.rotateRight()
289		} else {
290			// Left Right Case
291			left := node.getLeftNode()
292			node.leftNode = left.rotateLeft()
293			return node.rotateRight()
294		}
295	}
296	if balance < -1 {
297		if node.getRightNode().calcBalance() <= 0 {
298			// Right Right Case
299			return node.rotateLeft()
300		} else {
301			// Right Left Case
302			right := node.getRightNode()
303			node.rightNode = right.rotateRight()
304			return node.rotateLeft()
305		}
306	}
307	// Nothing changed
308	return node
309}
310
311// Shortcut for TraverseInRange.
312func (node *Node) Iterate(start, end string, cb func(*Node) bool) bool {
313	return node.TraverseInRange(start, end, true, true, cb)
314}
315
316// Shortcut for TraverseInRange.
317func (node *Node) ReverseIterate(start, end string, cb func(*Node) bool) bool {
318	return node.TraverseInRange(start, end, false, true, cb)
319}
320
321// TraverseInRange traverses all nodes, including inner nodes.
322// Start is inclusive and end is exclusive when ascending,
323// Start and end are inclusive when descending.
324// Empty start and empty end denote no start and no end.
325// If leavesOnly is true, only visit leaf nodes.
326// NOTE: To simulate an exclusive reverse traversal,
327// just append 0x00 to start.
328func (node *Node) TraverseInRange(start, end string, ascending bool, leavesOnly bool, cb func(*Node) bool) bool {
329	if node == nil {
330		return false
331	}
332	afterStart := (start == "" || start < node.key)
333	startOrAfter := (start == "" || start <= node.key)
334	beforeEnd := false
335	if ascending {
336		beforeEnd = (end == "" || node.key < end)
337	} else {
338		beforeEnd = (end == "" || node.key <= end)
339	}
340
341	// Run callback per inner/leaf node.
342	stop := false
343	if (!node.IsLeaf() && !leavesOnly) ||
344		(node.IsLeaf() && startOrAfter && beforeEnd) {
345		stop = cb(node)
346		if stop {
347			return stop
348		}
349	}
350	if node.IsLeaf() {
351		return stop
352	}
353
354	if ascending {
355		// check lower nodes, then higher
356		if afterStart {
357			stop = node.getLeftNode().TraverseInRange(start, end, ascending, leavesOnly, cb)
358		}
359		if stop {
360			return stop
361		}
362		if beforeEnd {
363			stop = node.getRightNode().TraverseInRange(start, end, ascending, leavesOnly, cb)
364		}
365	} else {
366		// check the higher nodes first
367		if beforeEnd {
368			stop = node.getRightNode().TraverseInRange(start, end, ascending, leavesOnly, cb)
369		}
370		if stop {
371			return stop
372		}
373		if afterStart {
374			stop = node.getLeftNode().TraverseInRange(start, end, ascending, leavesOnly, cb)
375		}
376	}
377
378	return stop
379}
380
381// TraverseByOffset traverses all nodes, including inner nodes.
382// A limit of math.MaxInt means no limit.
383func (node *Node) TraverseByOffset(offset, limit int, ascending bool, leavesOnly bool, cb func(*Node) bool) bool {
384	if node == nil {
385		return false
386	}
387
388	// Clamp negative offset to 0; otherwise `delta := first.size - offset`
389	// over-counts and silently drops nodes.
390	if offset < 0 {
391		offset = 0
392	}
393
394	// fast paths. these happen only if TraverseByOffset is called directly on a leaf.
395	if limit <= 0 || offset >= node.size {
396		return false
397	}
398	if node.IsLeaf() {
399		if offset > 0 {
400			return false
401		}
402		return cb(node)
403	}
404
405	// go to the actual recursive function.
406	return node.traverseByOffset(offset, limit, ascending, leavesOnly, cb)
407}
408
409// TraverseByOffset traverses the subtree rooted at the node by offset and limit,
410// in either ascending or descending order, and applies the callback function to each traversed node.
411// If leavesOnly is true, only leaf nodes are visited.
412func (node *Node) traverseByOffset(offset, limit int, ascending bool, leavesOnly bool, cb func(*Node) bool) bool {
413	// caller guarantees: offset < node.size; limit > 0.
414	if !leavesOnly {
415		if cb(node) {
416			return true // Stop traversal if callback returns true
417		}
418	}
419	first, second := node.getLeftNode(), node.getRightNode()
420	if !ascending {
421		first, second = second, first
422	}
423	if first.IsLeaf() {
424		// either run or skip, based on offset
425		if offset > 0 {
426			offset--
427		} else {
428			if cb(first) {
429				return true // Stop traversal if callback returns true
430			}
431			limit--
432			if limit <= 0 {
433				return true // Stop traversal when limit is reached
434			}
435		}
436	} else {
437		// possible cases:
438		// 1 the offset given skips the first node entirely
439		// 2 the offset skips none or part of the first node, but the limit requires some of the second node.
440		// 3 the offset skips none or part of the first node, and the limit stops our search on the first node.
441		if offset >= first.size {
442			offset -= first.size // 1
443		} else {
444			if first.traverseByOffset(offset, limit, ascending, leavesOnly, cb) {
445				return true
446			}
447			// number of leaves which could actually be called from inside
448			delta := first.size - offset
449			offset = 0
450			if delta >= limit {
451				return true // 3
452			}
453			limit -= delta // 2
454		}
455	}
456
457	// because of the caller guarantees and the way we handle the first node,
458	// at this point we know that limit > 0 and there must be some values in
459	// this second node that we include.
460
461	// => if the second node is a leaf, it has to be included.
462	if second.IsLeaf() {
463		return cb(second)
464	}
465	// => if it is not a leaf, it will still be enough to recursively call this
466	// function with the updated offset and limit
467	return second.traverseByOffset(offset, limit, ascending, leavesOnly, cb)
468}
469
470// Only used in testing...
471func (node *Node) lmd() *Node {
472	if node.height == 0 {
473		return node
474	}
475	return node.getLeftNode().lmd()
476}
477
478// Only used in testing...
479func (node *Node) rmd() *Node {
480	if node.height == 0 {
481		return node
482	}
483	return node.getRightNode().rmd()
484}
485
486func maxInt8(a, b int8) int8 {
487	if a > b {
488		return a
489	}
490	return b
491}