From 1d25587869a1c26b044f340b74476d733086f868 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 11 Dec 2024 08:26:25 +0100 Subject: [PATCH 01/10] feat(examples): add p/demo/avl/list Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/list/gno.mod | 1 + examples/gno.land/p/demo/avl/list/list.gno | 385 +++++++++++++++++ .../gno.land/p/demo/avl/list/list_test.gno | 400 ++++++++++++++++++ 3 files changed, 786 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..c11bb05f6a8 --- /dev/null +++ b/examples/gno.land/p/demo/avl/list/list.gno @@ -0,0 +1,385 @@ +// 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 +// l := list.New() +// 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) +// ufmt.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 +} + +// New creates a new empty list. +// +// Example: +// +// l := list.New() +// l.Append(1, 2, 3) +// ufmt.Println(l.Len()) // Output: 3 +func New() *List { + return &List{} +} + +// Make creates a new list with the specified length, filled with the given default value. +// If defaultValue is nil, the list will be filled with nil values. +// Returns an empty list if length is negative. +// +// Example: +// +// l := list.Make(3, "default") +// ufmt.Println(l.Get(0)) // Output: "default" +// ufmt.Println(l.Len()) // Output: 3 +// +// // With nil default value +// l2 := list.Make(2, nil) +// ufmt.Println(l2.Get(0)) // Output: nil +func Make(length int, defaultValue interface{}) *List { + if length < 0 { + return New() + } + l := New() + for i := 0; i < length; i++ { + l.Append(defaultValue) + } + return l +} + +// Len returns the number of elements in the list. +// +// Example: +// +// l := list.New() +// l.Append(1, 2, 3) +// ufmt.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 +// ufmt.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) +// ufmt.Println(l.Get(1)) // Output: 2 +// ufmt.Println(l.Get(-1)) // Output: nil +// ufmt.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 +// ufmt.Println(l.Get(1)) // Output: 42 +// +// l.Set(3, 4) // appends at end +// ufmt.Println(l.Get(3)) // Output: 4 +// +// l.Set(-1, 5) // invalid index +// ufmt.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) +// ufmt.Println(val, ok) // Output: 2 true +// ufmt.Println(l.Len()) // Output: 2 +// +// val, ok = l.Delete(-1) +// ufmt.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 +} + +// Range 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) +// +// ufmt.Println(l.Range(1, 4)) // Output: [2 3 4] +// ufmt.Println(l.Range(-1, 2)) // Output: [1 2] +// ufmt.Println(l.Range(3, 999)) // Output: [4 5] +// ufmt.Println(l.Range(3, 2)) // Output: nil +func (l *List) Range(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) + + // Fill the slice from end to start to maintain correct order + i := count - 1 + l.tree.IterateByOffset(size-endIndex, count, func(_ string, value interface{}) bool { + result[i] = value + i-- + return false + }) + return result +} + +// ForEach iterates through all elements in the list. +// The callback function receives the index and value of each element. +// Return true from the callback to stop iteration. +// +// Example: +// +// l := list.New() +// l.Append(1, 2, 3) +// +// sum := 0 +// l.ForEach(func(index int, value interface{}) bool { +// sum += value.(int) +// return false // continue iteration +// }) +// ufmt.Println(sum) // Output: 6 +// +// // Early stop +// l.ForEach(func(index int, value interface{}) bool { +// if value.(int) > 2 { +// return true // stop iteration +// } +// return false +// }) +func (l *List) ForEach(fn func(index int, value interface{}) bool) { + size := l.tree.Size() + values := make([]interface{}, size) + + // First collect values in reverse order + i := size - 1 + l.tree.IterateByOffset(0, size, func(_ string, value interface{}) bool { + values[i] = value + i-- + return false + }) + + // Then iterate through them in correct order + for i := 0; i < size; i++ { + if fn(i, values[i]) { + return + } + } +} + +// Clone creates a deep copy of the list. +// +// Example: +// +// l := list.New() +// l.Append(1, 2, 3) +// +// clone := l.Clone() +// clone.Set(0, 42) +// +// ufmt.Println(l.Get(0)) // Output: 1 +// ufmt.Println(clone.Get(0)) // Output: 42 +func (l *List) Clone() *List { + newList := &List{ + tree: avl.Tree{}, // Create a new empty tree + idGen: l.idGen, // Copy the current ID generator state + } + + size := l.tree.Size() + if size == 0 { + return newList + } + + // Collect values in correct order + values := make([]interface{}, size) + i := size - 1 + l.tree.IterateByOffset(0, size, func(_ string, value interface{}) bool { + values[i] = value + i-- + return false + }) + + // Add values to new list in correct order + for _, value := range values { + newList.Append(value) + } + + 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) +// ufmt.Println(deleted) // Output: 3 +// ufmt.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(size-endIndex, 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) +} + +// Clear removes all elements from the list. +// +// Example: +// +// l := list.New() +// l.Append(1, 2, 3) +// l.Clear() +// ufmt.Println(l.Len()) // Output: 0 +func (l *List) Clear() { + l.tree = avl.Tree{} // Reset to empty tree + l.idGen = seqid.ID(0) // Reset ID generator +} 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..2e7cc71a622 --- /dev/null +++ b/examples/gno.land/p/demo/avl/list/list_test.gno @@ -0,0 +1,400 @@ +package list + +import ( + "testing" +) + +func TestList_Basic(t *testing.T) { + l := New() + + // 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) { + l := New() + 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) { + l := New() + 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_Range(t *testing.T) { + l := New() + l.Append(1, 2, 3, 4, 5) + + // Test valid ranges + values := l.Range(1, 4) + expected := []interface{}{2, 3, 4} + if !sliceEqual(values, expected) { + t.Errorf("Range(1,4) = %v; want %v", values, expected) + } + + // Test edge cases + if values := l.Range(-1, 2); !sliceEqual(values, []interface{}{1, 2}) { + t.Errorf("Range(-1,2) = %v; want [1 2]", values) + } + if values := l.Range(3, 10); !sliceEqual(values, []interface{}{4, 5}) { + t.Errorf("Range(3,10) = %v; want [4 5]", values) + } + if values := l.Range(3, 2); values != nil { + t.Errorf("Range(3,2) = %v; want nil", values) + } +} + +func TestList_ForEach(t *testing.T) { + l := New() + 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) { + l := New() + 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) { + l := New() + 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 = New() + 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 = New() + 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) { + l := New() + + // 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 = New() // 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.Range(0, 1); values != nil { + t.Errorf("Range(0,1) on empty list = %v; want nil", values) + } +} + +func TestList_DifferentTypes(t *testing.T) { + l := New() + + // 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) { + l := New() + + // 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.Range(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) { + l := New() + + // 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) { + l := New() + 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.Range(tc.start, tc.end) + if !sliceEqual(got, tc.want) { + t.Errorf("Range(%d,%d) = %v; want %v", tc.start, tc.end, got, tc.want) + } + } +} + +func TestList_Clear(t *testing.T) { + l := New() + l.Append(1, 2, 3) + + // Test clear + l.Clear() + if l.Len() != 0 { + t.Errorf("after Clear(), Len() = %d; want 0", l.Len()) + } + + // Test that we can still append after clearing + l.Append(4) + if l.Len() != 1 { + t.Errorf("after Clear() and Append(), Len() = %d; want 1", l.Len()) + } + if v := l.Get(0); v != 4 { + t.Errorf("after Clear() and Append(), Get(0) = %v; want 4", v) + } +} + +func TestList_Make(t *testing.T) { + // Test with default value + l := Make(3, "default") + if l.Len() != 3 { + t.Errorf("Make(3, default) Len() = %d; want 3", l.Len()) + } + for i := 0; i < l.Len(); i++ { + if v := l.Get(i); v != "default" { + t.Errorf("index %d = %v; want 'default'", i, v) + } + } + + // Test with nil default value + l = Make(2, nil) + if l.Len() != 2 { + t.Errorf("Make(2, nil) Len() = %d; want 2", l.Len()) + } + for i := 0; i < l.Len(); i++ { + if v := l.Get(i); v != nil { + t.Errorf("index %d = %v; want nil", i, v) + } + } + + // Test with negative length + l = Make(-1, "default") + if l.Len() != 0 { + t.Errorf("Make(-1, default) Len() = %d; want 0", l.Len()) + } +} + +// 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 +} From 5e2c655e4f09046fa175615ca5c189aba1d6189c Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:28:11 +0100 Subject: [PATCH 02/10] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/list/list.gno | 52 +++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/examples/gno.land/p/demo/avl/list/list.gno b/examples/gno.land/p/demo/avl/list/list.gno index c11bb05f6a8..25842438879 100644 --- a/examples/gno.land/p/demo/avl/list/list.gno +++ b/examples/gno.land/p/demo/avl/list/list.gno @@ -33,7 +33,7 @@ // // Create a list using a variable declaration // var l2 list.List // l2.Append(4, 5, 6) -// ufmt.Println(l2.Len()) // Output: 3 +// println(l2.Len()) // Output: 3 package list import ( @@ -53,7 +53,7 @@ type List struct { // // l := list.New() // l.Append(1, 2, 3) -// ufmt.Println(l.Len()) // Output: 3 +// println(l.Len()) // Output: 3 func New() *List { return &List{} } @@ -65,12 +65,12 @@ func New() *List { // Example: // // l := list.Make(3, "default") -// ufmt.Println(l.Get(0)) // Output: "default" -// ufmt.Println(l.Len()) // Output: 3 +// println(l.Get(0)) // Output: "default" +// println(l.Len()) // Output: 3 // // // With nil default value // l2 := list.Make(2, nil) -// ufmt.Println(l2.Get(0)) // Output: nil +// println(l2.Get(0)) // Output: nil func Make(length int, defaultValue interface{}) *List { if length < 0 { return New() @@ -88,7 +88,7 @@ func Make(length int, defaultValue interface{}) *List { // // l := list.New() // l.Append(1, 2, 3) -// ufmt.Println(l.Len()) // Output: 3 +// println(l.Len()) // Output: 3 func (l *List) Len() int { return l.tree.Size() } @@ -100,7 +100,7 @@ func (l *List) Len() int { // l := list.New() // l.Append(1) // adds single value // l.Append(2, 3, 4) // adds multiple values -// ufmt.Println(l.Len()) // Output: 4 +// println(l.Len()) // Output: 4 func (l *List) Append(values ...interface{}) { for _, v := range values { l.tree.Set(l.idGen.Next().String(), v) @@ -114,9 +114,9 @@ func (l *List) Append(values ...interface{}) { // // l := list.New() // l.Append(1, 2, 3) -// ufmt.Println(l.Get(1)) // Output: 2 -// ufmt.Println(l.Get(-1)) // Output: nil -// ufmt.Println(l.Get(999)) // Output: nil +// 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 @@ -135,13 +135,13 @@ func (l *List) Get(index int) interface{} { // l.Append(1, 2, 3) // // l.Set(1, 42) // updates existing index -// ufmt.Println(l.Get(1)) // Output: 42 +// println(l.Get(1)) // Output: 42 // // l.Set(3, 4) // appends at end -// ufmt.Println(l.Get(3)) // Output: 4 +// println(l.Get(3)) // Output: 4 // // l.Set(-1, 5) // invalid index -// ufmt.Println(l.Len()) // Output: 4 (list unchanged) +// println(l.Len()) // Output: 4 (list unchanged) func (l *List) Set(index int, value interface{}) bool { size := l.tree.Size() @@ -184,11 +184,11 @@ func (l *List) Set(index int, value interface{}) bool { // l.Append(1, 2, 3) // // val, ok := l.Delete(1) -// ufmt.Println(val, ok) // Output: 2 true -// ufmt.Println(l.Len()) // Output: 2 +// println(val, ok) // Output: 2 true +// println(l.Len()) // Output: 2 // // val, ok = l.Delete(-1) -// ufmt.Println(val, ok) // Output: nil false +// 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 @@ -217,10 +217,10 @@ func (l *List) Delete(index int) (interface{}, bool) { // l := list.New() // l.Append(1, 2, 3, 4, 5) // -// ufmt.Println(l.Range(1, 4)) // Output: [2 3 4] -// ufmt.Println(l.Range(-1, 2)) // Output: [1 2] -// ufmt.Println(l.Range(3, 999)) // Output: [4 5] -// ufmt.Println(l.Range(3, 2)) // Output: nil +// println(l.Range(1, 4)) // Output: [2 3 4] +// println(l.Range(-1, 2)) // Output: [1 2] +// println(l.Range(3, 999)) // Output: [4 5] +// println(l.Range(3, 2)) // Output: nil func (l *List) Range(startIndex, endIndex int) []interface{} { size := l.tree.Size() @@ -262,7 +262,7 @@ func (l *List) Range(startIndex, endIndex int) []interface{} { // sum += value.(int) // return false // continue iteration // }) -// ufmt.Println(sum) // Output: 6 +// println(sum) // Output: 6 // // // Early stop // l.ForEach(func(index int, value interface{}) bool { @@ -301,8 +301,8 @@ func (l *List) ForEach(fn func(index int, value interface{}) bool) { // clone := l.Clone() // clone.Set(0, 42) // -// ufmt.Println(l.Get(0)) // Output: 1 -// ufmt.Println(clone.Get(0)) // Output: 42 +// println(l.Get(0)) // Output: 1 +// println(clone.Get(0)) // Output: 42 func (l *List) Clone() *List { newList := &List{ tree: avl.Tree{}, // Create a new empty tree @@ -340,8 +340,8 @@ func (l *List) Clone() *List { // l.Append(1, 2, 3, 4, 5) // // deleted := l.DeleteRange(1, 4) -// ufmt.Println(deleted) // Output: 3 -// ufmt.Println(l.Range(0, l.Len())) // Output: [1 5] +// 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() @@ -378,7 +378,7 @@ func (l *List) DeleteRange(startIndex, endIndex int) int { // l := list.New() // l.Append(1, 2, 3) // l.Clear() -// ufmt.Println(l.Len()) // Output: 0 +// println(l.Len()) // Output: 0 func (l *List) Clear() { l.tree = avl.Tree{} // Reset to empty tree l.idGen = seqid.ID(0) // Reset ID generator From 6811a308b8fc4bbd9a84c35bb3a6027ec3bf1288 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:29:24 +0100 Subject: [PATCH 03/10] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- .../gno.land/p/demo/avl/list/list_test.gno | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/examples/gno.land/p/demo/avl/list/list_test.gno b/examples/gno.land/p/demo/avl/list/list_test.gno index 2e7cc71a622..c8afc0a3756 100644 --- a/examples/gno.land/p/demo/avl/list/list_test.gno +++ b/examples/gno.land/p/demo/avl/list/list_test.gno @@ -386,6 +386,65 @@ func TestList_Make(t *testing.T) { } } +func TestList_IndexConsistency(t *testing.T) { + l := New() + + // 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.Range(0, l.Len()) + if !sliceEqual(allValues, expected) { + t.Errorf("Range(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) { From b482ec1252a7b04bec9613439f8f44cfb1443c7d Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:32:45 +0100 Subject: [PATCH 04/10] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/list/list.gno | 62 ++-------- .../gno.land/p/demo/avl/list/list_test.gno | 112 +++++------------- 2 files changed, 38 insertions(+), 136 deletions(-) diff --git a/examples/gno.land/p/demo/avl/list/list.gno b/examples/gno.land/p/demo/avl/list/list.gno index 25842438879..b0c1fd1012d 100644 --- a/examples/gno.land/p/demo/avl/list/list.gno +++ b/examples/gno.land/p/demo/avl/list/list.gno @@ -8,7 +8,7 @@ // Example usage: // // // Create a new list and add elements -// l := list.New() +// var l list.List // l.Append(1, 2, 3) // // // Get and set elements @@ -47,41 +47,6 @@ type List struct { idGen seqid.ID } -// New creates a new empty list. -// -// Example: -// -// l := list.New() -// l.Append(1, 2, 3) -// println(l.Len()) // Output: 3 -func New() *List { - return &List{} -} - -// Make creates a new list with the specified length, filled with the given default value. -// If defaultValue is nil, the list will be filled with nil values. -// Returns an empty list if length is negative. -// -// Example: -// -// l := list.Make(3, "default") -// println(l.Get(0)) // Output: "default" -// println(l.Len()) // Output: 3 -// -// // With nil default value -// l2 := list.Make(2, nil) -// println(l2.Get(0)) // Output: nil -func Make(length int, defaultValue interface{}) *List { - if length < 0 { - return New() - } - l := New() - for i := 0; i < length; i++ { - l.Append(defaultValue) - } - return l -} - // Len returns the number of elements in the list. // // Example: @@ -209,7 +174,7 @@ func (l *List) Delete(index int) (interface{}, bool) { return value, true } -// Range returns a slice of values from startIndex (inclusive) to endIndex (exclusive). +// Slice returns a slice of values from startIndex (inclusive) to endIndex (exclusive). // Returns nil if the range is invalid. // // Example: @@ -217,11 +182,11 @@ func (l *List) Delete(index int) (interface{}, bool) { // l := list.New() // l.Append(1, 2, 3, 4, 5) // -// println(l.Range(1, 4)) // Output: [2 3 4] -// println(l.Range(-1, 2)) // Output: [1 2] -// println(l.Range(3, 999)) // Output: [4 5] -// println(l.Range(3, 2)) // Output: nil -func (l *List) Range(startIndex, endIndex int) []interface{} { +// 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 @@ -370,16 +335,3 @@ func (l *List) DeleteRange(startIndex, endIndex int) int { return len(keysToDelete) } - -// Clear removes all elements from the list. -// -// Example: -// -// l := list.New() -// l.Append(1, 2, 3) -// l.Clear() -// println(l.Len()) // Output: 0 -func (l *List) Clear() { - l.tree = avl.Tree{} // Reset to empty tree - l.idGen = seqid.ID(0) // Reset ID generator -} diff --git a/examples/gno.land/p/demo/avl/list/list_test.gno b/examples/gno.land/p/demo/avl/list/list_test.gno index c8afc0a3756..265fbdb5eb1 100644 --- a/examples/gno.land/p/demo/avl/list/list_test.gno +++ b/examples/gno.land/p/demo/avl/list/list_test.gno @@ -5,7 +5,7 @@ import ( ) func TestList_Basic(t *testing.T) { - l := New() + var l List // Test empty list if l.Len() != 0 { @@ -39,7 +39,7 @@ func TestList_Basic(t *testing.T) { } func TestList_Set(t *testing.T) { - l := New() + var l List l.Append(1, 2, 3) // Test valid set within bounds @@ -76,7 +76,7 @@ func TestList_Set(t *testing.T) { } func TestList_Delete(t *testing.T) { - l := New() + var l List l.Append(1, 2, 3) // Test valid delete @@ -99,31 +99,31 @@ func TestList_Delete(t *testing.T) { } } -func TestList_Range(t *testing.T) { - l := New() +func TestList_Slice(t *testing.T) { + var l List l.Append(1, 2, 3, 4, 5) // Test valid ranges - values := l.Range(1, 4) + values := l.Slice(1, 4) expected := []interface{}{2, 3, 4} if !sliceEqual(values, expected) { - t.Errorf("Range(1,4) = %v; want %v", values, expected) + t.Errorf("Slice(1,4) = %v; want %v", values, expected) } // Test edge cases - if values := l.Range(-1, 2); !sliceEqual(values, []interface{}{1, 2}) { - t.Errorf("Range(-1,2) = %v; want [1 2]", values) + if values := l.Slice(-1, 2); !sliceEqual(values, []interface{}{1, 2}) { + t.Errorf("Slice(-1,2) = %v; want [1 2]", values) } - if values := l.Range(3, 10); !sliceEqual(values, []interface{}{4, 5}) { - t.Errorf("Range(3,10) = %v; want [4 5]", 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.Range(3, 2); values != nil { - t.Errorf("Range(3,2) = %v; want nil", values) + if values := l.Slice(3, 2); values != nil { + t.Errorf("Slice(3,2) = %v; want nil", values) } } func TestList_ForEach(t *testing.T) { - l := New() + var l List l.Append(1, 2, 3) sum := 0 @@ -149,7 +149,7 @@ func TestList_ForEach(t *testing.T) { } func TestList_Clone(t *testing.T) { - l := New() + var l List l.Append(1, 2, 3) clone := l.Clone() @@ -174,7 +174,7 @@ func TestList_Clone(t *testing.T) { } func TestList_DeleteRange(t *testing.T) { - l := New() + var l List l.Append(1, 2, 3, 4, 5) // Test valid range delete @@ -193,7 +193,7 @@ func TestList_DeleteRange(t *testing.T) { } // Test edge cases - l = New() + l = List{} l.Append(1, 2, 3) // Delete with negative start @@ -202,7 +202,7 @@ func TestList_DeleteRange(t *testing.T) { } // Delete with end > length - l = New() + 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) @@ -220,7 +220,7 @@ func TestList_DeleteRange(t *testing.T) { } func TestList_EmptyOperations(t *testing.T) { - l := New() + var l List // Operations on empty list if v := l.Get(0); v != nil { @@ -235,17 +235,17 @@ func TestList_EmptyOperations(t *testing.T) { t.Errorf("Get(0) after Set = %v; want 1", v) } - l = New() // Reset to empty list + 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.Range(0, 1); values != nil { + 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) { - l := New() + var l List // Test with different types l.Append(42, "hello", true, 3.14) @@ -265,7 +265,7 @@ func TestList_DifferentTypes(t *testing.T) { } func TestList_LargeOperations(t *testing.T) { - l := New() + var l List // Test with larger number of elements n := 1000 @@ -278,7 +278,7 @@ func TestList_LargeOperations(t *testing.T) { } // Test range on large list - values := l.Range(n-3, n) + 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) @@ -295,7 +295,7 @@ func TestList_LargeOperations(t *testing.T) { } func TestList_ChainedOperations(t *testing.T) { - l := New() + var l List // Test sequence of operations l.Append(1, 2, 3) @@ -312,7 +312,7 @@ func TestList_ChainedOperations(t *testing.T) { } func TestList_RangeEdgeCases(t *testing.T) { - l := New() + var l List l.Append(1, 2, 3, 4, 5) // Test various edge cases for Range @@ -329,65 +329,15 @@ func TestList_RangeEdgeCases(t *testing.T) { } for _, tc := range cases { - got := l.Range(tc.start, tc.end) + got := l.Slice(tc.start, tc.end) if !sliceEqual(got, tc.want) { - t.Errorf("Range(%d,%d) = %v; want %v", tc.start, tc.end, got, tc.want) + t.Errorf("Slice(%d,%d) = %v; want %v", tc.start, tc.end, got, tc.want) } } } -func TestList_Clear(t *testing.T) { - l := New() - l.Append(1, 2, 3) - - // Test clear - l.Clear() - if l.Len() != 0 { - t.Errorf("after Clear(), Len() = %d; want 0", l.Len()) - } - - // Test that we can still append after clearing - l.Append(4) - if l.Len() != 1 { - t.Errorf("after Clear() and Append(), Len() = %d; want 1", l.Len()) - } - if v := l.Get(0); v != 4 { - t.Errorf("after Clear() and Append(), Get(0) = %v; want 4", v) - } -} - -func TestList_Make(t *testing.T) { - // Test with default value - l := Make(3, "default") - if l.Len() != 3 { - t.Errorf("Make(3, default) Len() = %d; want 3", l.Len()) - } - for i := 0; i < l.Len(); i++ { - if v := l.Get(i); v != "default" { - t.Errorf("index %d = %v; want 'default'", i, v) - } - } - - // Test with nil default value - l = Make(2, nil) - if l.Len() != 2 { - t.Errorf("Make(2, nil) Len() = %d; want 2", l.Len()) - } - for i := 0; i < l.Len(); i++ { - if v := l.Get(i); v != nil { - t.Errorf("index %d = %v; want nil", i, v) - } - } - - // Test with negative length - l = Make(-1, "default") - if l.Len() != 0 { - t.Errorf("Make(-1, default) Len() = %d; want 0", l.Len()) - } -} - func TestList_IndexConsistency(t *testing.T) { - l := New() + var l List // Initial additions l.Append(1, 2, 3, 4, 5) // [1,2,3,4,5] @@ -418,9 +368,9 @@ func TestList_IndexConsistency(t *testing.T) { } // Verify all indices are accessible - allValues := l.Range(0, l.Len()) + allValues := l.Slice(0, l.Len()) if !sliceEqual(allValues, expected) { - t.Errorf("Range(0, Len()) = %v; want %v", allValues, expected) + t.Errorf("Slice(0, Len()) = %v; want %v", allValues, expected) } // Verify no gaps in iteration From 7e2a01253c9e3491f2949b0bf465ae12ced37632 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:33:37 +0100 Subject: [PATCH 05/10] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/list/list.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gno.land/p/demo/avl/list/list.gno b/examples/gno.land/p/demo/avl/list/list.gno index b0c1fd1012d..d4d2e1fce6e 100644 --- a/examples/gno.land/p/demo/avl/list/list.gno +++ b/examples/gno.land/p/demo/avl/list/list.gno @@ -256,7 +256,7 @@ func (l *List) ForEach(fn func(index int, value interface{}) bool) { } } -// Clone creates a deep copy of the list. +// Clone creates a shallow copy of the list. // // Example: // From c4afc1c72752842d036e1ecbbcd69b98f823de9b Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 20 Dec 2024 22:07:38 +0100 Subject: [PATCH 06/10] fix(p/demo/avl): correct order for IterateByOffset --- examples/gno.land/p/demo/avl/node.gno | 12 ++++---- examples/gno.land/p/demo/avl/node_test.gno | 36 ++++++++++------------ 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/examples/gno.land/p/demo/avl/node.gno b/examples/gno.land/p/demo/avl/node.gno index 7308e163768..c348712d08d 100644 --- a/examples/gno.land/p/demo/avl/node.gno +++ b/examples/gno.land/p/demo/avl/node.gno @@ -384,7 +384,7 @@ func (node *Node) TraverseInRange(start, end string, ascending bool, leavesOnly // TraverseByOffset traverses all nodes, including inner nodes. // A limit of math.MaxInt means no limit. -func (node *Node) TraverseByOffset(offset, limit int, descending bool, leavesOnly bool, cb func(*Node) bool) bool { +func (node *Node) TraverseByOffset(offset, limit int, ascending bool, leavesOnly bool, cb func(*Node) bool) bool { if node == nil { return false } @@ -401,13 +401,13 @@ func (node *Node) TraverseByOffset(offset, limit int, descending bool, leavesOnl } // go to the actual recursive function. - return node.traverseByOffset(offset, limit, descending, leavesOnly, cb) + return node.traverseByOffset(offset, limit, ascending, leavesOnly, cb) } // TraverseByOffset traverses the subtree rooted at the node by offset and limit, // in either ascending or descending order, and applies the callback function to each traversed node. // If leavesOnly is true, only leaf nodes are visited. -func (node *Node) traverseByOffset(offset, limit int, descending bool, leavesOnly bool, cb func(*Node) bool) bool { +func (node *Node) traverseByOffset(offset, limit int, ascending bool, leavesOnly bool, cb func(*Node) bool) bool { // caller guarantees: offset < node.size; limit > 0. if !leavesOnly { if cb(node) { @@ -415,7 +415,7 @@ func (node *Node) traverseByOffset(offset, limit int, descending bool, leavesOnl } } first, second := node.getLeftNode(), node.getRightNode() - if descending { + if !ascending { first, second = second, first } if first.IsLeaf() { @@ -437,7 +437,7 @@ func (node *Node) traverseByOffset(offset, limit int, descending bool, leavesOnl if offset >= first.size { offset -= first.size // 1 } else { - if first.traverseByOffset(offset, limit, descending, leavesOnly, cb) { + if first.traverseByOffset(offset, limit, ascending, leavesOnly, cb) { return true } // number of leaves which could actually be called from inside @@ -460,7 +460,7 @@ func (node *Node) traverseByOffset(offset, limit int, descending bool, leavesOnl } // => if it is not a leaf, it will still be enough to recursively call this // function with the updated offset and limit - return second.traverseByOffset(offset, limit, descending, leavesOnly, cb) + return second.traverseByOffset(offset, limit, ascending, leavesOnly, cb) } // Only used in testing... diff --git a/examples/gno.land/p/demo/avl/node_test.gno b/examples/gno.land/p/demo/avl/node_test.gno index f24217625ea..85be701e553 100644 --- a/examples/gno.land/p/demo/avl/node_test.gno +++ b/examples/gno.land/p/demo/avl/node_test.gno @@ -17,22 +17,24 @@ Book Browser` tt := []struct { name string - desc bool + asc bool }{ - {"ascending", false}, - {"descending", true}, + {"ascending", true}, + {"descending", false}, } for _, tt := range tt { t.Run(tt.name, func(t *testing.T) { + // use sl to insert the values, and reversed to match the values + // we do this to ensure that the order of TraverseByOffset is independent + // from the insertion order sl := strings.Split(testStrings, "\n") - - // sort a first time in the order opposite to how we'll be traversing - // the tree, to ensure that we are not just iterating through with - // insertion order. sort.Strings(sl) - if !tt.desc { - reverseSlice(sl) + reversed := append([]string{}, sl...) + reverseSlice(reversed) + + if !tt.asc { + sl, reversed = reversed, sl } r := NewNode(sl[0], nil) @@ -40,13 +42,9 @@ Browser` r, _ = r.Set(v, nil) } - // then sort sl in the order we'll be traversing it, so that we can - // compare the result with sl. - reverseSlice(sl) - var result []string for i := 0; i < len(sl); i++ { - r.TraverseByOffset(i, 1, tt.desc, true, func(n *Node) bool { + r.TraverseByOffset(i, 1, tt.asc, true, func(n *Node) bool { result = append(result, n.Key()) return false }) @@ -66,7 +64,7 @@ Browser` exp := sl[i:max] actual := []string{} - r.TraverseByOffset(i, l, tt.desc, true, func(tr *Node) bool { + r.TraverseByOffset(i, l, tt.asc, true, func(tr *Node) bool { actual = append(actual, tr.Key()) return false }) @@ -435,7 +433,7 @@ func TestRotateWhenHeightDiffers(t *testing.T) { { "right rotation when left subtree is higher", []string{"E", "C", "A", "B", "D"}, - []string{"A", "B", "C", "E", "D"}, + []string{"A", "B", "C", "D", "E"}, }, { "left rotation when right subtree is higher", @@ -445,12 +443,12 @@ func TestRotateWhenHeightDiffers(t *testing.T) { { "left-right rotation", []string{"E", "A", "C", "B", "D"}, - []string{"A", "B", "C", "E", "D"}, + []string{"A", "B", "C", "D", "E"}, }, { "right-left rotation", []string{"A", "E", "C", "B", "D"}, - []string{"A", "B", "C", "E", "D"}, + []string{"A", "B", "C", "D", "E"}, }, } @@ -533,7 +531,7 @@ func slicesEqual(w1, w2 []string) bool { return false } for i := 0; i < len(w1); i++ { - if w1[0] != w2[0] { + if w1[i] != w2[i] { return false } } From ced482b34f75b1bc6b607cd34e89622f5953af3e Mon Sep 17 00:00:00 2001 From: Morgan Bazalgette Date: Fri, 20 Dec 2024 22:14:59 +0100 Subject: [PATCH 07/10] reswap in pager --- examples/gno.land/p/demo/avl/pager/pager.gno | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/avl/pager/pager.gno b/examples/gno.land/p/demo/avl/pager/pager.gno index ca3eadde032..f5f909a473d 100644 --- a/examples/gno.land/p/demo/avl/pager/pager.gno +++ b/examples/gno.land/p/demo/avl/pager/pager.gno @@ -90,12 +90,12 @@ func (p *Pager) GetPageWithSize(pageNumber, pageSize int) *Page { items := []Item{} if p.Reversed { - p.Tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { + p.Tree.ReverseIterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { items = append(items, Item{Key: key, Value: value}) return false }) } else { - p.Tree.ReverseIterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { + p.Tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, value interface{}) bool { items = append(items, Item{Key: key, Value: value}) return false }) From 14a4976ab26f4c4d049886e0762b342b803cf6f9 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Fri, 20 Dec 2024 23:28:25 +0100 Subject: [PATCH 08/10] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/list/list.gno | 71 ++++++---------------- examples/gno.land/p/demo/avl/node.gno | 6 +- 2 files changed, 21 insertions(+), 56 deletions(-) diff --git a/examples/gno.land/p/demo/avl/list/list.gno b/examples/gno.land/p/demo/avl/list/list.gno index d4d2e1fce6e..454ac940fc7 100644 --- a/examples/gno.land/p/demo/avl/list/list.gno +++ b/examples/gno.land/p/demo/avl/list/list.gno @@ -203,57 +203,28 @@ func (l *List) Slice(startIndex, endIndex int) []interface{} { count := endIndex - startIndex result := make([]interface{}, count) - // Fill the slice from end to start to maintain correct order - i := count - 1 - l.tree.IterateByOffset(size-endIndex, count, func(_ string, value interface{}) bool { + // Change: Use IterateByOffset instead of ReverseIterateByOffset + i := 0 + l.tree.IterateByOffset(startIndex, count, func(_ string, value interface{}) bool { result[i] = value - i-- + i++ return false }) return result } // ForEach iterates through all elements in the list. -// The callback function receives the index and value of each element. -// Return true from the callback to stop iteration. -// -// Example: -// -// l := list.New() -// l.Append(1, 2, 3) -// -// sum := 0 -// l.ForEach(func(index int, value interface{}) bool { -// sum += value.(int) -// return false // continue iteration -// }) -// println(sum) // Output: 6 -// -// // Early stop -// l.ForEach(func(index int, value interface{}) bool { -// if value.(int) > 2 { -// return true // stop iteration -// } -// return false -// }) func (l *List) ForEach(fn func(index int, value interface{}) bool) { - size := l.tree.Size() - values := make([]interface{}, size) + if l.tree.Size() == 0 { + return + } - // First collect values in reverse order - i := size - 1 - l.tree.IterateByOffset(0, size, func(_ string, value interface{}) bool { - values[i] = value - i-- - return false + index := 0 + l.tree.IterateByOffset(0, l.tree.Size(), func(_ string, value interface{}) bool { + result := fn(index, value) + index++ + return result }) - - // Then iterate through them in correct order - for i := 0; i < size; i++ { - if fn(i, values[i]) { - return - } - } } // Clone creates a shallow copy of the list. @@ -270,8 +241,8 @@ func (l *List) ForEach(fn func(index int, value interface{}) bool) { // println(clone.Get(0)) // Output: 42 func (l *List) Clone() *List { newList := &List{ - tree: avl.Tree{}, // Create a new empty tree - idGen: l.idGen, // Copy the current ID generator state + tree: avl.Tree{}, + idGen: l.idGen, } size := l.tree.Size() @@ -279,20 +250,12 @@ func (l *List) Clone() *List { return newList } - // Collect values in correct order - values := make([]interface{}, size) - i := size - 1 + // Change: Use IterateByOffset instead of ReverseIterateByOffset l.tree.IterateByOffset(0, size, func(_ string, value interface{}) bool { - values[i] = value - i-- + newList.Append(value) return false }) - // Add values to new list in correct order - for _, value := range values { - newList.Append(value) - } - return newList } @@ -323,7 +286,7 @@ func (l *List) DeleteRange(startIndex, endIndex int) int { // Collect keys to delete keysToDelete := make([]string, 0, endIndex-startIndex) - l.tree.IterateByOffset(size-endIndex, endIndex-startIndex, func(key string, _ interface{}) bool { + l.tree.IterateByOffset(startIndex, endIndex-startIndex, func(key string, _ interface{}) bool { keysToDelete = append(keysToDelete, key) return false }) diff --git a/examples/gno.land/p/demo/avl/node.gno b/examples/gno.land/p/demo/avl/node.gno index c348712d08d..2acdb4938f7 100644 --- a/examples/gno.land/p/demo/avl/node.gno +++ b/examples/gno.land/p/demo/avl/node.gno @@ -423,10 +423,12 @@ func (node *Node) traverseByOffset(offset, limit int, ascending bool, leavesOnly if offset > 0 { offset-- } else { - cb(first) + if cb(first) { + return true + } limit-- if limit <= 0 { - return false + return true } } } else { From b28a4e3452e28791f2c0506243a983cfb060c8d1 Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Fri, 20 Dec 2024 23:33:20 +0100 Subject: [PATCH 09/10] chore: fix an early-termination bug Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/node.gno | 8 +++++--- examples/gno.land/p/demo/avl/node_test.gno | 24 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/examples/gno.land/p/demo/avl/node.gno b/examples/gno.land/p/demo/avl/node.gno index c348712d08d..7d4ddffff02 100644 --- a/examples/gno.land/p/demo/avl/node.gno +++ b/examples/gno.land/p/demo/avl/node.gno @@ -411,7 +411,7 @@ func (node *Node) traverseByOffset(offset, limit int, ascending bool, leavesOnly // caller guarantees: offset < node.size; limit > 0. if !leavesOnly { if cb(node) { - return true + return true // Stop traversal if callback returns true } } first, second := node.getLeftNode(), node.getRightNode() @@ -423,10 +423,12 @@ func (node *Node) traverseByOffset(offset, limit int, ascending bool, leavesOnly if offset > 0 { offset-- } else { - cb(first) + if cb(first) { + return true // Stop traversal if callback returns true + } limit-- if limit <= 0 { - return false + return true // Stop traversal when limit is reached } } } else { diff --git a/examples/gno.land/p/demo/avl/node_test.gno b/examples/gno.land/p/demo/avl/node_test.gno index 85be701e553..cf12bac8f55 100644 --- a/examples/gno.land/p/demo/avl/node_test.gno +++ b/examples/gno.land/p/demo/avl/node_test.gno @@ -420,6 +420,30 @@ func TestTraverse(t *testing.T) { t.Errorf("want %v got %v", expected, result) } }) + + t.Run("early termination", func(t *testing.T) { + if len(tt.input) == 0 { + return // Skip for empty tree + } + + var result []string + var count int + tree.Iterate("", "", func(n *Node) bool { + count++ + result = append(result, n.Key()) + return true // Stop after first item + }) + + if count != 1 { + t.Errorf("Expected callback to be called exactly once, got %d calls", count) + } + if len(result) != 1 { + t.Errorf("Expected exactly one result, got %d items", len(result)) + } + if len(result) > 0 && result[0] != tt.expected[0] { + t.Errorf("Expected first item to be %v, got %v", tt.expected[0], result[0]) + } + }) }) } } From cb215440ebcb97dc3031502ebcf468a692889a7f Mon Sep 17 00:00:00 2001 From: moul <94029+moul@users.noreply.github.com> Date: Fri, 20 Dec 2024 23:40:39 +0100 Subject: [PATCH 10/10] chore: fixup Signed-off-by: moul <94029+moul@users.noreply.github.com> --- examples/gno.land/p/demo/avl/list/list.gno | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/gno.land/p/demo/avl/list/list.gno b/examples/gno.land/p/demo/avl/list/list.gno index 454ac940fc7..0875eb66e01 100644 --- a/examples/gno.land/p/demo/avl/list/list.gno +++ b/examples/gno.land/p/demo/avl/list/list.gno @@ -203,7 +203,6 @@ func (l *List) Slice(startIndex, endIndex int) []interface{} { count := endIndex - startIndex result := make([]interface{}, count) - // Change: Use IterateByOffset instead of ReverseIterateByOffset i := 0 l.tree.IterateByOffset(startIndex, count, func(_ string, value interface{}) bool { result[i] = value @@ -250,7 +249,6 @@ func (l *List) Clone() *List { return newList } - // Change: Use IterateByOffset instead of ReverseIterateByOffset l.tree.IterateByOffset(0, size, func(_ string, value interface{}) bool { newList.Append(value) return false