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

tree_test.gno

64.91 Kb · 2584 lines
   1package bptree
   2
   3import (
   4	"math/rand"
   5	"sort"
   6	"testing"
   7
   8	"gno.land/p/nt/avl/v0"
   9)
  10
  11//----------------------------------------
  12// Basic operations
  13
  14func TestNewTree(t *testing.T) {
  15	tree := NewBPTree32()
  16	if tree.Size() != 0 {
  17		t.Error("Expected empty tree size to be 0")
  18	}
  19}
  20
  21func TestZeroValue(t *testing.T) {
  22	var tree BPTree
  23	if tree.Size() != 0 {
  24		t.Error("Expected zero-value tree size to be 0")
  25	}
  26	if tree.Has("x") {
  27		t.Error("Expected Has to return false on zero-value tree")
  28	}
  29	if v := tree.Get("x"); v != nil {
  30		t.Errorf("Expected Get to return nil on zero-value tree, got %v", v)
  31	}
  32	if _, ok := tree.Remove("x"); ok {
  33		t.Error("Expected Remove to return false on zero-value tree")
  34	}
  35
  36	// Set should work on zero-value tree.
  37	if updated := tree.Set("a", 1); updated {
  38		t.Error("Expected Set to return false for new key")
  39	}
  40	if tree.Size() != 1 {
  41		t.Errorf("Expected size 1, got %d", tree.Size())
  42	}
  43	if v := tree.Get("a"); v != 1 {
  44		t.Errorf("Expected Get(a) = 1, got %v", v)
  45	}
  46}
  47
  48func TestSetAndGet(t *testing.T) {
  49	tree := NewBPTreeN(4)
  50	tree.Set("key1", "value1")
  51	tree.Set("key2", "value2")
  52
  53	if tree.Size() != 2 {
  54		t.Errorf("Expected size 2, got %d", tree.Size())
  55	}
  56
  57	if v := tree.Get("key1"); v != "value1" {
  58		t.Errorf("Expected value1, got %v", v)
  59	}
  60
  61	if v := tree.Get("missing"); v != nil {
  62		t.Errorf("Expected Get to return nil for missing key, got %v", v)
  63	}
  64}
  65
  66func TestSetUpdate(t *testing.T) {
  67	tree := NewBPTreeN(4)
  68	if updated := tree.Set("k", "v1"); updated {
  69		t.Error("Expected false for new key")
  70	}
  71	if updated := tree.Set("k", "v2"); !updated {
  72		t.Error("Expected true for existing key")
  73	}
  74	if tree.Size() != 1 {
  75		t.Errorf("Expected size 1 after update, got %d", tree.Size())
  76	}
  77	if v := tree.Get("k"); v != "v2" {
  78		t.Errorf("Expected v2, got %v", v)
  79	}
  80}
  81
  82func TestHas(t *testing.T) {
  83	tree := NewBPTreeN(4)
  84	tree.Set("a", 1)
  85	if !tree.Has("a") {
  86		t.Error("Expected Has(a) = true")
  87	}
  88	if tree.Has("b") {
  89		t.Error("Expected Has(b) = false")
  90	}
  91}
  92
  93func TestRemove(t *testing.T) {
  94	tree := NewBPTreeN(4)
  95	tree.Set("a", 1)
  96	tree.Set("b", 2)
  97
  98	v, ok := tree.Remove("a")
  99	if !ok || v != 1 {
 100		t.Errorf("Expected (1, true), got (%v, %v)", v, ok)
 101	}
 102	if tree.Size() != 1 {
 103		t.Errorf("Expected size 1, got %d", tree.Size())
 104	}
 105	if tree.Has("a") {
 106		t.Error("Expected Has(a) = false after remove")
 107	}
 108
 109	v, ok = tree.Remove("missing")
 110	if ok || v != nil {
 111		t.Errorf("Expected (nil, false), got (%v, %v)", v, ok)
 112	}
 113}
 114
 115func TestRemoveLastKey(t *testing.T) {
 116	tree := NewBPTreeN(4)
 117	tree.Set("a", 1)
 118	tree.Remove("a")
 119	if tree.Size() != 0 {
 120		t.Errorf("Expected size 0, got %d", tree.Size())
 121	}
 122
 123	// Insert after removing everything.
 124	tree.Set("b", 2)
 125	if tree.Size() != 1 {
 126		t.Errorf("Expected size 1, got %d", tree.Size())
 127	}
 128	if v := tree.Get("b"); v != 2 {
 129		t.Errorf("Expected 2, got %v", v)
 130	}
 131}
 132
 133func TestNilValue(t *testing.T) {
 134	tree := NewBPTreeN(4)
 135	tree.Set("k", nil)
 136	// Has distinguishes a nil-valued entry from a missing key (Get returns
 137	// nil for both cases, by design).
 138	if !tree.Has("k") {
 139		t.Error("Expected Has(k) = true for nil-valued entry")
 140	}
 141	if v := tree.Get("k"); v != nil {
 142		t.Errorf("Expected nil value, got %v", v)
 143	}
 144	v, ok := tree.Remove("k")
 145	if !ok {
 146		t.Error("Expected remove to succeed")
 147	}
 148	if v != nil {
 149		t.Errorf("Expected nil removed value, got %v", v)
 150	}
 151}
 152
 153func TestEmptyStringKey(t *testing.T) {
 154	tree := NewBPTreeN(4)
 155	tree.Set("", "empty")
 156	tree.Set("a", "alpha")
 157	tree.Set("b", "beta")
 158
 159	if !tree.Has("") {
 160		t.Error("Expected Has('') = true for empty string key")
 161	}
 162	if v := tree.Get(""); v != "empty" {
 163		t.Errorf("Expected empty, got %v", v)
 164	}
 165	if tree.Size() != 3 {
 166		t.Errorf("Expected size 3, got %d", tree.Size())
 167	}
 168
 169	// Remove "" key.
 170	v, ok := tree.Remove("")
 171	if !ok || v != "empty" {
 172		t.Errorf("Remove('') = (%v, %v), want (empty, true)", v, ok)
 173	}
 174	if tree.Size() != 2 {
 175		t.Errorf("Expected size 2, got %d", tree.Size())
 176	}
 177	if tree.Has("") {
 178		t.Error("Expected Has('') = false after remove")
 179	}
 180}
 181
 182func TestGetMissingReturnsNil(t *testing.T) {
 183	tree := NewBPTreeN(4)
 184	tree.Set("a", 1)
 185	if v := tree.Get("missing"); v != nil {
 186		t.Errorf("Expected nil value for missing key, got %v", v)
 187	}
 188}
 189
 190func TestRemoveMissingReturnsNilFalse(t *testing.T) {
 191	tree := NewBPTreeN(4)
 192	tree.Set("a", 1)
 193	v, ok := tree.Remove("missing")
 194	if v != nil {
 195		t.Errorf("Expected nil value for missing remove, got %v", v)
 196	}
 197	if ok {
 198		t.Error("Expected false for missing remove")
 199	}
 200
 201	// Also on empty tree.
 202	var empty BPTree
 203	v, ok = empty.Remove("x")
 204	if v != nil || ok {
 205		t.Errorf("Remove on empty tree: got (%v, %v), want (nil, false)", v, ok)
 206	}
 207}
 208
 209func TestFanoutNeverResets(t *testing.T) {
 210	var tree BPTree
 211	tree.Set("a", 1)
 212	tree.Remove("a")
 213	// Tree is empty again, but fanout should still be 32.
 214	tree.Set("b", 2)
 215	if tree.Size() != 1 {
 216		t.Errorf("Expected size 1, got %d", tree.Size())
 217	}
 218	if tree.fanout != 32 {
 219		t.Errorf("Expected fanout 32 after re-insert, got %d", tree.fanout)
 220	}
 221}
 222
 223func TestSingleEntry(t *testing.T) {
 224	tree := NewBPTreeN(4)
 225	tree.Set("x", 42)
 226
 227	// Root should be a leaf for single entry.
 228	if tree.root == nil {
 229		t.Fatal("root is nil")
 230	}
 231	if !tree.root.isLeaf() {
 232		t.Error("root should be a leaf for single entry")
 233	}
 234	if tree.Size() != 1 {
 235		t.Errorf("Expected size 1, got %d", tree.Size())
 236	}
 237	k, v := tree.GetByIndex(0)
 238	if k != "x" || v != 42 {
 239		t.Errorf("GetByIndex(0) = (%v, %v), want (x, 42)", k, v)
 240	}
 241}
 242
 243//----------------------------------------
 244// GetByIndex
 245
 246func TestGetByIndex(t *testing.T) {
 247	tree := NewBPTreeN(4)
 248	tree.Set("c", 3)
 249	tree.Set("a", 1)
 250	tree.Set("b", 2)
 251
 252	k, v := tree.GetByIndex(0)
 253	if k != "a" || v != 1 {
 254		t.Errorf("GetByIndex(0) = (%v, %v), want (a, 1)", k, v)
 255	}
 256	k, v = tree.GetByIndex(1)
 257	if k != "b" || v != 2 {
 258		t.Errorf("GetByIndex(1) = (%v, %v), want (b, 2)", k, v)
 259	}
 260	k, v = tree.GetByIndex(2)
 261	if k != "c" || v != 3 {
 262		t.Errorf("GetByIndex(2) = (%v, %v), want (c, 3)", k, v)
 263	}
 264}
 265
 266func TestGetByIndexPanics(t *testing.T) {
 267	tree := NewBPTreeN(4)
 268	tree.Set("a", 1)
 269
 270	assertPanics(t, "empty tree", func() {
 271		var empty BPTree
 272		empty.GetByIndex(0)
 273	})
 274	assertPanics(t, "negative index", func() {
 275		tree.GetByIndex(-1)
 276	})
 277	assertPanics(t, "index == size", func() {
 278		tree.GetByIndex(1)
 279	})
 280}
 281
 282//----------------------------------------
 283// Iterate
 284
 285func TestIterate(t *testing.T) {
 286	tree := NewBPTreeN(4)
 287	tree.Set("a", 1)
 288	tree.Set("b", 2)
 289	tree.Set("c", 3)
 290	tree.Set("d", 4)
 291	tree.Set("e", 5)
 292
 293	// Full iteration.
 294	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 295		return tr.Iterate("", "", cb)
 296	})
 297	assertSliceEqual(t, got, []string{"a", "b", "c", "d", "e"})
 298
 299	// Bounded iteration [b, d) → b, c.
 300	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 301		return tr.Iterate("b", "d", cb)
 302	})
 303	assertSliceEqual(t, got, []string{"b", "c"})
 304
 305	// start == end → empty.
 306	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 307		return tr.Iterate("b", "b", cb)
 308	})
 309	assertSliceEqual(t, got, nil)
 310
 311	// start > end → empty.
 312	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 313		return tr.Iterate("z", "a", cb)
 314	})
 315	assertSliceEqual(t, got, nil)
 316
 317	// No lower bound.
 318	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 319		return tr.Iterate("", "c", cb)
 320	})
 321	assertSliceEqual(t, got, []string{"a", "b"})
 322
 323	// Empty tree.
 324	var empty BPTree
 325	stopped := empty.Iterate("", "", func(k string, v any) bool {
 326		t.Error("should not be called")
 327		return false
 328	})
 329	if stopped {
 330		t.Error("Expected false from Iterate on empty tree")
 331	}
 332}
 333
 334func TestIterateEarlyStop(t *testing.T) {
 335	tree := NewBPTreeN(4)
 336	tree.Set("a", 1)
 337	tree.Set("b", 2)
 338	tree.Set("c", 3)
 339
 340	count := 0
 341	stopped := tree.Iterate("", "", func(k string, v any) bool {
 342		count++
 343		return true
 344	})
 345	if !stopped {
 346		t.Error("Expected true from early-stopped Iterate")
 347	}
 348	if count != 1 {
 349		t.Errorf("Expected 1 callback, got %d", count)
 350	}
 351}
 352
 353func TestIterateAllEdgeCases(t *testing.T) {
 354	tree := NewBPTreeN(4)
 355	tree.Set("a", 1)
 356	tree.Set("b", 2)
 357	tree.Set("c", 3)
 358	tree.Set("d", 4)
 359	tree.Set("e", 5)
 360
 361	// Iterate("a", "a") → empty [a,a).
 362	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 363		return tr.Iterate("a", "a", cb)
 364	})
 365	assertSliceEqual(t, got, nil)
 366
 367	// Iterate("z", "a") → empty (start > end).
 368	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 369		return tr.Iterate("z", "a", cb)
 370	})
 371	assertSliceEqual(t, got, nil)
 372
 373	// Iterate("", "a") → nothing < "a".
 374	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 375		return tr.Iterate("", "a", cb)
 376	})
 377	assertSliceEqual(t, got, nil)
 378
 379	// Iterate("", "b") → just "a".
 380	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 381		return tr.Iterate("", "b", cb)
 382	})
 383	assertSliceEqual(t, got, []string{"a"})
 384
 385	// ReverseIterate("a", "a") → visits "a" (both inclusive).
 386	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 387		return tr.ReverseIterate("a", "a", cb)
 388	})
 389	assertSliceEqual(t, got, []string{"a"})
 390
 391	// ReverseIterate("z", "a") → empty (bounds don't swap).
 392	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 393		return tr.ReverseIterate("z", "a", cb)
 394	})
 395	assertSliceEqual(t, got, nil)
 396
 397	// ReverseIterate("c", "") → keys >= "c" descending.
 398	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 399		return tr.ReverseIterate("c", "", cb)
 400	})
 401	assertSliceEqual(t, got, []string{"e", "d", "c"})
 402
 403	// Iterate("", "a") on tree with key "" stored.
 404	tree2 := NewBPTreeN(4)
 405	tree2.Set("", "empty")
 406	tree2.Set("a", "alpha")
 407	tree2.Set("b", "beta")
 408	got = collectKeys(tree2, func(tr *BPTree, cb IterCbFn) bool {
 409		return tr.Iterate("", "a", cb)
 410	})
 411	assertSliceEqual(t, got, []string{""})
 412
 413	// Iterate("", "") visits all including "" key.
 414	got = collectKeys(tree2, func(tr *BPTree, cb IterCbFn) bool {
 415		return tr.Iterate("", "", cb)
 416	})
 417	assertSliceEqual(t, got, []string{"", "a", "b"})
 418}
 419
 420func TestAllIterationEmptyTree(t *testing.T) {
 421	var tree BPTree
 422	noop := func(k string, v any) bool {
 423		t.Error("should not be called")
 424		return false
 425	}
 426	if tree.Iterate("", "", noop) {
 427		t.Error("Iterate on empty tree should return false")
 428	}
 429	if tree.ReverseIterate("", "", noop) {
 430		t.Error("ReverseIterate on empty tree should return false")
 431	}
 432	if tree.IterateByOffset(0, 1, noop) {
 433		t.Error("IterateByOffset on empty tree should return false")
 434	}
 435	if tree.ReverseIterateByOffset(0, 1, noop) {
 436		t.Error("ReverseIterateByOffset on empty tree should return false")
 437	}
 438}
 439
 440func TestReverseIterate(t *testing.T) {
 441	tree := NewBPTreeN(4)
 442	tree.Set("a", 1)
 443	tree.Set("b", 2)
 444	tree.Set("c", 3)
 445	tree.Set("d", 4)
 446	tree.Set("e", 5)
 447
 448	// Full reverse.
 449	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 450		return tr.ReverseIterate("", "", cb)
 451	})
 452	assertSliceEqual(t, got, []string{"e", "d", "c", "b", "a"})
 453
 454	// Bounded [b, d] inclusive → d, c, b.
 455	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 456		return tr.ReverseIterate("b", "d", cb)
 457	})
 458	assertSliceEqual(t, got, []string{"d", "c", "b"})
 459
 460	// start == end → visits that one key.
 461	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 462		return tr.ReverseIterate("c", "c", cb)
 463	})
 464	assertSliceEqual(t, got, []string{"c"})
 465
 466	// start > end → empty.
 467	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 468		return tr.ReverseIterate("z", "a", cb)
 469	})
 470	assertSliceEqual(t, got, nil)
 471
 472	// No upper bound.
 473	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 474		return tr.ReverseIterate("c", "", cb)
 475	})
 476	assertSliceEqual(t, got, []string{"e", "d", "c"})
 477
 478	// end > max key → e, d, c, b, a.
 479	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 480		return tr.ReverseIterate("a", "z", cb)
 481	})
 482	assertSliceEqual(t, got, []string{"e", "d", "c", "b", "a"})
 483}
 484
 485func TestIterateByOffset(t *testing.T) {
 486	tree := NewBPTreeN(4)
 487	tree.Set("a", 1)
 488	tree.Set("b", 2)
 489	tree.Set("c", 3)
 490	tree.Set("d", 4)
 491	tree.Set("e", 5)
 492
 493	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 494		return tr.IterateByOffset(1, 3, cb)
 495	})
 496	assertSliceEqual(t, got, []string{"b", "c", "d"})
 497
 498	// offset=0, count=0 → nothing.
 499	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 500		return tr.IterateByOffset(0, 0, cb)
 501	})
 502	assertSliceEqual(t, got, nil)
 503
 504	// offset=size → nothing.
 505	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 506		return tr.IterateByOffset(5, 1, cb)
 507	})
 508	assertSliceEqual(t, got, nil)
 509
 510	// count exceeds remaining.
 511	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 512		return tr.IterateByOffset(3, 100, cb)
 513	})
 514	assertSliceEqual(t, got, []string{"d", "e"})
 515}
 516
 517func TestReverseIterateByOffset(t *testing.T) {
 518	tree := NewBPTreeN(4)
 519	tree.Set("a", 1)
 520	tree.Set("b", 2)
 521	tree.Set("c", 3)
 522	tree.Set("d", 4)
 523	tree.Set("e", 5)
 524
 525	// Full reverse.
 526	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 527		return tr.ReverseIterateByOffset(0, 5, cb)
 528	})
 529	assertSliceEqual(t, got, []string{"e", "d", "c", "b", "a"})
 530
 531	// offset=1, count=2 → [d, c].
 532	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 533		return tr.ReverseIterateByOffset(1, 2, cb)
 534	})
 535	assertSliceEqual(t, got, []string{"d", "c"})
 536
 537	// offset=4, count=1 → [a].
 538	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 539		return tr.ReverseIterateByOffset(4, 1, cb)
 540	})
 541	assertSliceEqual(t, got, []string{"a"})
 542
 543	// offset=4, count=10 → [a].
 544	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 545		return tr.ReverseIterateByOffset(4, 10, cb)
 546	})
 547	assertSliceEqual(t, got, []string{"a"})
 548
 549	// offset >= size → nothing.
 550	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 551		return tr.ReverseIterateByOffset(5, 1, cb)
 552	})
 553	assertSliceEqual(t, got, nil)
 554
 555	// count=0 → nothing.
 556	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 557		return tr.ReverseIterateByOffset(0, 0, cb)
 558	})
 559	assertSliceEqual(t, got, nil)
 560
 561	// negative offset
 562	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 563		return tr.ReverseIterateByOffset(-1, 3, cb)
 564	})
 565	assertSliceEqual(t, got, []string{"e", "d", "c"})
 566}
 567
 568func TestReverseIterateEarlyStop(t *testing.T) {
 569	tree := NewBPTreeN(4)
 570	tree.Set("a", 1)
 571	tree.Set("b", 2)
 572	tree.Set("c", 3)
 573	count := 0
 574	stopped := tree.ReverseIterate("", "", func(k string, v any) bool {
 575		count++
 576		return true
 577	})
 578	if !stopped {
 579		t.Error("Expected true from early-stopped ReverseIterate")
 580	}
 581	if count != 1 {
 582		t.Errorf("Expected 1 callback, got %d", count)
 583	}
 584}
 585
 586//----------------------------------------
 587// Splits and merges (use small fanout to trigger them)
 588
 589func TestSplitAndMerge(t *testing.T) {
 590	tree := NewBPTreeN(4)
 591
 592	// Insert enough keys to cause multiple splits.
 593	keys := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}
 594	for _, k := range keys {
 595		tree.Set(k, k)
 596	}
 597	if tree.Size() != len(keys) {
 598		t.Errorf("Expected size %d, got %d", len(keys), tree.Size())
 599	}
 600
 601	// Verify all keys present and in order.
 602	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 603		return tr.Iterate("", "", cb)
 604	})
 605	assertSliceEqual(t, got, keys)
 606
 607	// Verify GetByIndex works for all positions.
 608	for i, k := range keys {
 609		gk, gv := tree.GetByIndex(i)
 610		if gk != k || gv != k {
 611			t.Errorf("GetByIndex(%d) = (%v, %v), want (%v, %v)", i, gk, gv, k, k)
 612		}
 613	}
 614
 615	// Remove keys one by one and verify.
 616	for _, k := range keys {
 617		v, ok := tree.Remove(k)
 618		if !ok || v != k {
 619			t.Errorf("Remove(%v) = (%v, %v), want (%v, true)", k, v, ok, k)
 620		}
 621	}
 622	if tree.Size() != 0 {
 623		t.Errorf("Expected size 0 after removing all, got %d", tree.Size())
 624	}
 625}
 626
 627func TestRemoveFromMiddle(t *testing.T) {
 628	tree := NewBPTreeN(4)
 629	for _, k := range []string{"a", "b", "c", "d", "e", "f", "g", "h"} {
 630		tree.Set(k, k)
 631	}
 632
 633	// Remove from the middle to trigger redistributions and merges.
 634	tree.Remove("d")
 635	tree.Remove("e")
 636	tree.Remove("b")
 637	tree.Remove("g")
 638
 639	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 640		return tr.Iterate("", "", cb)
 641	})
 642	assertSliceEqual(t, got, []string{"a", "c", "f", "h"})
 643	if tree.Size() != 4 {
 644		t.Errorf("Expected size 4, got %d", tree.Size())
 645	}
 646}
 647
 648func TestSequentialInsertRemove(t *testing.T) {
 649	tree := NewBPTreeN(4)
 650
 651	// Sequential insert.
 652	n := 100
 653	for i := 0; i < n; i++ {
 654		k := intToKey(i)
 655		tree.Set(k, i)
 656	}
 657	if tree.Size() != n {
 658		t.Errorf("Expected size %d, got %d", n, tree.Size())
 659	}
 660
 661	// Verify sorted order.
 662	prev := ""
 663	tree.Iterate("", "", func(k string, v any) bool {
 664		if k <= prev && prev != "" {
 665			t.Errorf("Keys not in order: %q after %q", k, prev)
 666		}
 667		prev = k
 668		return false
 669	})
 670
 671	// Remove all in reverse order.
 672	for i := n - 1; i >= 0; i-- {
 673		k := intToKey(i)
 674		_, ok := tree.Remove(k)
 675		if !ok {
 676			t.Errorf("Remove(%v) failed", k)
 677		}
 678	}
 679	if tree.Size() != 0 {
 680		t.Errorf("Expected size 0, got %d", tree.Size())
 681	}
 682}
 683
 684func TestRandomInsertRemove(t *testing.T) {
 685	tree := NewBPTreeN(4)
 686
 687	// Insert in "random" order (shuffled via simple hash).
 688	keys := make([]string, 50)
 689	for i := range keys {
 690		keys[i] = intToKey((i*37 + 13) % 50)
 691	}
 692	for _, k := range keys {
 693		tree.Set(k, k)
 694	}
 695	if tree.Size() != 50 {
 696		t.Errorf("Expected size 50, got %d", tree.Size())
 697	}
 698
 699	// Remove half.
 700	for i := 0; i < 25; i++ {
 701		tree.Remove(keys[i])
 702	}
 703	if tree.Size() != 25 {
 704		t.Errorf("Expected size 25, got %d", tree.Size())
 705	}
 706
 707	// Verify remaining keys are in sorted order.
 708	prev := ""
 709	tree.Iterate("", "", func(k string, v any) bool {
 710		if k <= prev && prev != "" {
 711			t.Errorf("Keys not in order: %q after %q", k, prev)
 712		}
 713		prev = k
 714		return false
 715	})
 716}
 717
 718func TestDifferentFanouts(t *testing.T) {
 719	for _, fanout := range []int{4, 5, 8, 16, 32} {
 720		tree := NewBPTreeN(fanout)
 721		n := 100
 722		for i := 0; i < n; i++ {
 723			tree.Set(intToKey(i), i)
 724		}
 725		if tree.Size() != n {
 726			t.Errorf("fanout=%d: expected size %d, got %d", fanout, n, tree.Size())
 727		}
 728
 729		// Verify order.
 730		got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 731			return tr.Iterate("", "", cb)
 732		})
 733		for i := 1; i < len(got); i++ {
 734			if got[i] <= got[i-1] {
 735				t.Errorf("fanout=%d: keys not in order at %d", fanout, i)
 736				break
 737			}
 738		}
 739
 740		// Remove all.
 741		for i := 0; i < n; i++ {
 742			tree.Remove(intToKey(i))
 743		}
 744		if tree.Size() != 0 {
 745			t.Errorf("fanout=%d: expected size 0 after remove all, got %d", fanout, tree.Size())
 746		}
 747	}
 748}
 749
 750func TestNegativeCount(t *testing.T) {
 751	tree := NewBPTreeN(4)
 752	tree.Set("a", 1)
 753
 754	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 755		return tr.IterateByOffset(0, -1, cb)
 756	})
 757	assertSliceEqual(t, got, nil)
 758
 759	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 760		return tr.ReverseIterateByOffset(0, -1, cb)
 761	})
 762	assertSliceEqual(t, got, nil)
 763}
 764
 765func TestRootCollapse(t *testing.T) {
 766	tree := NewBPTreeN(4)
 767	// Insert enough to create inner nodes.
 768	for _, k := range []string{"a", "b", "c", "d", "e"} {
 769		tree.Set(k, k)
 770	}
 771	if tree.root.isLeaf() {
 772		t.Error("Expected inner root after 5 inserts with fanout 4")
 773	}
 774
 775	// Remove enough to trigger merges and root collapse.
 776	tree.Remove("a")
 777	tree.Remove("b")
 778	tree.Remove("c")
 779	// With only "d" and "e" left, root should collapse to a leaf.
 780	if !tree.root.isLeaf() {
 781		t.Error("Expected leaf root after removing down to 2 entries")
 782	}
 783	if tree.Size() != 2 {
 784		t.Errorf("Expected size 2, got %d", tree.Size())
 785	}
 786	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 787		return tr.Iterate("", "", cb)
 788	})
 789	assertSliceEqual(t, got, []string{"d", "e"})
 790}
 791
 792func TestSeparatorKeyAfterLeftmostDeletion(t *testing.T) {
 793	tree := NewBPTreeN(4)
 794	// Insert keys to create a split.
 795	for _, k := range []string{"a", "b", "c", "d", "e", "f", "g", "h"} {
 796		tree.Set(k, k)
 797	}
 798	// Remove leftmost key "a" — should trigger separator update.
 799	tree.Remove("a")
 800
 801	// Verify all remaining keys are accessible.
 802	for _, k := range []string{"b", "c", "d", "e", "f", "g", "h"} {
 803		if !tree.Has(k) {
 804			t.Errorf("Expected Has(%s) = true after removing 'a'", k)
 805		}
 806	}
 807	if tree.Has("a") {
 808		t.Error("Expected Has(a) = false")
 809	}
 810
 811	// Verify sorted order.
 812	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 813		return tr.Iterate("", "", cb)
 814	})
 815	assertSliceEqual(t, got, []string{"b", "c", "d", "e", "f", "g", "h"})
 816
 817	// Verify GetByIndex still works.
 818	k, _ := tree.GetByIndex(0)
 819	if k != "b" {
 820		t.Errorf("GetByIndex(0) = %v, want b", k)
 821	}
 822}
 823
 824func TestNinetyTenSplit(t *testing.T) {
 825	// Sequential inserts should trigger 90/10 splits, keeping left leaves ~97% full.
 826	tree := NewBPTreeN(4)
 827	for _, k := range []string{"a", "b", "c", "d", "e"} {
 828		tree.Set(k, k)
 829	}
 830	// After inserting a,b,c,d (leaf full), then e (append → 90/10 split):
 831	// Left should have fanout-1=3 entries [a,b,c], right should have 2 entries [d,e].
 832	if !tree.root.isLeaf() == true {
 833		// Root should be an inner node after split.
 834	}
 835	inner := tree.root.(*innerNode)
 836	left := inner.children[0].(*leafNode)
 837	right := inner.children[1].(*leafNode)
 838	if len(left.keys) != 3 {
 839		t.Errorf("90/10 split: left leaf has %d entries, want 3", len(left.keys))
 840	}
 841	if len(right.keys) != 2 {
 842		t.Errorf("90/10 split: right leaf has %d entries, want 2", len(right.keys))
 843	}
 844
 845	// Verify all keys accessible.
 846	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 847		return tr.Iterate("", "", cb)
 848	})
 849	assertSliceEqual(t, got, []string{"a", "b", "c", "d", "e"})
 850}
 851
 852func TestNinetyTenSplitLargeFanout(t *testing.T) {
 853	// With fanout=32, sequential inserts should produce high fill factor.
 854	tree := NewBPTree32()
 855	n := 200
 856	for i := 0; i < n; i++ {
 857		tree.Set(intToKey(i), i)
 858	}
 859	if tree.Size() != n {
 860		t.Errorf("Expected size %d, got %d", n, tree.Size())
 861	}
 862
 863	// Verify all keys in order.
 864	prev := ""
 865	count := 0
 866	tree.Iterate("", "", func(k string, v any) bool {
 867		if k <= prev && prev != "" {
 868			t.Errorf("Keys not in order: %q after %q", k, prev)
 869		}
 870		prev = k
 871		count++
 872		return false
 873	})
 874	if count != n {
 875		t.Errorf("Iterate visited %d entries, want %d", count, n)
 876	}
 877
 878	// Remove all and verify.
 879	for i := 0; i < n; i++ {
 880		_, ok := tree.Remove(intToKey(i))
 881		if !ok {
 882			t.Errorf("Remove(%s) failed", intToKey(i))
 883		}
 884	}
 885	if tree.Size() != 0 {
 886		t.Errorf("Expected size 0, got %d", tree.Size())
 887	}
 888}
 889
 890func TestMixedSplitTypes(t *testing.T) {
 891	// Sequential inserts trigger 90/10, then a middle insert triggers 50/50.
 892	tree := NewBPTreeN(4)
 893	// Sequential: triggers 90/10 split.
 894	for _, k := range []string{"b", "c", "d", "e"} {
 895		tree.Set(k, k)
 896	}
 897	// Insert "a" at the beginning of the left leaf — not an append, so if
 898	// that leaf overflows it will use 50/50.
 899	tree.Set("a", "a")
 900
 901	// Verify all keys present and sorted.
 902	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
 903		return tr.Iterate("", "", cb)
 904	})
 905	assertSliceEqual(t, got, []string{"a", "b", "c", "d", "e"})
 906}
 907
 908func TestSizeCacheConsistency(t *testing.T) {
 909	tree := NewBPTreeN(4)
 910	keys := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}
 911
 912	// Check after each insert.
 913	for _, k := range keys {
 914		tree.Set(k, k)
 915		verifySizeCache(t, tree.root)
 916	}
 917
 918	// Check after each remove.
 919	for _, k := range keys {
 920		tree.Remove(k)
 921		verifySizeCache(t, tree.root)
 922	}
 923
 924	// Also check with large fanout.
 925	tree2 := NewBPTree32()
 926	for i := 0; i < 100; i++ {
 927		tree2.Set(intToKey(i), i)
 928	}
 929	verifySizeCache(t, tree2.root)
 930	for i := 0; i < 100; i++ {
 931		tree2.Remove(intToKey(i))
 932		verifySizeCache(t, tree2.root)
 933	}
 934}
 935
 936func verifySizeCache(t *testing.T, n node) {
 937	t.Helper()
 938	if n == nil || n.isLeaf() {
 939		return
 940	}
 941	inner := n.(*innerNode)
 942	// Verify each sizes[i] matches the actual child subtree size.
 943	for i, child := range inner.children {
 944		actual := child.nodeSize()
 945		if inner.sizes[i] != actual {
 946			t.Errorf("innerNode.sizes[%d]=%d but child.nodeSize()=%d", i, inner.sizes[i], actual)
 947		}
 948		verifySizeCache(t, child)
 949	}
 950}
 951
 952func TestValueIndirection(t *testing.T) {
 953	tree := NewBPTreeN(4)
 954
 955	// Nil value through *any.
 956	tree.Set("nil", nil)
 957	if !tree.Has("nil") {
 958		t.Error("Expected key 'nil' to exist")
 959	}
 960	if v := tree.Get("nil"); v != nil {
 961		t.Errorf("Expected nil value, got %v", v)
 962	}
 963
 964	// Various types.
 965	tree.Set("int", 42)
 966	tree.Set("str", "hello")
 967	tree.Set("slice", []byte{1, 2, 3})
 968
 969	if v := tree.Get("int"); v != 42 {
 970		t.Errorf("Expected 42, got %v", v)
 971	}
 972	if v := tree.Get("str"); v != "hello" {
 973		t.Errorf("Expected hello, got %v", v)
 974	}
 975	if v := tree.Get("slice"); len(v.([]byte)) != 3 {
 976		t.Errorf("Expected 3-byte slice, got %v", v)
 977	}
 978
 979	// Update value in place (should reuse the *any pointer).
 980	tree.Set("int", 99)
 981	if v := tree.Get("int"); v != 99 {
 982		t.Errorf("Expected 99 after update, got %v", v)
 983	}
 984
 985	// Remove nil value.
 986	v, ok := tree.Remove("nil")
 987	if !ok || v != nil {
 988		t.Errorf("Remove nil: got (%v, %v), want (nil, true)", v, ok)
 989	}
 990
 991	// Remove typed value.
 992	v, ok = tree.Remove("int")
 993	if !ok || v != 99 {
 994		t.Errorf("Remove int: got (%v, %v), want (99, true)", v, ok)
 995	}
 996
 997	// Large string values — each stored as separate *any object.
 998	bigVal := make([]byte, 10000)
 999	for i := range bigVal {
1000		bigVal[i] = byte(i % 256)
1001	}
1002	for i := 0; i < 10; i++ {
1003		tree.Set(intToKey(i), string(bigVal))
1004	}
1005	if tree.Size() != 12 { // 10 new + "str" + "slice" remaining
1006		t.Errorf("Expected size 12, got %d", tree.Size())
1007	}
1008
1009	// Verify large values round-trip correctly.
1010	for i := 0; i < 10; i++ {
1011		v := tree.Get(intToKey(i))
1012		if v == nil {
1013			t.Errorf("Missing key %s", intToKey(i))
1014			continue
1015		}
1016		s := v.(string)
1017		if len(s) != 10000 {
1018			t.Errorf("Key %s: expected 10000-byte string, got %d", intToKey(i), len(s))
1019		}
1020	}
1021
1022	// Iteration with mixed values.
1023	count := 0
1024	tree.Iterate("", "", func(k string, v any) bool {
1025		count++
1026		return false
1027	})
1028	if count != 12 {
1029		t.Errorf("Iterate counted %d, want 12", count)
1030	}
1031
1032	// Remove all and verify clean.
1033	for i := 0; i < 10; i++ {
1034		tree.Remove(intToKey(i))
1035	}
1036	tree.Remove("str")
1037	tree.Remove("slice")
1038	if tree.Size() != 0 {
1039		t.Errorf("Expected empty tree, got size %d", tree.Size())
1040	}
1041}
1042
1043func TestFanoutPanics(t *testing.T) {
1044	assertPanics(t, "fanout 3", func() {
1045		NewBPTreeN(3)
1046	})
1047	assertPanics(t, "fanout 0", func() {
1048		NewBPTreeN(0)
1049	})
1050}
1051
1052//----------------------------------------
1053// Ported from avl/v0 node_test.gno
1054
1055func TestHasTableDriven(t *testing.T) {
1056	tests := []struct {
1057		name     string
1058		input    []string
1059		hasKey   string
1060		expected bool
1061	}{
1062		{"has key in non-empty tree", []string{"C", "A", "B", "E", "D"}, "B", true},
1063		{"does not have key in non-empty tree", []string{"C", "A", "B", "E", "D"}, "F", false},
1064		{"has key in single-node tree", []string{"A"}, "A", true},
1065		{"does not have key in single-node tree", []string{"A"}, "B", false},
1066		{"does not have key in empty tree", []string{}, "A", false},
1067	}
1068	for _, tt := range tests {
1069		t.Run(tt.name, func(t *testing.T) {
1070			tree := NewBPTreeN(4)
1071			for _, key := range tt.input {
1072				tree.Set(key, nil)
1073			}
1074			result := tree.Has(tt.hasKey)
1075			if result != tt.expected {
1076				t.Errorf("Expected %v, got %v", tt.expected, result)
1077			}
1078		})
1079	}
1080}
1081
1082func TestGetByIndexTableDriven(t *testing.T) {
1083	tests := []struct {
1084		name        string
1085		input       []string
1086		idx         int
1087		expectKey   string
1088		expectPanic bool
1089	}{
1090		{"get by valid index", []string{"C", "A", "B", "E", "D"}, 2, "C", false},
1091		{"get by valid index (smallest)", []string{"C", "A", "B", "E", "D"}, 0, "A", false},
1092		{"get by valid index (largest)", []string{"C", "A", "B", "E", "D"}, 4, "E", false},
1093		{"get by invalid index (negative)", []string{"C", "A", "B", "E", "D"}, -1, "", true},
1094		{"get by invalid index (out of range)", []string{"C", "A", "B", "E", "D"}, 5, "", true},
1095	}
1096	for _, tt := range tests {
1097		t.Run(tt.name, func(t *testing.T) {
1098			tree := NewBPTreeN(4)
1099			for _, key := range tt.input {
1100				tree.Set(key, nil)
1101			}
1102			if tt.expectPanic {
1103				defer func() {
1104					if r := recover(); r == nil {
1105						t.Errorf("Expected a panic but didn't get one")
1106					}
1107				}()
1108			}
1109			key, _ := tree.GetByIndex(tt.idx)
1110			if !tt.expectPanic && key != tt.expectKey {
1111				t.Errorf("Expected key %s, got %s", tt.expectKey, key)
1112			}
1113		})
1114	}
1115}
1116
1117func TestRemoveTableDriven(t *testing.T) {
1118	tests := []struct {
1119		name      string
1120		input     []string
1121		removeKey string
1122		expected  []string
1123	}{
1124		{"remove from middle", []string{"C", "A", "B", "D"}, "B", []string{"A", "C", "D"}},
1125		{"remove first key", []string{"C", "A", "B", "D"}, "A", []string{"B", "C", "D"}},
1126		{"remove last key", []string{"C", "A", "B", "E", "D"}, "E", []string{"A", "B", "C", "D"}},
1127		{"remove root-equivalent key", []string{"C", "A", "B", "E", "D"}, "C", []string{"A", "B", "D", "E"}},
1128		{"remove non-existent key", []string{"C", "A", "B", "E", "D"}, "F", []string{"A", "B", "C", "D", "E"}},
1129	}
1130	for _, tt := range tests {
1131		t.Run(tt.name, func(t *testing.T) {
1132			tree := NewBPTreeN(4)
1133			for _, key := range tt.input {
1134				tree.Set(key, nil)
1135			}
1136			tree.Remove(tt.removeKey)
1137			var result []string
1138			tree.Iterate("", "", func(key string, value any) bool {
1139				result = append(result, key)
1140				return false
1141			})
1142			if len(result) == 0 {
1143				result = []string{}
1144			}
1145			assertSliceEqual(t, result, tt.expected)
1146		})
1147	}
1148}
1149
1150func TestTraverse(t *testing.T) {
1151	tests := []struct {
1152		name     string
1153		input    []string
1154		expected []string
1155	}{
1156		{"empty tree", []string{}, []string{}},
1157		{"single node tree", []string{"A"}, []string{"A"}},
1158		{"small tree", []string{"C", "A", "B", "E", "D"}, []string{"A", "B", "C", "D", "E"}},
1159		{"large tree", []string{"H", "D", "L", "B", "F", "J", "N", "A", "C", "E", "G", "I", "K", "M", "O"},
1160			[]string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"}},
1161	}
1162	for _, tt := range tests {
1163		t.Run(tt.name, func(t *testing.T) {
1164			tree := NewBPTreeN(4)
1165			for _, key := range tt.input {
1166				tree.Set(key, nil)
1167			}
1168
1169			t.Run("iterate", func(t *testing.T) {
1170				var result []string
1171				tree.Iterate("", "", func(key string, value any) bool {
1172					result = append(result, key)
1173					return false
1174				})
1175				if len(result) == 0 {
1176					result = []string{}
1177				}
1178				assertSliceEqual(t, result, tt.expected)
1179			})
1180
1181			t.Run("ReverseIterate", func(t *testing.T) {
1182				var result []string
1183				tree.ReverseIterate("", "", func(key string, value any) bool {
1184					result = append(result, key)
1185					return false
1186				})
1187				expected := make([]string, len(tt.expected))
1188				copy(expected, tt.expected)
1189				for i, j := 0, len(expected)-1; i < j; i, j = i+1, j-1 {
1190					expected[i], expected[j] = expected[j], expected[i]
1191				}
1192				if len(result) == 0 {
1193					result = []string{}
1194				}
1195				assertSliceEqual(t, result, expected)
1196			})
1197
1198			t.Run("TraverseInRange", func(t *testing.T) {
1199				var result []string
1200				start, end := "C", "M"
1201				tree.Iterate(start, end, func(key string, value any) bool {
1202					result = append(result, key)
1203					return false
1204				})
1205				expected := make([]string, 0)
1206				for _, key := range tt.expected {
1207					if key >= start && key < end {
1208						expected = append(expected, key)
1209					}
1210				}
1211				if len(result) == 0 {
1212					result = []string{}
1213				}
1214				assertSliceEqual(t, result, expected)
1215			})
1216
1217			t.Run("early termination", func(t *testing.T) {
1218				if len(tt.input) == 0 {
1219					return
1220				}
1221				var result []string
1222				var count int
1223				tree.Iterate("", "", func(key string, value any) bool {
1224					count++
1225					result = append(result, key)
1226					return true
1227				})
1228				if count != 1 {
1229					t.Errorf("Expected callback to be called exactly once, got %d calls", count)
1230				}
1231				if len(result) != 1 {
1232					t.Errorf("Expected exactly one result, got %d items", len(result))
1233				}
1234				if len(result) > 0 && result[0] != tt.expected[0] {
1235					t.Errorf("Expected first item to be %v, got %v", tt.expected[0], result[0])
1236				}
1237			})
1238		})
1239	}
1240}
1241
1242func TestTraverseByOffset(t *testing.T) {
1243	sl := []string{"Alfa", "Alfred", "Alpha", "Alphabet", "Beta", "Beth", "Book", "Browser"}
1244
1245	// Insert in reverse order to ensure ordering is independent of insertion order.
1246	reversed := make([]string, len(sl))
1247	copy(reversed, sl)
1248	for i, j := 0, len(reversed)-1; i < j; i, j = i+1, j-1 {
1249		reversed[i], reversed[j] = reversed[j], reversed[i]
1250	}
1251
1252	t.Run("ascending", func(t *testing.T) {
1253		tree := NewBPTreeN(4)
1254		for _, v := range reversed {
1255			tree.Set(v, nil)
1256		}
1257
1258		// Single-element offset traversal.
1259		var result []string
1260		for i := 0; i < len(sl); i++ {
1261			tree.IterateByOffset(i, 1, func(key string, value any) bool {
1262				result = append(result, key)
1263				return false
1264			})
1265		}
1266		assertSliceEqual(t, result, sl)
1267
1268		// Sliding window.
1269		for l := 2; l <= len(sl); l++ {
1270			for i := 0; i <= len(sl); i++ {
1271				max := i + l
1272				if max > len(sl) {
1273					max = len(sl)
1274				}
1275				exp := sl[i:max]
1276				var actual []string
1277				tree.IterateByOffset(i, l, func(key string, value any) bool {
1278					actual = append(actual, key)
1279					return false
1280				})
1281				if len(actual) == 0 {
1282					actual = []string{}
1283				}
1284				assertSliceEqual(t, actual, exp)
1285			}
1286		}
1287	})
1288
1289	t.Run("descending", func(t *testing.T) {
1290		tree := NewBPTreeN(4)
1291		for _, v := range reversed {
1292			tree.Set(v, nil)
1293		}
1294
1295		// The descending order.
1296		desc := make([]string, len(sl))
1297		copy(desc, sl)
1298		for i, j := 0, len(desc)-1; i < j; i, j = i+1, j-1 {
1299			desc[i], desc[j] = desc[j], desc[i]
1300		}
1301
1302		// Single-element offset traversal in reverse.
1303		var result []string
1304		for i := 0; i < len(desc); i++ {
1305			tree.ReverseIterateByOffset(i, 1, func(key string, value any) bool {
1306				result = append(result, key)
1307				return false
1308			})
1309		}
1310		assertSliceEqual(t, result, desc)
1311
1312		// Sliding window in descending.
1313		for l := 2; l <= len(desc); l++ {
1314			for i := 0; i <= len(desc); i++ {
1315				max := i + l
1316				if max > len(desc) {
1317					max = len(desc)
1318				}
1319				exp := desc[i:max]
1320				var actual []string
1321				tree.ReverseIterateByOffset(i, l, func(key string, value any) bool {
1322					actual = append(actual, key)
1323					return false
1324				})
1325				if len(actual) == 0 {
1326					actual = []string{}
1327				}
1328				assertSliceEqual(t, actual, exp)
1329			}
1330		}
1331	})
1332}
1333
1334func TestBSTProperty(t *testing.T) {
1335	tree := NewBPTreeN(4)
1336	keys := []string{"D", "B", "F", "A", "C", "E", "G"}
1337	for _, key := range keys {
1338		tree.Set(key, nil)
1339	}
1340
1341	var result []string
1342	tree.Iterate("", "", func(key string, value any) bool {
1343		result = append(result, key)
1344		return false
1345	})
1346
1347	for i := 1; i < len(result); i++ {
1348		if result[i] < result[i-1] {
1349			t.Errorf("Sorted property violated: %s < %s (index %d)",
1350				result[i], result[i-1], i)
1351		}
1352	}
1353}
1354
1355func TestRemoveFromEmptyTree(t *testing.T) {
1356	var tree BPTree
1357	val, removed := tree.Remove("NonExistent")
1358	if val != nil || removed {
1359		t.Errorf("Expected no value and removed=false when removing from empty tree")
1360	}
1361}
1362
1363// Ported from avl tree_test.gno
1364
1365func TestPortedTreeSize(t *testing.T) {
1366	tree := NewBPTree32()
1367	if tree.Size() != 0 {
1368		t.Error("Expected empty tree size to be 0")
1369	}
1370	tree.Set("key1", "value1")
1371	tree.Set("key2", "value2")
1372	if tree.Size() != 2 {
1373		t.Error("Expected tree size to be 2")
1374	}
1375}
1376
1377func TestPortedTreeHas(t *testing.T) {
1378	tree := NewBPTree32()
1379	tree.Set("key1", "value1")
1380	if !tree.Has("key1") {
1381		t.Error("Expected tree to have key1")
1382	}
1383	if tree.Has("key2") {
1384		t.Error("Expected tree to not have key2")
1385	}
1386}
1387
1388func TestPortedTreeGet(t *testing.T) {
1389	tree := NewBPTree32()
1390	tree.Set("key1", "value1")
1391	if value := tree.Get("key1"); value != "value1" {
1392		t.Error("Expected Get to return value1")
1393	}
1394	if value := tree.Get("key2"); value != nil {
1395		t.Error("Expected Get to return nil for non-existent key")
1396	}
1397}
1398
1399func TestPortedTreeGetByIndex(t *testing.T) {
1400	tree := NewBPTree32()
1401	tree.Set("key1", "value1")
1402	tree.Set("key2", "value2")
1403	key, value := tree.GetByIndex(0)
1404	if key != "key1" || value != "value1" {
1405		t.Error("Expected GetByIndex(0) to return key1 and value1")
1406	}
1407	key, value = tree.GetByIndex(1)
1408	if key != "key2" || value != "value2" {
1409		t.Error("Expected GetByIndex(1) to return key2 and value2")
1410	}
1411	defer func() {
1412		if r := recover(); r == nil {
1413			t.Error("Expected GetByIndex to panic for out-of-range index")
1414		}
1415	}()
1416	tree.GetByIndex(2)
1417}
1418
1419func TestPortedTreeRemove(t *testing.T) {
1420	tree := NewBPTree32()
1421	tree.Set("key1", "value1")
1422	value, removed := tree.Remove("key1")
1423	if !removed || value != "value1" || tree.Size() != 0 {
1424		t.Error("Expected Remove to remove key-value pair")
1425	}
1426	_, removed = tree.Remove("key2")
1427	if removed {
1428		t.Error("Expected Remove to return false for non-existent key")
1429	}
1430}
1431
1432func TestPortedTreeIterate(t *testing.T) {
1433	tree := NewBPTree32()
1434	tree.Set("key1", "value1")
1435	tree.Set("key2", "value2")
1436	tree.Set("key3", "value3")
1437	var keys []string
1438	tree.Iterate("", "", func(key string, value any) bool {
1439		keys = append(keys, key)
1440		return false
1441	})
1442	assertSliceEqual(t, keys, []string{"key1", "key2", "key3"})
1443}
1444
1445func TestPortedTreeReverseIterate(t *testing.T) {
1446	tree := NewBPTree32()
1447	tree.Set("key1", "value1")
1448	tree.Set("key2", "value2")
1449	tree.Set("key3", "value3")
1450	var keys []string
1451	tree.ReverseIterate("", "", func(key string, value any) bool {
1452		keys = append(keys, key)
1453		return false
1454	})
1455	assertSliceEqual(t, keys, []string{"key3", "key2", "key1"})
1456}
1457
1458func TestPortedTreeIterateByOffset(t *testing.T) {
1459	tree := NewBPTree32()
1460	tree.Set("key1", "value1")
1461	tree.Set("key2", "value2")
1462	tree.Set("key3", "value3")
1463	var keys []string
1464	tree.IterateByOffset(1, 2, func(key string, value any) bool {
1465		keys = append(keys, key)
1466		return false
1467	})
1468	assertSliceEqual(t, keys, []string{"key2", "key3"})
1469}
1470
1471func TestPortedTreeReverseIterateByOffset(t *testing.T) {
1472	tree := NewBPTree32()
1473	tree.Set("key1", "value1")
1474	tree.Set("key2", "value2")
1475	tree.Set("key3", "value3")
1476	var keys []string
1477	tree.ReverseIterateByOffset(1, 2, func(key string, value any) bool {
1478		keys = append(keys, key)
1479		return false
1480	})
1481	assertSliceEqual(t, keys, []string{"key2", "key1"})
1482}
1483
1484func TestPortedReverseIterateByOffsetVaried(t *testing.T) {
1485	tree := NewBPTree32()
1486	tree.Set("a", 1)
1487	tree.Set("b", 2)
1488	tree.Set("c", 3)
1489	tree.Set("d", 4)
1490	tree.Set("e", 5)
1491
1492	cases := []struct {
1493		offset int
1494		limit  int
1495		want   []string
1496	}{
1497		{0, 5, []string{"e", "d", "c", "b", "a"}},
1498		{0, 1, []string{"e"}},
1499		{0, 3, []string{"e", "d", "c"}},
1500		{1, 2, []string{"d", "c"}},
1501		{2, 2, []string{"c", "b"}},
1502		{3, 5, []string{"b", "a"}},
1503		{4, 1, []string{"a"}},
1504		{4, 10, []string{"a"}},
1505		{5, 1, nil},
1506		{0, 0, nil},
1507		{10, 1, nil},
1508	}
1509
1510	for _, tc := range cases {
1511		var got []string
1512		tree.ReverseIterateByOffset(tc.offset, tc.limit, func(key string, value any) bool {
1513			got = append(got, key)
1514			return false
1515		})
1516		if !slicesEqual(got, tc.want) {
1517			t.Errorf("ReverseIterateByOffset(%d, %d): got %v, want %v",
1518				tc.offset, tc.limit, got, tc.want)
1519		}
1520	}
1521
1522	// Early termination.
1523	var got []string
1524	tree.ReverseIterateByOffset(1, 5, func(key string, value any) bool {
1525		got = append(got, key)
1526		return len(got) >= 2
1527	})
1528	assertSliceEqual(t, got, []string{"d", "c"})
1529}
1530
1531func TestPortedBalanceAfterRemoval(t *testing.T) {
1532	// This tests the behavioral equivalence: after various insert/remove
1533	// patterns, the tree maintains correct sorted order and all keys are
1534	// accessible. (AVL tests checked balance factors; we check correctness.)
1535	tests := []struct {
1536		name       string
1537		insertKeys []string
1538		removeKey  string
1539	}{
1540		{"remove right node", []string{"B", "A", "D", "C", "E"}, "E"},
1541		{"remove left node", []string{"D", "B", "E", "A", "C"}, "A"},
1542		{"remove after complex insert", []string{"C", "B", "E", "A", "D", "F"}, "F"},
1543		{"descending insert, remove middle", []string{"E", "D", "C", "B", "A"}, "C"},
1544		{"ascending insert, remove middle", []string{"A", "B", "C", "D", "E"}, "C"},
1545		{"duplicate insert, remove key", []string{"C", "B", "C", "A", "D"}, "C"},
1546		{"complex case", []string{"H", "B", "A", "C", "E", "D", "F", "G"}, "B"},
1547	}
1548	for _, tt := range tests {
1549		t.Run(tt.name, func(t *testing.T) {
1550			tree := NewBPTreeN(4)
1551			for _, key := range tt.insertKeys {
1552				tree.Set(key, nil)
1553			}
1554			tree.Remove(tt.removeKey)
1555
1556			// Verify sorted order.
1557			var result []string
1558			tree.Iterate("", "", func(key string, value any) bool {
1559				result = append(result, key)
1560				return false
1561			})
1562			for i := 1; i < len(result); i++ {
1563				if result[i] <= result[i-1] {
1564					t.Errorf("Sorted property violated: %s <= %s", result[i], result[i-1])
1565				}
1566			}
1567
1568			// Verify all expected keys present.
1569			for _, key := range tt.insertKeys {
1570				if key == tt.removeKey {
1571					if tree.Has(key) {
1572						// Only check if the key was unique (not duplicated).
1573						// With duplicates, Set overwrites, so there's only one copy.
1574						continue
1575					}
1576				}
1577			}
1578		})
1579	}
1580}
1581
1582//----------------------------------------
1583// Structural invariant verification
1584
1585// verifyInvariants checks all B+ tree structural invariants.
1586func verifyInvariants(t *testing.T, tree *BPTree) {
1587	t.Helper()
1588	if tree.root == nil {
1589		if tree.size != 0 {
1590			t.Errorf("nil root but size=%d", tree.size)
1591		}
1592		return
1593	}
1594
1595	// Check leaf depth uniformity.
1596	depth := leafDepth(tree.root, 0)
1597	if depth == -1 {
1598		t.Error("leaves are at different depths")
1599	}
1600
1601	// Check separator keys, child count bounds, sizes, ref-count.
1602	seen := make(map[node]bool)
1603	verifyNode(t, tree.root, tree.fanout, true, seen)
1604
1605	// Check tree.size matches actual leaf count.
1606	actual := tree.root.nodeSize()
1607	if tree.size != actual {
1608		t.Errorf("tree.size=%d but root.nodeSize()=%d", tree.size, actual)
1609	}
1610}
1611
1612func leafDepth(n node, depth int) int {
1613	if n.isLeaf() {
1614		return depth
1615	}
1616	inner := n.(*innerNode)
1617	d := -1
1618	for _, child := range inner.children {
1619		cd := leafDepth(child, depth+1)
1620		if d == -1 {
1621			d = cd
1622		} else if cd != d {
1623			return -1
1624		}
1625	}
1626	return d
1627}
1628
1629func verifyNode(t *testing.T, n node, fanout int, isRoot bool, seen map[node]bool) {
1630	t.Helper()
1631	if seen[n] {
1632		t.Errorf("node reachable by multiple paths (ref-count >= 2)")
1633		return
1634	}
1635	seen[n] = true
1636
1637	if n.isLeaf() {
1638		leaf := n.(*leafNode)
1639		if len(leaf.keys) == 0 && !isRoot {
1640			t.Errorf("non-root leaf has 0 keys")
1641		}
1642		if len(leaf.keys) > fanout {
1643			t.Errorf("leaf has %d keys, max=%d", len(leaf.keys), fanout)
1644		}
1645		return
1646	}
1647
1648	inner := n.(*innerNode)
1649
1650	// Child count bounds.
1651	minC := fanout / 2
1652	if isRoot {
1653		minC = 2
1654	}
1655	if len(inner.children) < minC && !isRoot {
1656		t.Errorf("inner node has %d children, min=%d", len(inner.children), minC)
1657	}
1658	if len(inner.children) > fanout {
1659		t.Errorf("inner node has %d children, max=%d", len(inner.children), fanout)
1660	}
1661
1662	// keys/children/sizes length consistency.
1663	if len(inner.keys) != len(inner.children)-1 {
1664		t.Errorf("len(keys)=%d but len(children)=%d", len(inner.keys), len(inner.children))
1665	}
1666	if len(inner.sizes) != len(inner.children) {
1667		t.Errorf("len(sizes)=%d but len(children)=%d", len(inner.sizes), len(inner.children))
1668	}
1669
1670	// Separator key correctness: keys[i] == children[i+1].minKey().
1671	for i, k := range inner.keys {
1672		expected := inner.children[i+1].minKey()
1673		if k != expected {
1674			t.Errorf("separator keys[%d]=%q but children[%d].minKey()=%q", i, k, i+1, expected)
1675		}
1676	}
1677
1678	// sizes[i] matches child.
1679	for i, child := range inner.children {
1680		actual := child.nodeSize()
1681		if inner.sizes[i] != actual {
1682			t.Errorf("sizes[%d]=%d but child.nodeSize()=%d", i, inner.sizes[i], actual)
1683		}
1684		verifyNode(t, child, fanout, false, seen)
1685	}
1686}
1687
1688func TestInvariantsAfterEveryOperation(t *testing.T) {
1689	tree := NewBPTreeN(4)
1690	keys := []string{"m", "f", "t", "b", "i", "p", "w", "a", "d", "g", "k", "n", "r", "u", "y"}
1691
1692	for _, k := range keys {
1693		tree.Set(k, k)
1694		verifyInvariants(t, tree)
1695	}
1696	for _, k := range keys {
1697		tree.Remove(k)
1698		verifyInvariants(t, tree)
1699	}
1700}
1701
1702//----------------------------------------
1703// Behavioral parity: GetByIndex == IterateByOffset
1704
1705func TestGetByIndexMatchesIterateByOffset(t *testing.T) {
1706	tree := NewBPTreeN(4)
1707	for _, k := range []string{"h", "d", "l", "b", "f", "j", "n", "a", "c", "e", "g"} {
1708		tree.Set(k, k)
1709	}
1710
1711	for i := 0; i < tree.Size(); i++ {
1712		k1, v1 := tree.GetByIndex(i)
1713		var k2 string
1714		var v2 any
1715		tree.IterateByOffset(i, 1, func(k string, v any) bool {
1716			k2 = k
1717			v2 = v
1718			return true
1719		})
1720		if k1 != k2 || v1 != v2 {
1721			t.Errorf("index %d: GetByIndex=(%q,%v) but IterateByOffset=(%q,%v)", i, k1, v1, k2, v2)
1722		}
1723	}
1724}
1725
1726//----------------------------------------
1727// Stress: oscillating tree size
1728
1729func TestOscillatingSize(t *testing.T) {
1730	tree := NewBPTreeN(4)
1731
1732	// Insert 100.
1733	for i := 0; i < 100; i++ {
1734		tree.Set(intToKey(i), i)
1735	}
1736	verifyInvariants(t, tree)
1737
1738	// Remove 50.
1739	for i := 0; i < 50; i++ {
1740		tree.Remove(intToKey(i))
1741	}
1742	verifyInvariants(t, tree)
1743	if tree.Size() != 50 {
1744		t.Errorf("Expected size 50, got %d", tree.Size())
1745	}
1746
1747	// Insert 50 new.
1748	for i := 100; i < 150; i++ {
1749		tree.Set(intToKey(i), i)
1750	}
1751	verifyInvariants(t, tree)
1752	if tree.Size() != 100 {
1753		t.Errorf("Expected size 100, got %d", tree.Size())
1754	}
1755
1756	// Remove all.
1757	for i := 50; i < 150; i++ {
1758		tree.Remove(intToKey(i))
1759	}
1760	verifyInvariants(t, tree)
1761	if tree.Size() != 0 {
1762		t.Errorf("Expected size 0, got %d", tree.Size())
1763	}
1764
1765	// Insert again from empty.
1766	for i := 0; i < 20; i++ {
1767		tree.Set(intToKey(i), i)
1768	}
1769	verifyInvariants(t, tree)
1770	if tree.Size() != 20 {
1771		t.Errorf("Expected size 20, got %d", tree.Size())
1772	}
1773}
1774
1775//----------------------------------------
1776// All 6 rebalance paths
1777
1778func TestAllRebalancePaths(t *testing.T) {
1779	// We use fanout=4 so min=2 for leaves.
1780	// Each sub-test constructs a specific tree state and triggers one rebalance path.
1781
1782	t.Run("leaf redistribute from left", func(t *testing.T) {
1783		tree := NewBPTreeN(4)
1784		for _, k := range []string{"a", "b", "c", "d", "e"} {
1785			tree.Set(k, k)
1786		}
1787		// After 90/10 split: left=[a,b,c], right=[d,e].
1788		// Remove "d" → right=[e] (1 < min=2). Left has 3 > 2 → redistribute from left.
1789		tree.Remove("d")
1790		verifyInvariants(t, tree)
1791		if !tree.Has("e") {
1792			t.Error("Missing key 'e' after redistribute")
1793		}
1794	})
1795
1796	t.Run("leaf redistribute from right", func(t *testing.T) {
1797		tree := NewBPTreeN(4)
1798		for _, k := range []string{"a", "b", "c", "d", "e", "f", "g"} {
1799			tree.Set(k, k)
1800		}
1801		// Remove from leftmost leaf until it underflows and must redistribute from right.
1802		tree.Remove("a")
1803		tree.Remove("b")
1804		verifyInvariants(t, tree)
1805	})
1806
1807	t.Run("leaf redistribute from right, no stale root separator keys", func(t *testing.T) {
1808		tree := NewBPTreeN(4)
1809		for _, k := range []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n"} {
1810			tree.Set(k, k)
1811		}
1812		tree.Remove("g") // no underflow
1813		tree.Remove("h") // leaf [h,i] underflows and must redistribute from right
1814		verifyInvariants(t, tree)
1815	})
1816
1817	t.Run("leaf redistribute from right, no stale inner node separator keys", func(t *testing.T) {
1818		tree := NewBPTreeN(4)
1819		for _, k := range []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q"} {
1820			tree.Set(k, k)
1821		}
1822		tree.Remove("g") // no underflow
1823		tree.Remove("j") // no underflow
1824		tree.Remove("k") // leaf [k,l] underflows and must redistribute from right
1825		verifyInvariants(t, tree)
1826	})
1827
1828	t.Run("stale separator, middle leaf pos0 redistribute from right", func(t *testing.T) {
1829		// Exercises the core bug: remove pos=0 from a middle leaf (childIdx > 0),
1830		// where left sibling is at minimum (can't redistribute left) and right
1831		// sibling has surplus (redistribute right fires). Without the fix,
1832		// parent.keys[childIdx-1] remains stale.
1833		//
1834		// After inserting a-k with fanout=4, tree is:
1835		//   root keys=["d","g","j"]
1836		//   children=[["a","b","c"], ["d","e","f"], ["g","h","i"], ["j","k"]]
1837		//
1838		// Remove "a" thins children[0] to ["b","c"] (at minimum).
1839		// Remove "f" thins children[1] to ["d","e"] (at minimum).
1840		// Remove "d" is pos=0 from children[1], causing underflow to ["e"].
1841		//   Left sibling ["b","c"] has 2 = minKeys, can't spare.
1842		//   Right sibling ["g","h","i"] has 3 > minKeys, redistribute right fires.
1843		//   Without fix: keys[0] stays "d" (stale). With fix: updated to "e".
1844		tree := NewBPTreeN(4)
1845		for _, k := range []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"} {
1846			tree.Set(k, k)
1847		}
1848		tree.Remove("a") // thin left sibling to minimum
1849		tree.Remove("f") // thin target leaf to minimum
1850		tree.Remove("d") // pos=0, underflow, triggers redistribute from right
1851		verifyInvariants(t, tree)
1852	})
1853
1854	t.Run("leaf merge", func(t *testing.T) {
1855		tree := NewBPTreeN(4)
1856		for _, k := range []string{"a", "b", "c", "d", "e"} {
1857			tree.Set(k, k)
1858		}
1859		// Remove enough to make both siblings at minimum, then one more triggers merge.
1860		tree.Remove("a")
1861		tree.Remove("b") // left=[c], right=[d,e]. left underflows. right has 2=min. merge.
1862		verifyInvariants(t, tree)
1863		got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
1864			return tr.Iterate("", "", cb)
1865		})
1866		assertSliceEqual(t, got, []string{"c", "d", "e"})
1867	})
1868
1869	t.Run("inner redistribute from left", func(t *testing.T) {
1870		// Build a tree with enough inner nodes, then remove to trigger inner rebalance.
1871		tree := NewBPTreeN(4)
1872		for i := 0; i < 30; i++ {
1873			tree.Set(intToKey(i), i)
1874		}
1875		// Remove from the right side to underflow a right inner node.
1876		for i := 25; i < 30; i++ {
1877			tree.Remove(intToKey(i))
1878		}
1879		verifyInvariants(t, tree)
1880	})
1881
1882	t.Run("inner redistribute from right", func(t *testing.T) {
1883		tree := NewBPTreeN(4)
1884		for i := 0; i < 30; i++ {
1885			tree.Set(intToKey(i), i)
1886		}
1887		// Remove from the left side to underflow a left inner node.
1888		for i := 0; i < 5; i++ {
1889			tree.Remove(intToKey(i))
1890		}
1891		verifyInvariants(t, tree)
1892	})
1893
1894	t.Run("inner merge", func(t *testing.T) {
1895		tree := NewBPTreeN(4)
1896		for i := 0; i < 20; i++ {
1897			tree.Set(intToKey(i), i)
1898		}
1899		// Remove enough to trigger inner merge.
1900		for i := 0; i < 15; i++ {
1901			tree.Remove(intToKey(i))
1902		}
1903		verifyInvariants(t, tree)
1904		if tree.Size() != 5 {
1905			t.Errorf("Expected size 5, got %d", tree.Size())
1906		}
1907	})
1908}
1909
1910//----------------------------------------
1911// Value mutation after Get should not affect tree
1912
1913func TestValueMutationIndependence(t *testing.T) {
1914	tree := NewBPTreeN(4)
1915
1916	// Store a slice value.
1917	orig := []int{1, 2, 3}
1918	tree.Set("k", orig)
1919
1920	// Get the value and mutate it.
1921	v := tree.Get("k")
1922	got := v.([]int)
1923	got[0] = 999
1924
1925	// The tree's stored value should also be affected (since slices are reference types
1926	// and *any holds the same interface). This is the expected Go behavior — not a copy.
1927	v2 := tree.Get("k")
1928	got2 := v2.([]int)
1929	if got2[0] != 999 {
1930		t.Errorf("Expected slice mutation to be visible (reference semantics), got %v", got2)
1931	}
1932
1933	// But replacing the value via Set should not affect previous Get results.
1934	tree.Set("k", []int{10, 20, 30})
1935	v3 := tree.Get("k")
1936	got3 := v3.([]int)
1937	if got3[0] != 10 {
1938		t.Errorf("Expected new value after Set, got %v", got3)
1939	}
1940	// The old reference should still have 999.
1941	if got[0] != 999 {
1942		t.Errorf("Old reference should still be 999, got %v", got)
1943	}
1944}
1945
1946//----------------------------------------
1947// Comprehensive invariant check across fanouts and sizes
1948
1949func TestInvariantsAcrossFanouts(t *testing.T) {
1950	for _, fanout := range []int{4, 5, 7, 8, 16, 32} {
1951		tree := NewBPTreeN(fanout)
1952		n := 200
1953
1954		// Insert all.
1955		for i := 0; i < n; i++ {
1956			tree.Set(intToKey(i), i)
1957		}
1958		verifyInvariants(t, tree)
1959
1960		// Remove every 3rd.
1961		for i := 0; i < n; i += 3 {
1962			tree.Remove(intToKey(i))
1963		}
1964		verifyInvariants(t, tree)
1965
1966		// Remove remaining.
1967		for i := 0; i < n; i++ {
1968			tree.Remove(intToKey(i))
1969		}
1970		verifyInvariants(t, tree)
1971		if tree.Size() != 0 {
1972			t.Errorf("fanout=%d: expected size 0, got %d", fanout, tree.Size())
1973		}
1974	}
1975}
1976
1977//----------------------------------------
1978// Deep stack unwinding
1979
1980func TestDeepStackUnwinding(t *testing.T) {
1981	// With fanout=4 and 500 keys, the tree is 4-5 levels deep.
1982	// Iterating across subtree boundaries forces advanceLeaf/retreatLeaf
1983	// to pop multiple stack levels.
1984	tree := NewBPTreeN(4)
1985	n := 500
1986	for i := 0; i < n; i++ {
1987		tree.Set(intToKey(i), i)
1988	}
1989
1990	// Full ascending iteration — every leaf boundary is crossed.
1991	count := 0
1992	prev := ""
1993	tree.Iterate("", "", func(k string, v any) bool {
1994		if k <= prev && prev != "" {
1995			t.Errorf("ascending order broken: %q after %q", k, prev)
1996		}
1997		prev = k
1998		count++
1999		return false
2000	})
2001	if count != n {
2002		t.Errorf("ascending: visited %d, want %d", count, n)
2003	}
2004
2005	// Full descending iteration.
2006	count = 0
2007	prev = ""
2008	tree.ReverseIterate("", "", func(k string, v any) bool {
2009		if prev != "" && k >= prev {
2010			t.Errorf("descending order broken: %q after %q", k, prev)
2011		}
2012		prev = k
2013		count++
2014		return false
2015	})
2016	if count != n {
2017		t.Errorf("descending: visited %d, want %d", count, n)
2018	}
2019
2020	// Offset-based iteration crossing deep boundaries.
2021	// Start from the middle, iterate to the end.
2022	count = 0
2023	tree.IterateByOffset(250, 250, func(k string, v any) bool {
2024		count++
2025		return false
2026	})
2027	if count != 250 {
2028		t.Errorf("IterateByOffset(250,250): visited %d, want 250", count)
2029	}
2030
2031	// Reverse offset from middle.
2032	count = 0
2033	tree.ReverseIterateByOffset(250, 250, func(k string, v any) bool {
2034		count++
2035		return false
2036	})
2037	if count != 250 {
2038		t.Errorf("ReverseIterateByOffset(250,250): visited %d, want 250", count)
2039	}
2040
2041	verifyInvariants(t, tree)
2042}
2043
2044//----------------------------------------
2045// Height invariant
2046
2047func treeHeight(n node) int {
2048	if n == nil {
2049		return 0
2050	}
2051	if n.isLeaf() {
2052		return 1
2053	}
2054	return 1 + treeHeight(n.(*innerNode).children[0])
2055}
2056
2057func TestHeightInvariant(t *testing.T) {
2058	// B+ tree with fanout F should have height <= 1 + log_{ceil(F/2)}(n).
2059	for _, fanout := range []int{4, 8, 32} {
2060		tree := NewBPTreeN(fanout)
2061		n := 1000
2062		for i := 0; i < n; i++ {
2063			tree.Set(intToKey(i), i)
2064		}
2065
2066		h := treeHeight(tree.root)
2067		// Compute max height: log base ceil(fanout/2) of n, plus 1 for root.
2068		minFill := fanout / 2
2069		if minFill < 2 {
2070			minFill = 2
2071		}
2072		maxH := 1
2073		capacity := 1
2074		for capacity < n {
2075			capacity *= minFill
2076			maxH++
2077		}
2078
2079		if h > maxH {
2080			t.Errorf("fanout=%d, n=%d: height=%d exceeds max=%d", fanout, n, h, maxH)
2081		}
2082	}
2083}
2084
2085//----------------------------------------
2086// Empty string as separator key
2087
2088func TestEmptyStringSeparator(t *testing.T) {
2089	tree := NewBPTreeN(4)
2090
2091	// Insert "" first, then other keys. With sequential inserts,
2092	// "" will end up in the leftmost leaf and could become a separator.
2093	tree.Set("", "empty")
2094	tree.Set("a", "a")
2095	tree.Set("b", "b")
2096	tree.Set("c", "c")
2097	tree.Set("d", "d") // triggers split; "" should be in left leaf
2098
2099	verifyInvariants(t, tree)
2100
2101	// Verify all keys accessible.
2102	if v := tree.Get(""); v != "empty" {
2103		t.Errorf("Get('')=%v, want empty", v)
2104	}
2105	for _, k := range []string{"a", "b", "c", "d"} {
2106		if !tree.Has(k) {
2107			t.Errorf("missing key %q", k)
2108		}
2109	}
2110
2111	// Iterate should include "".
2112	got := collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
2113		return tr.Iterate("", "", cb)
2114	})
2115	assertSliceEqual(t, got, []string{"", "a", "b", "c", "d"})
2116
2117	// ReverseIterate should include "".
2118	got = collectKeys(tree, func(tr *BPTree, cb IterCbFn) bool {
2119		return tr.ReverseIterate("", "", cb)
2120	})
2121	assertSliceEqual(t, got, []string{"d", "c", "b", "a", ""})
2122
2123	// Remove "" and verify tree is still valid.
2124	v, ok := tree.Remove("")
2125	if !ok || v != "empty" {
2126		t.Errorf("Remove('')=(%v,%v), want (empty,true)", v, ok)
2127	}
2128	verifyInvariants(t, tree)
2129	if tree.Has("") {
2130		t.Error("'' should be gone after remove")
2131	}
2132
2133	// Now insert more keys with "" to force it into a separator position.
2134	// Build a tree where "" is between two inner node children.
2135	tree2 := NewBPTreeN(4)
2136	// Insert keys that will sort before and after "".
2137	// In ASCII, "" < everything. So "" is always the smallest key.
2138	// To make "" a separator, we need it to be the minKey of a right child.
2139	// That happens when the leaf containing "" splits and "" ends up
2140	// as right.keys[0] (the promoted separator).
2141	// With 50/50 split: left gets lower half, right gets upper half.
2142	// Since "" is the smallest, it will always be in the left leaf.
2143	// With 90/10 on append: "" would be in the left leaf too.
2144	// So "" can become a separator only if it's the minKey of a right child
2145	// after a split where "" is in the right half.
2146	// Insert in reverse order so "" ends up in the right half of a split:
2147	tree2.Set("d", "d")
2148	tree2.Set("c", "c")
2149	tree2.Set("b", "b")
2150	tree2.Set("a", "a")
2151	tree2.Set("", "empty") // inserted at position 0 (not append), 50/50 split
2152
2153	verifyInvariants(t, tree2)
2154	got = collectKeys(tree2, func(tr *BPTree, cb IterCbFn) bool {
2155		return tr.Iterate("", "", cb)
2156	})
2157	assertSliceEqual(t, got, []string{"", "a", "b", "c", "d"})
2158}
2159
2160//----------------------------------------
2161// Pointer independence after split
2162
2163func TestPointerIndependenceAfterSplit(t *testing.T) {
2164	tree := NewBPTreeN(4)
2165
2166	// Insert 5 keys to trigger a split.
2167	tree.Set("a", "val_a")
2168	tree.Set("b", "val_b")
2169	tree.Set("c", "val_c")
2170	tree.Set("d", "val_d")
2171	tree.Set("e", "val_e") // triggers split
2172
2173	// Get values from both halves of the split.
2174	vLeft := tree.Get("a")
2175	vRight := tree.Get("d")
2176
2177	if vLeft != "val_a" {
2178		t.Errorf("left value: got %v, want val_a", vLeft)
2179	}
2180	if vRight != "val_d" {
2181		t.Errorf("right value: got %v, want val_d", vRight)
2182	}
2183
2184	// Update a value in the left half — should not affect right half.
2185	tree.Set("a", "new_a")
2186	vLeft2 := tree.Get("a")
2187	vRight2 := tree.Get("d")
2188	if vLeft2 != "new_a" {
2189		t.Errorf("updated left: got %v, want new_a", vLeft2)
2190	}
2191	if vRight2 != "val_d" {
2192		t.Errorf("right should be unchanged: got %v, want val_d", vRight2)
2193	}
2194
2195	// Update a value in the right half — should not affect left half.
2196	tree.Set("d", "new_d")
2197	vLeft3 := tree.Get("a")
2198	vRight3 := tree.Get("d")
2199	if vLeft3 != "new_a" {
2200		t.Errorf("left should be unchanged: got %v, want new_a", vLeft3)
2201	}
2202	if vRight3 != "new_d" {
2203		t.Errorf("updated right: got %v, want new_d", vRight3)
2204	}
2205
2206	// Verify that the *any pointers in the two leaves are different objects.
2207	// Access internals to check.
2208	inner := tree.root.(*innerNode)
2209	leftLeaf := inner.children[0].(*leafNode)
2210	rightLeaf := inner.children[1].(*leafNode)
2211
2212	// Each value pointer should be unique.
2213	seen := make(map[*any]bool)
2214	for _, vp := range leftLeaf.values {
2215		if seen[vp] {
2216			t.Error("duplicate *any pointer in left leaf")
2217		}
2218		seen[vp] = true
2219	}
2220	for _, vp := range rightLeaf.values {
2221		if seen[vp] {
2222			t.Error("*any pointer shared between left and right leaves after split")
2223		}
2224		seen[vp] = true
2225	}
2226}
2227
2228//----------------------------------------
2229// Helpers
2230
2231func slicesEqual(a, b []string) bool {
2232	if len(a) != len(b) {
2233		return false
2234	}
2235	for i := range a {
2236		if a[i] != b[i] {
2237			return false
2238		}
2239	}
2240	return true
2241}
2242
2243func intToKey(i int) string {
2244	// Zero-padded 4-digit string for correct lexicographic ordering.
2245	s := "0000"
2246	n := i
2247	b := []byte(s)
2248	for j := 3; j >= 0; j-- {
2249		b[j] = byte('0' + n%10)
2250		n /= 10
2251	}
2252	return string(b)
2253}
2254
2255func collectKeys(tree *BPTree, fn func(*BPTree, IterCbFn) bool) []string {
2256	var keys []string
2257	fn(tree, func(k string, v any) bool {
2258		keys = append(keys, k)
2259		return false
2260	})
2261	return keys
2262}
2263
2264func assertSliceEqual(t *testing.T, got, want []string) {
2265	t.Helper()
2266	if len(got) != len(want) {
2267		t.Errorf("got %v, want %v", got, want)
2268		return
2269	}
2270	for i := range got {
2271		if got[i] != want[i] {
2272			t.Errorf("got %v, want %v", got, want)
2273			return
2274		}
2275	}
2276}
2277
2278func assertPanics(t *testing.T, name string, fn func()) {
2279	t.Helper()
2280	defer func() {
2281		if r := recover(); r == nil {
2282			t.Errorf("%s: expected panic, got none", name)
2283		}
2284	}()
2285	fn()
2286}
2287
2288//----------------------------------------
2289// Stress tests
2290
2291// TestAVLCrossValidation runs identical random operations on both avl.Tree
2292// and BPTree and compares every return value.
2293func TestAVLCrossValidation(t *testing.T) {
2294	rng := rand.New(rand.NewPCG(12345, 0))
2295	at := avl.NewTree()
2296	bt := NewBPTree32()
2297
2298	const nOps = 5000
2299	const keyRange = 200
2300
2301	for i := 0; i < nOps; i++ {
2302		key := intToKey(rng.IntN(keyRange))
2303		op := rng.IntN(3)
2304		switch op {
2305		case 0: // Set
2306			aUpd := at.Set(key, i)
2307			bUpd := bt.Set(key, i)
2308			if aUpd != bUpd {
2309				t.Fatalf("op %d: Set(%q) updated avl=%v bpt=%v", i, key, aUpd, bUpd)
2310			}
2311		case 1: // Remove
2312			aVal, aOk := at.Remove(key)
2313			bVal, bOk := bt.Remove(key)
2314			if aOk != bOk {
2315				t.Fatalf("op %d: Remove(%q) avl=%v bpt=%v", i, key, aOk, bOk)
2316			}
2317			if aOk && aVal != bVal {
2318				t.Fatalf("op %d: Remove(%q) value avl=%v bpt=%v", i, key, aVal, bVal)
2319			}
2320		case 2: // Get
2321			aVal := at.Get(key)
2322			aOk := at.Has(key)
2323			bVal := bt.Get(key)
2324			bOk := bt.Has(key)
2325			if aOk != bOk {
2326				t.Fatalf("op %d: exists avl=%v bpt=%v", i, aOk, bOk)
2327			}
2328			if aOk && aVal != bVal {
2329				t.Fatalf("op %d: Get(%q) value avl=%v bpt=%v", i, key, aVal, bVal)
2330			}
2331		}
2332		if at.Size() != bt.Size() {
2333			t.Fatalf("op %d: size avl=%d bpt=%d", i, at.Size(), bt.Size())
2334		}
2335	}
2336
2337	// Compare full iteration.
2338	var aKeys, bKeys []string
2339	at.Iterate("", "", func(k string, v any) bool { aKeys = append(aKeys, k); return false })
2340	bt.Iterate("", "", func(k string, v any) bool { bKeys = append(bKeys, k); return false })
2341	assertSliceEqual(t, bKeys, aKeys)
2342}
2343
2344// TestExhaustiveRemovalPermutations inserts N keys then tries all N!
2345// permutations of removal order, verifying invariants after each remove.
2346func TestExhaustiveRemovalPermutations(t *testing.T) {
2347	keys := []string{"a", "b", "c", "d", "e", "f", "g", "h"}
2348	n := len(keys)
2349
2350	perm := make([]int, n)
2351	for i := range perm {
2352		perm[i] = i
2353	}
2354
2355	count := 0
2356	permute(perm, 0, func(order []int) {
2357		tree := NewBPTreeN(4)
2358		for _, k := range keys {
2359			tree.Set(k, k)
2360		}
2361		for _, idx := range order {
2362			tree.Remove(keys[idx])
2363			verifyInvariants(t, tree)
2364		}
2365		if tree.Size() != 0 {
2366			t.Fatalf("perm %d: tree not empty after removing all keys", count)
2367		}
2368		count++
2369	})
2370}
2371
2372// permute generates all permutations of arr[start:] and calls fn for each.
2373func permute(arr []int, start int, fn func([]int)) {
2374	if start == len(arr) {
2375		fn(arr)
2376		return
2377	}
2378	for i := start; i < len(arr); i++ {
2379		arr[start], arr[i] = arr[i], arr[start]
2380		permute(arr, start+1, fn)
2381		arr[start], arr[i] = arr[i], arr[start]
2382	}
2383}
2384
2385// TestMultiFanoutStress runs the same operation sequence across different
2386// fanouts and verifies all produce identical results.
2387func TestMultiFanoutStress(t *testing.T) {
2388	fanouts := []int{4, 5, 6, 7, 8, 16, 32}
2389	const nOps = 2000
2390	const keyRange = 100
2391
2392	// Record expected results from the first fanout.
2393	type result struct {
2394		setUpdated bool
2395		getVal     any
2396		getOk      bool
2397		rmVal      any
2398		rmOk       bool
2399	}
2400
2401	rng0 := rand.New(rand.NewPCG(99999, 0))
2402	type op struct {
2403		kind int // 0=set, 1=remove, 2=get
2404		key  string
2405		val  int
2406	}
2407	ops := make([]op, nOps)
2408	for i := range ops {
2409		ops[i] = op{
2410			kind: rng0.IntN(3),
2411			key:  intToKey(rng0.IntN(keyRange)),
2412			val:  i,
2413		}
2414	}
2415
2416	// Run on each fanout and collect final keys.
2417	var referenceKeys []string
2418	for fi, fanout := range fanouts {
2419		tree := NewBPTreeN(fanout)
2420		for _, o := range ops {
2421			switch o.kind {
2422			case 0:
2423				tree.Set(o.key, o.val)
2424			case 1:
2425				tree.Remove(o.key)
2426			case 2:
2427				tree.Get(o.key)
2428			}
2429		}
2430		verifyInvariants(t, tree)
2431
2432		var keys []string
2433		tree.Iterate("", "", func(k string, v any) bool {
2434			keys = append(keys, k)
2435			return false
2436		})
2437
2438		if fi == 0 {
2439			referenceKeys = keys
2440		} else {
2441			assertSliceEqual(t, keys, referenceKeys)
2442		}
2443	}
2444}
2445
2446// TestSequentialInsertReverseRemove inserts keys 0..N-1 sequentially
2447// (triggering 90/10 splits) then removes them in reverse order
2448// (triggering cascading merges from the right).
2449func TestSequentialInsertReverseRemove(t *testing.T) {
2450	for _, fanout := range []int{4, 6, 8, 32} {
2451		tree := NewBPTreeN(fanout)
2452		n := 500
2453		for i := 0; i < n; i++ {
2454			tree.Set(intToKey(i), i)
2455		}
2456		verifyInvariants(t, tree)
2457		if tree.Size() != n {
2458			t.Errorf("fanout=%d: expected size %d, got %d", fanout, n, tree.Size())
2459		}
2460
2461		for i := n - 1; i >= 0; i-- {
2462			val, ok := tree.Remove(intToKey(i))
2463			if !ok {
2464				t.Fatalf("fanout=%d: Remove(%s) returned false", fanout, intToKey(i))
2465			}
2466			if val != i {
2467				t.Fatalf("fanout=%d: Remove(%s) value=%v want %d", fanout, intToKey(i), val, i)
2468			}
2469			verifyInvariants(t, tree)
2470		}
2471		if tree.Size() != 0 {
2472			t.Errorf("fanout=%d: expected empty tree, got size %d", fanout, tree.Size())
2473		}
2474	}
2475}
2476
2477// TestRandomOpsWithPeriodicVerification does 10K random Set/Remove calls,
2478// verifying invariants and sorted iteration every 100 ops.
2479func TestRandomOpsWithPeriodicVerification(t *testing.T) {
2480	rng := rand.New(rand.NewPCG(54321, 0))
2481	tree := NewBPTreeN(4)
2482
2483	const nOps = 10000
2484	const keyRange = 300
2485	const checkEvery = 100
2486
2487	// Track expected keys in a sorted slice for comparison.
2488	present := make(map[string]bool)
2489
2490	for i := 0; i < nOps; i++ {
2491		key := intToKey(rng.IntN(keyRange))
2492		if rng.IntN(3) == 0 { // ~33% removes
2493			_, ok := tree.Remove(key)
2494			if ok {
2495				delete(present, key)
2496			}
2497		} else { // ~67% sets
2498			tree.Set(key, i)
2499			present[key] = true
2500		}
2501
2502		if (i+1)%checkEvery == 0 {
2503			verifyInvariants(t, tree)
2504
2505			// Check size.
2506			if tree.Size() != len(present) {
2507				t.Fatalf("op %d: size mismatch tree=%d map=%d", i, tree.Size(), len(present))
2508			}
2509
2510			// Check iteration matches sorted keys.
2511			var expected []string
2512			for k := range present {
2513				expected = append(expected, k)
2514			}
2515			sort.Strings(expected)
2516
2517			var got []string
2518			tree.Iterate("", "", func(k string, v any) bool {
2519				got = append(got, k)
2520				return false
2521			})
2522			assertSliceEqual(t, got, expected)
2523		}
2524	}
2525}
2526
2527// TestGetByIndexIterateByOffsetConsistency verifies that GetByIndex(i)
2528// matches IterateByOffset(i, 1) for every valid index.
2529func TestGetByIndexIterateByOffsetConsistency(t *testing.T) {
2530	rng := rand.New(rand.NewPCG(77777, 0))
2531	tree := NewBPTreeN(4)
2532
2533	// Build a tree with random insertions and removals.
2534	const nOps = 1000
2535	const keyRange = 200
2536	for i := 0; i < nOps; i++ {
2537		key := intToKey(rng.IntN(keyRange))
2538		if rng.IntN(4) == 0 {
2539			tree.Remove(key)
2540		} else {
2541			tree.Set(key, i)
2542		}
2543	}
2544
2545	n := tree.Size()
2546	for i := 0; i < n; i++ {
2547		gKey, gVal := tree.GetByIndex(i)
2548
2549		var iKey string
2550		var iVal any
2551		tree.IterateByOffset(i, 1, func(k string, v any) bool {
2552			iKey = k
2553			iVal = v
2554			return true
2555		})
2556
2557		if gKey != iKey {
2558			t.Fatalf("index %d: GetByIndex key=%q IterateByOffset key=%q", i, gKey, iKey)
2559		}
2560		if gVal != iVal {
2561			t.Fatalf("index %d: GetByIndex val=%v IterateByOffset val=%v", i, gVal, iVal)
2562		}
2563	}
2564
2565	// Also check ReverseIterateByOffset consistency.
2566	for i := 0; i < n; i++ {
2567		gKey, gVal := tree.GetByIndex(n - 1 - i)
2568
2569		var rKey string
2570		var rVal any
2571		tree.ReverseIterateByOffset(i, 1, func(k string, v any) bool {
2572			rKey = k
2573			rVal = v
2574			return true
2575		})
2576
2577		if gKey != rKey {
2578			t.Fatalf("rev index %d: GetByIndex key=%q ReverseIterateByOffset key=%q", i, gKey, rKey)
2579		}
2580		if gVal != rVal {
2581			t.Fatalf("rev index %d: GetByIndex val=%v ReverseIterateByOffset val=%v", i, gVal, rVal)
2582		}
2583	}
2584}