From 88fb8cb3aac1e3b393a9f8e88304b78fbe1240da Mon Sep 17 00:00:00 2001
From: Manfred Touron <94029+moul@users.noreply.github.com>
Date: Sat, 21 Dec 2024 01:44:14 +0100
Subject: [PATCH] feat(examples): add p/demo/avl/list (#3324)

Could become the preferred method for managing top-level lists.

It can likely be utilized extensively in the existing codebase,
potentially for most instances involving a top-level slice or those
involving an avl.Tree using a seqid.

---------

Signed-off-by: moul <94029+moul@users.noreply.github.com>
Co-authored-by: Morgan <morgan@morganbaz.com>
---
 examples/gno.land/p/demo/avl/list/gno.mod     |   1 +
 examples/gno.land/p/demo/avl/list/list.gno    | 298 +++++++++++++
 .../gno.land/p/demo/avl/list/list_test.gno    | 409 ++++++++++++++++++
 3 files changed, 708 insertions(+)
 create mode 100644 examples/gno.land/p/demo/avl/list/gno.mod
 create mode 100644 examples/gno.land/p/demo/avl/list/list.gno
 create mode 100644 examples/gno.land/p/demo/avl/list/list_test.gno

diff --git a/examples/gno.land/p/demo/avl/list/gno.mod b/examples/gno.land/p/demo/avl/list/gno.mod
new file mode 100644
index 00000000000..c05923b7708
--- /dev/null
+++ b/examples/gno.land/p/demo/avl/list/gno.mod
@@ -0,0 +1 @@
+module gno.land/p/demo/avl/list
diff --git a/examples/gno.land/p/demo/avl/list/list.gno b/examples/gno.land/p/demo/avl/list/list.gno
new file mode 100644
index 00000000000..0875eb66e01
--- /dev/null
+++ b/examples/gno.land/p/demo/avl/list/list.gno
@@ -0,0 +1,298 @@
+// Package list implements a dynamic list data structure backed by an AVL tree.
+// It provides O(log n) operations for most list operations while maintaining
+// order stability.
+//
+// The list supports various operations including append, get, set, delete,
+// range queries, and iteration. It can store values of any type.
+//
+// Example usage:
+//
+//	// Create a new list and add elements
+//	var l list.List
+//	l.Append(1, 2, 3)
+//
+//	// Get and set elements
+//	value := l.Get(1)  // returns 2
+//	l.Set(1, 42)      // updates index 1 to 42
+//
+//	// Delete elements
+//	l.Delete(0)       // removes first element
+//
+//	// Iterate over elements
+//	l.ForEach(func(index int, value interface{}) bool {
+//	    ufmt.Printf("index %d: %v\n", index, value)
+//	    return false  // continue iteration
+//	})
+//	// Output:
+//	// index 0: 42
+//	// index 1: 3
+//
+//	// Create a list of specific size
+//	l = list.Make(3, "default")  // creates [default, default, default]
+//
+//	// Create a list using a variable declaration
+//	var l2 list.List
+//	l2.Append(4, 5, 6)
+//	println(l2.Len())  // Output: 3
+package list
+
+import (
+	"gno.land/p/demo/avl"
+	"gno.land/p/demo/seqid"
+)
+
+// List represents an ordered sequence of items backed by an AVL tree
+type List struct {
+	tree  avl.Tree
+	idGen seqid.ID
+}
+
+// Len returns the number of elements in the list.
+//
+// Example:
+//
+//	l := list.New()
+//	l.Append(1, 2, 3)
+//	println(l.Len()) // Output: 3
+func (l *List) Len() int {
+	return l.tree.Size()
+}
+
+// Append adds one or more values to the end of the list.
+//
+// Example:
+//
+//	l := list.New()
+//	l.Append(1)        // adds single value
+//	l.Append(2, 3, 4)  // adds multiple values
+//	println(l.Len()) // Output: 4
+func (l *List) Append(values ...interface{}) {
+	for _, v := range values {
+		l.tree.Set(l.idGen.Next().String(), v)
+	}
+}
+
+// Get returns the value at the specified index.
+// Returns nil if index is out of bounds.
+//
+// Example:
+//
+//	l := list.New()
+//	l.Append(1, 2, 3)
+//	println(l.Get(1))    // Output: 2
+//	println(l.Get(-1))   // Output: nil
+//	println(l.Get(999))  // Output: nil
+func (l *List) Get(index int) interface{} {
+	if index < 0 || index >= l.tree.Size() {
+		return nil
+	}
+	_, value := l.tree.GetByIndex(index)
+	return value
+}
+
+// Set updates or appends a value at the specified index.
+// Returns true if the operation was successful, false otherwise.
+// For empty lists, only index 0 is valid (append case).
+//
+// Example:
+//
+//	l := list.New()
+//	l.Append(1, 2, 3)
+//
+//	l.Set(1, 42)      // updates existing index
+//	println(l.Get(1)) // Output: 42
+//
+//	l.Set(3, 4)       // appends at end
+//	println(l.Get(3)) // Output: 4
+//
+//	l.Set(-1, 5)      // invalid index
+//	println(l.Len()) // Output: 4 (list unchanged)
+func (l *List) Set(index int, value interface{}) bool {
+	size := l.tree.Size()
+
+	// Handle empty list case - only allow index 0
+	if size == 0 {
+		if index == 0 {
+			l.Append(value)
+			return true
+		}
+		return false
+	}
+
+	if index < 0 || index > size {
+		return false
+	}
+
+	// If setting at the end (append case)
+	if index == size {
+		l.Append(value)
+		return true
+	}
+
+	// Get the key at the specified index
+	key, _ := l.tree.GetByIndex(index)
+	if key == "" {
+		return false
+	}
+
+	// Update the value at the existing key
+	l.tree.Set(key, value)
+	return true
+}
+
+// Delete removes the element at the specified index.
+// Returns the deleted value and true if successful, nil and false otherwise.
+//
+// Example:
+//
+//	l := list.New()
+//	l.Append(1, 2, 3)
+//
+//	val, ok := l.Delete(1)
+//	println(val, ok)  // Output: 2 true
+//	println(l.Len())  // Output: 2
+//
+//	val, ok = l.Delete(-1)
+//	println(val, ok)  // Output: nil false
+func (l *List) Delete(index int) (interface{}, bool) {
+	size := l.tree.Size()
+	// Always return nil, false for empty list
+	if size == 0 {
+		return nil, false
+	}
+
+	if index < 0 || index >= size {
+		return nil, false
+	}
+
+	key, value := l.tree.GetByIndex(index)
+	if key == "" {
+		return nil, false
+	}
+
+	l.tree.Remove(key)
+	return value, true
+}
+
+// Slice returns a slice of values from startIndex (inclusive) to endIndex (exclusive).
+// Returns nil if the range is invalid.
+//
+// Example:
+//
+//	l := list.New()
+//	l.Append(1, 2, 3, 4, 5)
+//
+//	println(l.Slice(1, 4))   // Output: [2 3 4]
+//	println(l.Slice(-1, 2))  // Output: [1 2]
+//	println(l.Slice(3, 999)) // Output: [4 5]
+//	println(l.Slice(3, 2))   // Output: nil
+func (l *List) Slice(startIndex, endIndex int) []interface{} {
+	size := l.tree.Size()
+
+	// Normalize bounds
+	if startIndex < 0 {
+		startIndex = 0
+	}
+	if endIndex > size {
+		endIndex = size
+	}
+	if startIndex >= endIndex {
+		return nil
+	}
+
+	count := endIndex - startIndex
+	result := make([]interface{}, count)
+
+	i := 0
+	l.tree.IterateByOffset(startIndex, count, func(_ string, value interface{}) bool {
+		result[i] = value
+		i++
+		return false
+	})
+	return result
+}
+
+// ForEach iterates through all elements in the list.
+func (l *List) ForEach(fn func(index int, value interface{}) bool) {
+	if l.tree.Size() == 0 {
+		return
+	}
+
+	index := 0
+	l.tree.IterateByOffset(0, l.tree.Size(), func(_ string, value interface{}) bool {
+		result := fn(index, value)
+		index++
+		return result
+	})
+}
+
+// Clone creates a shallow copy of the list.
+//
+// Example:
+//
+//	l := list.New()
+//	l.Append(1, 2, 3)
+//
+//	clone := l.Clone()
+//	clone.Set(0, 42)
+//
+//	println(l.Get(0))    // Output: 1
+//	println(clone.Get(0)) // Output: 42
+func (l *List) Clone() *List {
+	newList := &List{
+		tree:  avl.Tree{},
+		idGen: l.idGen,
+	}
+
+	size := l.tree.Size()
+	if size == 0 {
+		return newList
+	}
+
+	l.tree.IterateByOffset(0, size, func(_ string, value interface{}) bool {
+		newList.Append(value)
+		return false
+	})
+
+	return newList
+}
+
+// DeleteRange removes elements from startIndex (inclusive) to endIndex (exclusive).
+// Returns the number of elements deleted.
+//
+// Example:
+//
+//	l := list.New()
+//	l.Append(1, 2, 3, 4, 5)
+//
+//	deleted := l.DeleteRange(1, 4)
+//	println(deleted)     // Output: 3
+//	println(l.Range(0, l.Len())) // Output: [1 5]
+func (l *List) DeleteRange(startIndex, endIndex int) int {
+	size := l.tree.Size()
+
+	// Normalize bounds
+	if startIndex < 0 {
+		startIndex = 0
+	}
+	if endIndex > size {
+		endIndex = size
+	}
+	if startIndex >= endIndex {
+		return 0
+	}
+
+	// Collect keys to delete
+	keysToDelete := make([]string, 0, endIndex-startIndex)
+	l.tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, _ interface{}) bool {
+		keysToDelete = append(keysToDelete, key)
+		return false
+	})
+
+	// Delete collected keys
+	for _, key := range keysToDelete {
+		l.tree.Remove(key)
+	}
+
+	return len(keysToDelete)
+}
diff --git a/examples/gno.land/p/demo/avl/list/list_test.gno b/examples/gno.land/p/demo/avl/list/list_test.gno
new file mode 100644
index 00000000000..265fbdb5eb1
--- /dev/null
+++ b/examples/gno.land/p/demo/avl/list/list_test.gno
@@ -0,0 +1,409 @@
+package list
+
+import (
+	"testing"
+)
+
+func TestList_Basic(t *testing.T) {
+	var l List
+
+	// Test empty list
+	if l.Len() != 0 {
+		t.Errorf("new list should be empty, got len %d", l.Len())
+	}
+
+	// Test append and length
+	l.Append(1, 2, 3)
+	if l.Len() != 3 {
+		t.Errorf("expected len 3, got %d", l.Len())
+	}
+
+	// Test get
+	if v := l.Get(0); v != 1 {
+		t.Errorf("expected 1 at index 0, got %v", v)
+	}
+	if v := l.Get(1); v != 2 {
+		t.Errorf("expected 2 at index 1, got %v", v)
+	}
+	if v := l.Get(2); v != 3 {
+		t.Errorf("expected 3 at index 2, got %v", v)
+	}
+
+	// Test out of bounds
+	if v := l.Get(-1); v != nil {
+		t.Errorf("expected nil for negative index, got %v", v)
+	}
+	if v := l.Get(3); v != nil {
+		t.Errorf("expected nil for out of bounds index, got %v", v)
+	}
+}
+
+func TestList_Set(t *testing.T) {
+	var l List
+	l.Append(1, 2, 3)
+
+	// Test valid set within bounds
+	if ok := l.Set(1, 42); !ok {
+		t.Error("Set should return true for valid index")
+	}
+	if v := l.Get(1); v != 42 {
+		t.Errorf("expected 42 after Set, got %v", v)
+	}
+
+	// Test set at size (append)
+	if ok := l.Set(3, 4); !ok {
+		t.Error("Set should return true when appending at size")
+	}
+	if v := l.Get(3); v != 4 {
+		t.Errorf("expected 4 after Set at size, got %v", v)
+	}
+
+	// Test invalid sets
+	if ok := l.Set(-1, 10); ok {
+		t.Error("Set should return false for negative index")
+	}
+	if ok := l.Set(5, 10); ok {
+		t.Error("Set should return false for index > size")
+	}
+
+	// Verify list state hasn't changed after invalid operations
+	expected := []interface{}{1, 42, 3, 4}
+	for i, want := range expected {
+		if got := l.Get(i); got != want {
+			t.Errorf("index %d = %v; want %v", i, got, want)
+		}
+	}
+}
+
+func TestList_Delete(t *testing.T) {
+	var l List
+	l.Append(1, 2, 3)
+
+	// Test valid delete
+	if v, ok := l.Delete(1); !ok || v != 2 {
+		t.Errorf("Delete(1) = %v, %v; want 2, true", v, ok)
+	}
+	if l.Len() != 2 {
+		t.Errorf("expected len 2 after delete, got %d", l.Len())
+	}
+	if v := l.Get(1); v != 3 {
+		t.Errorf("expected 3 at index 1 after delete, got %v", v)
+	}
+
+	// Test invalid delete
+	if v, ok := l.Delete(-1); ok || v != nil {
+		t.Errorf("Delete(-1) = %v, %v; want nil, false", v, ok)
+	}
+	if v, ok := l.Delete(2); ok || v != nil {
+		t.Errorf("Delete(2) = %v, %v; want nil, false", v, ok)
+	}
+}
+
+func TestList_Slice(t *testing.T) {
+	var l List
+	l.Append(1, 2, 3, 4, 5)
+
+	// Test valid ranges
+	values := l.Slice(1, 4)
+	expected := []interface{}{2, 3, 4}
+	if !sliceEqual(values, expected) {
+		t.Errorf("Slice(1,4) = %v; want %v", values, expected)
+	}
+
+	// Test edge cases
+	if values := l.Slice(-1, 2); !sliceEqual(values, []interface{}{1, 2}) {
+		t.Errorf("Slice(-1,2) = %v; want [1 2]", values)
+	}
+	if values := l.Slice(3, 10); !sliceEqual(values, []interface{}{4, 5}) {
+		t.Errorf("Slice(3,10) = %v; want [4 5]", values)
+	}
+	if values := l.Slice(3, 2); values != nil {
+		t.Errorf("Slice(3,2) = %v; want nil", values)
+	}
+}
+
+func TestList_ForEach(t *testing.T) {
+	var l List
+	l.Append(1, 2, 3)
+
+	sum := 0
+	l.ForEach(func(index int, value interface{}) bool {
+		sum += value.(int)
+		return false
+	})
+
+	if sum != 6 {
+		t.Errorf("ForEach sum = %d; want 6", sum)
+	}
+
+	// Test early termination
+	count := 0
+	l.ForEach(func(index int, value interface{}) bool {
+		count++
+		return true // stop after first item
+	})
+
+	if count != 1 {
+		t.Errorf("ForEach early termination count = %d; want 1", count)
+	}
+}
+
+func TestList_Clone(t *testing.T) {
+	var l List
+	l.Append(1, 2, 3)
+
+	clone := l.Clone()
+
+	// Test same length
+	if clone.Len() != l.Len() {
+		t.Errorf("clone.Len() = %d; want %d", clone.Len(), l.Len())
+	}
+
+	// Test same values
+	for i := 0; i < l.Len(); i++ {
+		if clone.Get(i) != l.Get(i) {
+			t.Errorf("clone.Get(%d) = %v; want %v", i, clone.Get(i), l.Get(i))
+		}
+	}
+
+	// Test independence
+	l.Set(0, 42)
+	if clone.Get(0) == l.Get(0) {
+		t.Error("clone should be independent of original")
+	}
+}
+
+func TestList_DeleteRange(t *testing.T) {
+	var l List
+	l.Append(1, 2, 3, 4, 5)
+
+	// Test valid range delete
+	deleted := l.DeleteRange(1, 4)
+	if deleted != 3 {
+		t.Errorf("DeleteRange(1,4) deleted %d elements; want 3", deleted)
+	}
+	if l.Len() != 2 {
+		t.Errorf("after DeleteRange(1,4) len = %d; want 2", l.Len())
+	}
+	expected := []interface{}{1, 5}
+	for i, want := range expected {
+		if got := l.Get(i); got != want {
+			t.Errorf("after DeleteRange(1,4) index %d = %v; want %v", i, got, want)
+		}
+	}
+
+	// Test edge cases
+	l = List{}
+	l.Append(1, 2, 3)
+
+	// Delete with negative start
+	if deleted := l.DeleteRange(-1, 2); deleted != 2 {
+		t.Errorf("DeleteRange(-1,2) deleted %d elements; want 2", deleted)
+	}
+
+	// Delete with end > length
+	l = List{}
+	l.Append(1, 2, 3)
+	if deleted := l.DeleteRange(1, 5); deleted != 2 {
+		t.Errorf("DeleteRange(1,5) deleted %d elements; want 2", deleted)
+	}
+
+	// Delete invalid range
+	if deleted := l.DeleteRange(2, 1); deleted != 0 {
+		t.Errorf("DeleteRange(2,1) deleted %d elements; want 0", deleted)
+	}
+
+	// Delete empty range
+	if deleted := l.DeleteRange(1, 1); deleted != 0 {
+		t.Errorf("DeleteRange(1,1) deleted %d elements; want 0", deleted)
+	}
+}
+
+func TestList_EmptyOperations(t *testing.T) {
+	var l List
+
+	// Operations on empty list
+	if v := l.Get(0); v != nil {
+		t.Errorf("Get(0) on empty list = %v; want nil", v)
+	}
+
+	// Set should work at index 0 for empty list (append case)
+	if ok := l.Set(0, 1); !ok {
+		t.Error("Set(0,1) on empty list = false; want true")
+	}
+	if v := l.Get(0); v != 1 {
+		t.Errorf("Get(0) after Set = %v; want 1", v)
+	}
+
+	l = List{} // Reset to empty list
+	if v, ok := l.Delete(0); ok || v != nil {
+		t.Errorf("Delete(0) on empty list = %v, %v; want nil, false", v, ok)
+	}
+	if values := l.Slice(0, 1); values != nil {
+		t.Errorf("Range(0,1) on empty list = %v; want nil", values)
+	}
+}
+
+func TestList_DifferentTypes(t *testing.T) {
+	var l List
+
+	// Test with different types
+	l.Append(42, "hello", true, 3.14)
+
+	if v := l.Get(0).(int); v != 42 {
+		t.Errorf("Get(0) = %v; want 42", v)
+	}
+	if v := l.Get(1).(string); v != "hello" {
+		t.Errorf("Get(1) = %v; want 'hello'", v)
+	}
+	if v := l.Get(2).(bool); !v {
+		t.Errorf("Get(2) = %v; want true", v)
+	}
+	if v := l.Get(3).(float64); v != 3.14 {
+		t.Errorf("Get(3) = %v; want 3.14", v)
+	}
+}
+
+func TestList_LargeOperations(t *testing.T) {
+	var l List
+
+	// Test with larger number of elements
+	n := 1000
+	for i := 0; i < n; i++ {
+		l.Append(i)
+	}
+
+	if l.Len() != n {
+		t.Errorf("Len() = %d; want %d", l.Len(), n)
+	}
+
+	// Test range on large list
+	values := l.Slice(n-3, n)
+	expected := []interface{}{n - 3, n - 2, n - 1}
+	if !sliceEqual(values, expected) {
+		t.Errorf("Range(%d,%d) = %v; want %v", n-3, n, values, expected)
+	}
+
+	// Test large range deletion
+	deleted := l.DeleteRange(100, 900)
+	if deleted != 800 {
+		t.Errorf("DeleteRange(100,900) = %d; want 800", deleted)
+	}
+	if l.Len() != 200 {
+		t.Errorf("Len() after large delete = %d; want 200", l.Len())
+	}
+}
+
+func TestList_ChainedOperations(t *testing.T) {
+	var l List
+
+	// Test sequence of operations
+	l.Append(1, 2, 3)
+	l.Delete(1)
+	l.Append(4)
+	l.Set(1, 5)
+
+	expected := []interface{}{1, 5, 4}
+	for i, want := range expected {
+		if got := l.Get(i); got != want {
+			t.Errorf("index %d = %v; want %v", i, got, want)
+		}
+	}
+}
+
+func TestList_RangeEdgeCases(t *testing.T) {
+	var l List
+	l.Append(1, 2, 3, 4, 5)
+
+	// Test various edge cases for Range
+	cases := []struct {
+		start, end int
+		want       []interface{}
+	}{
+		{-10, 2, []interface{}{1, 2}},
+		{3, 10, []interface{}{4, 5}},
+		{0, 0, nil},
+		{5, 5, nil},
+		{4, 3, nil},
+		{-1, -1, nil},
+	}
+
+	for _, tc := range cases {
+		got := l.Slice(tc.start, tc.end)
+		if !sliceEqual(got, tc.want) {
+			t.Errorf("Slice(%d,%d) = %v; want %v", tc.start, tc.end, got, tc.want)
+		}
+	}
+}
+
+func TestList_IndexConsistency(t *testing.T) {
+	var l List
+
+	// Initial additions
+	l.Append(1, 2, 3, 4, 5) // [1,2,3,4,5]
+
+	// Delete from middle
+	l.Delete(2) // [1,2,4,5]
+
+	// Add more elements
+	l.Append(6, 7) // [1,2,4,5,6,7]
+
+	// Delete range from middle
+	l.DeleteRange(1, 4) // [1,6,7]
+
+	// Add more elements
+	l.Append(8, 9, 10) // [1,6,7,8,9,10]
+
+	// Verify sequence is continuous
+	expected := []interface{}{1, 6, 7, 8, 9, 10}
+	for i, want := range expected {
+		if got := l.Get(i); got != want {
+			t.Errorf("index %d = %v; want %v", i, got, want)
+		}
+	}
+
+	// Verify no extra elements exist
+	if l.Len() != len(expected) {
+		t.Errorf("length = %d; want %d", l.Len(), len(expected))
+	}
+
+	// Verify all indices are accessible
+	allValues := l.Slice(0, l.Len())
+	if !sliceEqual(allValues, expected) {
+		t.Errorf("Slice(0, Len()) = %v; want %v", allValues, expected)
+	}
+
+	// Verify no gaps in iteration
+	var iteratedValues []interface{}
+	var indices []int
+	l.ForEach(func(index int, value interface{}) bool {
+		iteratedValues = append(iteratedValues, value)
+		indices = append(indices, index)
+		return false
+	})
+
+	// Check values from iteration
+	if !sliceEqual(iteratedValues, expected) {
+		t.Errorf("ForEach values = %v; want %v", iteratedValues, expected)
+	}
+
+	// Check indices are sequential
+	for i, idx := range indices {
+		if idx != i {
+			t.Errorf("ForEach index %d = %d; want %d", i, idx, i)
+		}
+	}
+}
+
+// Helper function to compare slices
+func sliceEqual(a, b []interface{}) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	for i := range a {
+		if a[i] != b[i] {
+			return false
+		}
+	}
+	return true
+}