ulist.gno

10.68 Kb ยท 439 lines
  1// Package ulist provides an append-only list implementation using a binary tree structure,
  2// optimized for scenarios requiring sequential inserts with auto-incrementing indices.
  3//
  4// The implementation uses a binary tree where new elements are added by following a path
  5// determined by the binary representation of the index. This provides automatic balancing
  6// for append operations without requiring any balancing logic.
  7//
  8// Unlike the AVL tree-based list implementation (p/demo/avl/list), ulist is specifically
  9// designed for append-only operations and does not require rebalancing. This makes it more
 10// efficient for sequential inserts but less flexible for general-purpose list operations.
 11//
 12// Key differences from AVL list:
 13// * Append-only design (no arbitrary inserts)
 14// * No tree rebalancing needed
 15// * Simpler implementation
 16// * More memory efficient for sequential operations
 17// * Less flexible than AVL (no arbitrary inserts/reordering)
 18//
 19// Key characteristics:
 20// * O(log n) append and access operations
 21// * Perfect balance for power-of-2 sizes
 22// * No balancing needed
 23// * Memory efficient
 24// * Natural support for range queries
 25// * Support for soft deletion of elements
 26// * Forward and reverse iteration capabilities
 27// * Offset-based iteration with count control
 28package ulist
 29
 30// TODO: Make avl/pager compatible in some way. Explain the limitations (not always 10 items because of nil ones).
 31// TODO: Use this ulist in moul/collection for the primary index.
 32// TODO: Consider adding a "compact" method that removes nil nodes.
 33// TODO: Benchmarks.
 34
 35import (
 36	"errors"
 37)
 38
 39// List represents an append-only binary tree list
 40type List struct {
 41	root       *treeNode
 42	totalSize  int
 43	activeSize int
 44}
 45
 46// Entry represents a key-value pair in the list, where Index is the position
 47// and Value is the stored data
 48type Entry struct {
 49	Index int
 50	Value any
 51}
 52
 53// treeNode represents a node in the binary tree
 54type treeNode struct {
 55	data  any
 56	left  *treeNode
 57	right *treeNode
 58}
 59
 60// Error variables
 61var (
 62	ErrOutOfBounds = errors.New("index out of bounds")
 63	ErrDeleted     = errors.New("element already deleted")
 64)
 65
 66// New creates a new empty List instance
 67func New() *List {
 68	return &List{}
 69}
 70
 71// Append adds one or more values to the end of the list.
 72// Values are added sequentially, and the list grows automatically.
 73func (l *List) Append(values ...any) {
 74	for _, value := range values {
 75		index := l.totalSize
 76		node := l.findNode(index, true)
 77		node.data = value
 78		l.totalSize++
 79		l.activeSize++
 80	}
 81}
 82
 83// Get retrieves the value at the specified index.
 84// Returns nil if the index is out of bounds or if the element was deleted.
 85func (l *List) Get(index int) any {
 86	node := l.findNode(index, false)
 87	if node == nil {
 88		return nil
 89	}
 90	return node.data
 91}
 92
 93// Delete marks the elements at the specified indices as deleted.
 94// Returns ErrOutOfBounds if any index is invalid or ErrDeleted if
 95// the element was already deleted.
 96func (l *List) Delete(indices ...int) error {
 97	if len(indices) == 0 {
 98		return nil
 99	}
100	if l == nil || l.totalSize == 0 {
101		return ErrOutOfBounds
102	}
103
104	for _, index := range indices {
105		if index < 0 || index >= l.totalSize {
106			return ErrOutOfBounds
107		}
108
109		node := l.findNode(index, false)
110		if node == nil || node.data == nil {
111			return ErrDeleted
112		}
113		node.data = nil
114		l.activeSize--
115	}
116
117	return nil
118}
119
120// Set updates or restores a value at the specified index if within bounds
121// Returns ErrOutOfBounds if the index is invalid
122func (l *List) Set(index int, value any) error {
123	if l == nil || index < 0 || index >= l.totalSize {
124		return ErrOutOfBounds
125	}
126
127	node := l.findNode(index, false)
128	if node == nil {
129		return ErrOutOfBounds
130	}
131
132	// If this is restoring a deleted element
133	if value != nil && node.data == nil {
134		l.activeSize++
135	}
136
137	// If this is deleting an element
138	if value == nil && node.data != nil {
139		l.activeSize--
140	}
141
142	node.data = value
143	return nil
144}
145
146// Size returns the number of active (non-deleted) elements in the list
147func (l *List) Size() int {
148	if l == nil {
149		return 0
150	}
151	return l.activeSize
152}
153
154// TotalSize returns the total number of elements ever added to the list,
155// including deleted elements
156func (l *List) TotalSize() int {
157	if l == nil {
158		return 0
159	}
160	return l.totalSize
161}
162
163// IterCbFn is a callback function type used in iteration methods.
164// Return true to stop iteration, false to continue.
165type IterCbFn func(index int, value any) bool
166
167// Iterator performs iteration between start and end indices, calling cb for each entry.
168// If start > end, iteration is performed in reverse order.
169// Returns true if iteration was stopped early by the callback returning true.
170// Skips deleted elements.
171func (l *List) Iterator(start, end int, cb IterCbFn) bool {
172	// For empty list or invalid range
173	if l == nil || l.totalSize == 0 {
174		return false
175	}
176	if start < 0 && end < 0 {
177		return false
178	}
179	if start >= l.totalSize && end >= l.totalSize {
180		return false
181	}
182
183	// Normalize indices
184	if start < 0 {
185		start = 0
186	}
187	if end < 0 {
188		end = 0
189	}
190	if end >= l.totalSize {
191		end = l.totalSize - 1
192	}
193	if start >= l.totalSize {
194		start = l.totalSize - 1
195	}
196
197	// Handle reverse iteration
198	if start > end {
199		for i := start; i >= end; i-- {
200			val := l.Get(i)
201			if val != nil {
202				if cb(i, val) {
203					return true
204				}
205			}
206		}
207		return false
208	}
209
210	// Handle forward iteration
211	for i := start; i <= end; i++ {
212		val := l.Get(i)
213		if val != nil {
214			if cb(i, val) {
215				return true
216			}
217		}
218	}
219	return false
220}
221
222// IteratorByOffset performs iteration starting from offset for count elements.
223// If count is positive, iterates forward; if negative, iterates backward.
224// The iteration stops after abs(count) elements or when reaching list bounds.
225// Skips deleted elements.
226func (l *List) IteratorByOffset(offset int, count int, cb IterCbFn) bool {
227	if count == 0 || l == nil || l.totalSize == 0 {
228		return false
229	}
230
231	// Normalize offset
232	if offset < 0 {
233		offset = 0
234	}
235	if offset >= l.totalSize {
236		offset = l.totalSize - 1
237	}
238
239	// Determine end based on count direction
240	var end int
241	if count > 0 {
242		end = l.totalSize - 1
243	} else {
244		end = 0
245	}
246
247	wrapperReturned := false
248
249	// Wrap the callback to limit iterations
250	remaining := abs(count)
251	wrapper := func(index int, value any) bool {
252		if remaining <= 0 {
253			wrapperReturned = true
254			return true
255		}
256		remaining--
257		return cb(index, value)
258	}
259	ret := l.Iterator(offset, end, wrapper)
260	if wrapperReturned {
261		return false
262	}
263	return ret
264}
265
266// abs returns the absolute value of x
267func abs(x int) int {
268	if x < 0 {
269		return -x
270	}
271	return x
272}
273
274// findNode locates or creates a node at the given index in the binary tree.
275// The tree is structured such that the path to a node is determined by the binary
276// representation of the index. For example, a tree with 15 elements would look like:
277//
278//	          0
279//	       /      \
280//	     1         2
281//	   /   \     /   \
282//	  3    4    5     6
283//	 / \  / \  / \   / \
284//	7  8 9 10 11 12 13 14
285//
286// To find index 13 (binary 1101):
287// 1. Start at root (0)
288// 2. Calculate bits needed (4 bits for index 13)
289// 3. Skip the highest bit position and start from bits-2
290// 4. Read bits from left to right:
291//   - 1 -> go right to 2
292//   - 1 -> go right to 6
293//   - 0 -> go left to 13
294//
295// Special cases:
296// - Index 0 always returns the root node
297// - For create=true, missing nodes are created along the path
298// - For create=false, returns nil if any node is missing
299func (l *List) findNode(index int, create bool) *treeNode {
300	// For read operations, check bounds strictly
301	if !create && (l == nil || index < 0 || index >= l.totalSize) {
302		return nil
303	}
304
305	// For create operations, allow index == totalSize for append
306	if create && (l == nil || index < 0 || index > l.totalSize) {
307		return nil
308	}
309
310	// Initialize root if needed
311	if l.root == nil {
312		if !create {
313			return nil
314		}
315		l.root = &treeNode{}
316		return l.root
317	}
318
319	node := l.root
320
321	// Special case for root node
322	if index == 0 {
323		return node
324	}
325
326	// Calculate the number of bits needed (inline highestBit logic)
327	bits := 0
328	n := index + 1
329	for n > 0 {
330		n >>= 1
331		bits++
332	}
333
334	// Start from the second highest bit
335	for level := bits - 2; level >= 0; level-- {
336		bit := (index & (1 << uint(level))) != 0
337
338		if bit {
339			if node.right == nil {
340				if !create {
341					return nil
342				}
343				node.right = &treeNode{}
344			}
345			node = node.right
346		} else {
347			if node.left == nil {
348				if !create {
349					return nil
350				}
351				node.left = &treeNode{}
352			}
353			node = node.left
354		}
355	}
356
357	return node
358}
359
360// MustDelete deletes elements at the specified indices.
361// Panics if any index is invalid or if any element was already deleted.
362func (l *List) MustDelete(indices ...int) {
363	if err := l.Delete(indices...); err != nil {
364		panic(err)
365	}
366}
367
368// MustGet retrieves the value at the specified index.
369// Panics if the index is out of bounds or if the element was deleted.
370func (l *List) MustGet(index int) any {
371	if l == nil || index < 0 || index >= l.totalSize {
372		panic(ErrOutOfBounds)
373	}
374	value := l.Get(index)
375	if value == nil {
376		panic(ErrDeleted)
377	}
378	return value
379}
380
381// MustSet updates or restores a value at the specified index.
382// Panics if the index is out of bounds.
383func (l *List) MustSet(index int, value any) {
384	if err := l.Set(index, value); err != nil {
385		panic(err)
386	}
387}
388
389// GetRange returns a slice of Entry containing elements between start and end indices.
390// If start > end, elements are returned in reverse order.
391// Deleted elements are skipped.
392func (l *List) GetRange(start, end int) []Entry {
393	var entries []Entry
394	l.Iterator(start, end, func(index int, value any) bool {
395		entries = append(entries, Entry{Index: index, Value: value})
396		return false
397	})
398	return entries
399}
400
401// GetByOffset returns a slice of Entry starting from offset for count elements.
402// If count is positive, returns elements forward; if negative, returns elements backward.
403// The operation stops after abs(count) elements or when reaching list bounds.
404// Deleted elements are skipped.
405func (l *List) GetByOffset(offset int, count int) []Entry {
406	var entries []Entry
407	l.IteratorByOffset(offset, count, func(index int, value any) bool {
408		entries = append(entries, Entry{Index: index, Value: value})
409		return false
410	})
411	return entries
412}
413
414// IList defines the interface for an ulist.List compatible structure.
415type IList interface {
416	// Basic operations
417	Append(values ...any)
418	Get(index int) any
419	Delete(indices ...int) error
420	Size() int
421	TotalSize() int
422	Set(index int, value any) error
423
424	// Must variants that panic instead of returning errors
425	MustDelete(indices ...int)
426	MustGet(index int) any
427	MustSet(index int, value any)
428
429	// Range operations
430	GetRange(start, end int) []Entry
431	GetByOffset(offset int, count int) []Entry
432
433	// Iterator operations
434	Iterator(start, end int, cb IterCbFn) bool
435	IteratorByOffset(offset int, count int, cb IterCbFn) bool
436}
437
438// Verify that List implements IList
439var _ IList = (*List)(nil)