From cb77afbb367c3d319a533549c7a7abb8b438f9be Mon Sep 17 00:00:00 2001 From: moul Date: Thu, 5 Dec 2024 22:16:31 +0000 Subject: [PATCH] chore: update portal-loop backup --- portal-loop/README.md | 2 +- ...6001-6570.jsonl => backup_portal_loop_txs_6001-6574.jsonl} | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) rename portal-loop/{backup_portal_loop_txs_6001-6570.jsonl => backup_portal_loop_txs_6001-6574.jsonl} (99%) diff --git a/portal-loop/README.md b/portal-loop/README.md index 6c254d38..f5c18232 100644 --- a/portal-loop/README.md +++ b/portal-loop/README.md @@ -2,7 +2,7 @@ ## TXs ``` -6627 +6631 ``` ## addpkgs diff --git a/portal-loop/backup_portal_loop_txs_6001-6570.jsonl b/portal-loop/backup_portal_loop_txs_6001-6574.jsonl similarity index 99% rename from portal-loop/backup_portal_loop_txs_6001-6570.jsonl rename to portal-loop/backup_portal_loop_txs_6001-6574.jsonl index f88c5798..2ff193da 100644 --- a/portal-loop/backup_portal_loop_txs_6001-6570.jsonl +++ b/portal-loop/backup_portal_loop_txs_6001-6574.jsonl @@ -568,3 +568,7 @@ {"tx":{"msg":[{"@type":"/vm.m_addpkg","creator":"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5","package":{"name":"xorshift64star","path":"gno.land/p/wyhaines/rand/xorshift64star","files":[{"name":"xorshift64star.gno","body":"// Xorshift64* is a very fast psuedo-random number generation algorithm with strong\n// statistical properties.\n//\n// The default random number algorithm in gno was ported from Go's v2 rand implementatoon, which\n// defaults to the PCG algorithm. This algorithm is commonly used in language PRNG implementations\n// because it has modest seeding requirements, and generates statistically strong randomness.\n//\n// This package provides an implementation of the Xorshift64* PRNG algorithm. This algorithm provides\n// strong statistical performance with most seeds (just don't seed it with zero), and the performance\n// of this implementation in Gno is more than four times faster than the default PCG implementation in\n// `math/rand`.\n//\n//\tBenchmark\n//\t---------\n//\tPCG: 1000000 Uint64 generated in 15.58s\n//\tXorshift64*: 1000000 Uint64 generated in 3.77s\n//\tRatio: x4.11 times faster than PCG\n//\n// Use it directly:\n//\n//\tprng = xorshift64star.New() // pass a uint64 to seed it or pass nothing to seed it with entropy\n//\n// Or use it as a drop-in replacement for the default PRNT in Rand:\n//\n//\tsource = xorshift64star.New()\n//\tprng := rand.New(source)\npackage xorshift64star\n\nimport (\n\t\"errors\"\n\t\"math\"\n\n\t\"gno.land/p/demo/entropy\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\n// Xorshift64Star is a PRNG that implements the Xorshift64* algorithm.\ntype Xorshift64Star struct {\n\tseed uint64\n}\n\n// New() creates a new instance of the PRNG with a given seed, which\n// should be a uint64. If no seed is provided, the PRNG will be seeded via the\n// gno.land/p/demo/entropy package.\nfunc New(seed ...uint64) *Xorshift64Star {\n\txs := \u0026Xorshift64Star{}\n\txs.Seed(seed...)\n\treturn xs\n}\n\n// Seed() implements the rand.Source interface. It provides a way to set the seed for the PRNG.\nfunc (xs *Xorshift64Star) Seed(seed ...uint64) {\n\tif len(seed) == 0 {\n\t\te := entropy.New()\n\t\txs.seed = e.Value64()\n\t} else {\n\t\txs.seed = seed[0]\n\t}\n}\n\n// beUint64() decodes a uint64 from a set of eight bytes, assuming big endian encoding.\n// binary.bigEndian.Uint64, copied to avoid dependency\nfunc beUint64(b []byte) uint64 {\n\t_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808\n\treturn uint64(b[7]) | uint64(b[6])\u003c\u003c8 | uint64(b[5])\u003c\u003c16 | uint64(b[4])\u003c\u003c24 |\n\t\tuint64(b[3])\u003c\u003c32 | uint64(b[2])\u003c\u003c40 | uint64(b[1])\u003c\u003c48 | uint64(b[0])\u003c\u003c56\n}\n\n// bePutUint64() encodes a uint64 into a buffer of eight bytes.\n// binary.bigEndian.PutUint64, copied to avoid dependency\nfunc bePutUint64(b []byte, v uint64) {\n\t_ = b[7] // early bounds check to guarantee safety of writes below\n\tb[0] = byte(v \u003e\u003e 56)\n\tb[1] = byte(v \u003e\u003e 48)\n\tb[2] = byte(v \u003e\u003e 40)\n\tb[3] = byte(v \u003e\u003e 32)\n\tb[4] = byte(v \u003e\u003e 24)\n\tb[5] = byte(v \u003e\u003e 16)\n\tb[6] = byte(v \u003e\u003e 8)\n\tb[7] = byte(v)\n}\n\n// A label to identify the marshalled data.\nvar marshalXorshift64StarLabel = []byte(\"xorshift64*:\")\n\n// MarshalBinary() returns a byte array that encodes the state of the PRNG. This can later be used\n// with UnmarshalBinary() to restore the state of the PRNG.\n// MarshalBinary implements the encoding.BinaryMarshaler interface.\nfunc (xs *Xorshift64Star) MarshalBinary() ([]byte, error) {\n\tb := make([]byte, 20)\n\tcopy(b, marshalXorshift64StarLabel)\n\tbePutUint64(b[12:], xs.seed)\n\treturn b, nil\n}\n\n// errUnmarshalXorshift64Star is returned when unmarshalling fails.\nvar errUnmarshalXorshift64Star = errors.New(\"invalid Xorshift64* encoding\")\n\n// UnmarshalBinary() restores the state of the PRNG from a byte array that was created with MarshalBinary().\n// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.\nfunc (xs *Xorshift64Star) UnmarshalBinary(data []byte) error {\n\tif len(data) != 20 || string(data[:12]) != string(marshalXorshift64StarLabel) {\n\t\treturn errUnmarshalXorshift64Star\n\t}\n\txs.seed = beUint64(data[12:])\n\treturn nil\n}\n\n// Uint64() generates the next random uint64 value.\nfunc (xs *Xorshift64Star) Uint64() uint64 {\n\txs.seed ^= xs.seed \u003e\u003e 12\n\txs.seed ^= xs.seed \u003c\u003c 25\n\txs.seed ^= xs.seed \u003e\u003e 27\n\txs.seed *= 2685821657736338717\n\treturn xs.seed // Operations naturally wrap around in uint64\n}\n\n// Until there is better benchmarking support in gno, you can test the performance of this PRNG with this function.\n// This isn't perfect, since it will include the startup time of gno in the results, but this will give you a timing\n// for generating a million random uint64 numbers on any unix based system:\n//\n// `time gno run -expr 'benchmarkXorshift64Star()' xorshift64star.gno\nfunc benchmarkXorshift64Star(_iterations ...int) {\n\titerations := 1000000\n\tif len(_iterations) \u003e 0 {\n\t\titerations = _iterations[0]\n\t}\n\txs64s := New()\n\n\tfor i := 0; i \u003c iterations; i++ {\n\t\t_ = xs64s.Uint64()\n\t}\n\tufmt.Println(ufmt.Sprintf(\"Xorshift64*: generate %d uint64\\n\", iterations))\n}\n\n// The averageXorshift64Star() function is a simple benchmarking helper to demonstrate\n// the most basic statistical property of the Xorshift64* PRNG.\nfunc averageXorshift64Star(_iterations ...int) {\n\ttarget := uint64(500000)\n\titerations := 1000000\n\tvar squares [1000000]uint64\n\n\tufmt.Println(\n\t\tufmt.Sprintf(\n\t\t\t\"Averaging %d random numbers. The average should be very close to %d.\\n\",\n\t\t\titerations,\n\t\t\ttarget))\n\n\tif len(_iterations) \u003e 0 {\n\t\titerations = _iterations[0]\n\t}\n\txs64s := New()\n\n\tvar average float64 = 0\n\tfor i := 0; i \u003c iterations; i++ {\n\t\tn := xs64s.Uint64()%(target*2) + 1\n\t\taverage += (float64(n) - average) / float64(i+1)\n\t\tsquares[i] = n\n\t}\n\n\tsum_of_squares := uint64(0)\n\t// transform numbers into their squares of the distance from the average\n\tfor i := 0; i \u003c iterations; i++ {\n\t\tdifference := average - float64(squares[i])\n\t\tsquare := uint64(difference * difference)\n\t\tsum_of_squares += square\n\t}\n\n\tufmt.Println(ufmt.Sprintf(\"Xorshift64* average of %d uint64: %f\\n\", iterations, average))\n\tufmt.Println(ufmt.Sprintf(\"Xorshift64* standard deviation : %f\\n\", math.Sqrt(float64(sum_of_squares)/float64(iterations))))\n\tufmt.Println(ufmt.Sprintf(\"Xorshift64* theoretical perfect deviation: %f\\n\", (float64(target*2)-1)/math.Sqrt(12)))\n}\n"},{"name":"xorshift64star_test.gno","body":"package xorshift64star\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n)\n\nfunc TestXorshift64StarSeeding(t *testing.T) {\n\txs64s := New()\n\tvalue1 := xs64s.Uint64()\n\n\txs64s = New(987654321)\n\tvalue2 := xs64s.Uint64()\n\n\tif value1 != 5083824587905981259 || value2 != 18211065302896784785 || value1 == value2 {\n\t\tt.Errorf(\"Expected 5083824587905981259 to be != to 18211065302896784785; got: %d == %d\", value1, value2)\n\t}\n}\n\nfunc TestXorshift64StarRand(t *testing.T) {\n\tsource := New(987654321)\n\trng := rand.New(source)\n\n\t// Expected outputs for the first 5 random floats with the given seed\n\texpected := []float64{\n\t\t.8344002228310946,\n\t\t0.01777174153236205,\n\t\t0.23521769507865276,\n\t\t0.5387610198576143,\n\t\t0.631539862225968,\n\t\t0.9369068148346704,\n\t\t0.6387002315083188,\n\t\t0.5047507613688854,\n\t\t0.5208486273732391,\n\t\t0.25023746271541747,\n\t}\n\n\tfor i, exp := range expected {\n\t\tval := rng.Float64()\n\t\tif exp != val {\n\t\t\tt.Errorf(\"Rand.Float64() at iteration %d: got %g, expected %g\", i, val, exp)\n\t\t}\n\t}\n}\n\nfunc TestXorshift64StarUint64(t *testing.T) {\n\txs64s := New()\n\n\texpected := []uint64{\n\t\t5083824587905981259,\n\t\t4607286371009545754,\n\t\t2070557085263023674,\n\t\t14094662988579565368,\n\t\t2910745910478213381,\n\t\t18037409026311016155,\n\t\t17169624916429864153,\n\t\t10459214929523155306,\n\t\t11840179828060641081,\n\t\t1198750959721587199,\n\t}\n\n\tfor i, exp := range expected {\n\t\tval := xs64s.Uint64()\n\t\tif exp != val {\n\t\t\tt.Errorf(\"Xorshift64Star.Uint64() at iteration %d: got %d, expected %d\", i, val, exp)\n\t\t}\n\t}\n}\n\nfunc TestXorshift64StarMarshalUnmarshal(t *testing.T) {\n\txs64s := New()\n\n\texpected1 := []uint64{\n\t\t5083824587905981259,\n\t\t4607286371009545754,\n\t\t2070557085263023674,\n\t\t14094662988579565368,\n\t\t2910745910478213381,\n\t}\n\n\texpected2 := []uint64{\n\t\t18037409026311016155,\n\t\t17169624916429864153,\n\t\t10459214929523155306,\n\t\t11840179828060641081,\n\t\t1198750959721587199,\n\t}\n\n\tfor i, exp := range expected1 {\n\t\tval := xs64s.Uint64()\n\t\tif exp != val {\n\t\t\tt.Errorf(\"Xorshift64Star.Uint64() at iteration %d: got %d, expected %d\", i, val, exp)\n\t\t}\n\t}\n\n\tmarshalled, err := xs64s.MarshalBinary()\n\n\tt.Logf(\"Original State: [%x]\\n\", xs64s.seed)\n\tt.Logf(\"Marshalled State: [%x] -- %v\\n\", marshalled, err)\n\tstate_before := xs64s.seed\n\n\tif err != nil {\n\t\tt.Errorf(\"Xorshift64Star.MarshalBinary() error: %v\", err)\n\t}\n\n\t// Advance state by one number; then check the next 5. The expectation is that they _will_ fail.\n\txs64s.Uint64()\n\n\tfor i, exp := range expected2 {\n\t\tval := xs64s.Uint64()\n\t\tif exp == val {\n\t\t\tt.Errorf(\" Iteration %d matched %d; which is from iteration %d; something strange is happening.\", (i + 6), val, (i + 5))\n\t\t}\n\t}\n\n\tt.Logf(\"State before unmarshall: [%x]\\n\", xs64s.seed)\n\n\t// Now restore the state of the PRNG\n\terr = xs64s.UnmarshalBinary(marshalled)\n\n\tt.Logf(\"State after unmarshall: [%x]\\n\", xs64s.seed)\n\n\tif state_before != xs64s.seed {\n\t\tt.Errorf(\"States before and after marshal/unmarshal are not equal; go %x and %x\", state_before, xs64s.seed)\n\t}\n\n\t// Now we should be back on track for the last 5 numbers\n\tfor i, exp := range expected2 {\n\t\tval := xs64s.Uint64()\n\t\tif exp != val {\n\t\t\tt.Errorf(\"Xorshift64Star.Uint64() at iteration %d: got %d, expected %d\", (i + 5), val, exp)\n\t\t}\n\t}\n}\n"}]},"deposit":""}],"fee":{"gas_wanted":"50000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":null,"signature":null}],"memo":""}} {"tx":{"msg":[{"@type":"/vm.m_addpkg","creator":"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5","package":{"name":"xorshiftr128plus","path":"gno.land/p/wyhaines/rand/xorshiftr128plus","files":[{"name":"xorshiftr128plus.gno","body":"// Xorshiftr128+ is a very fast psuedo-random number generation algorithm with strong\n// statistical properties.\n//\n// The default random number algorithm in gno was ported from Go's v2 rand implementatoon, which\n// defaults to the PCG algorithm. This algorithm is commonly used in language PRNG implementations\n// because it has modest seeding requirements, and generates statistically strong randomness.\n//\n// This package provides an implementation of the Xorshiftr128+ PRNG algorithm. This algorithm provides\n// strong statistical performance with most seeds (just don't seed it with zeros), and the performance\n// of this implementation in Gno is more than four times faster than the default PCG implementation in\n// `math/rand`.\n//\n//\tBenchmark\n//\t---------\n//\tPCG: 1000000 Uint64 generated in 15.48s\n//\tXorshiftr128+: 1000000 Uint64 generated in 3.22s\n//\tRatio: x4.81 times faster than PCG\n//\n// Use it directly:\n//\n//\tprng = xorshiftr128plus.New() // pass a uint64 to seed it or pass nothing to seed it with entropy\n//\n// Or use it as a drop-in replacement for the default PRNT in Rand:\n//\n//\tsource = xorshiftr128plus.New()\n//\tprng := rand.New(source)\npackage xorshiftr128plus\n\nimport (\n\t\"errors\"\n\t\"math\"\n\n\t\"gno.land/p/demo/entropy\"\n\t\"gno.land/p/demo/ufmt\"\n)\n\ntype Xorshiftr128Plus struct {\n\tseed [2]uint64 // Seeds\n}\n\nfunc New(seeds ...uint64) *Xorshiftr128Plus {\n\tvar s1, s2 uint64\n\tseed_length := len(seeds)\n\tif seed_length \u003c 2 {\n\t\te := entropy.New()\n\t\tif seed_length == 0 {\n\t\t\ts1 = e.Value64()\n\t\t\ts2 = e.Value64()\n\t\t} else {\n\t\t\ts1 = seeds[0]\n\t\t\ts2 = e.Value64()\n\t\t}\n\t} else {\n\t\ts1 = seeds[0]\n\t\ts2 = seeds[1]\n\t}\n\n\tprng := \u0026Xorshiftr128Plus{}\n\tprng.Seed(s1, s2)\n\treturn prng\n}\n\nfunc (x *Xorshiftr128Plus) Seed(s1, s2 uint64) {\n\tif s1 == 0 \u0026\u0026 s2 == 0 {\n\t\tpanic(\"Seeds must not both be zero\")\n\t}\n\tx.seed[0] = s1\n\tx.seed[1] = s2\n}\n\n// beUint64() decodes a uint64 from a set of eight bytes, assuming big endian encoding.\n// binary.bigEndian.Uint64, copied to avoid dependency\nfunc beUint64(b []byte) uint64 {\n\t_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808\n\treturn uint64(b[7]) | uint64(b[6])\u003c\u003c8 | uint64(b[5])\u003c\u003c16 | uint64(b[4])\u003c\u003c24 |\n\t\tuint64(b[3])\u003c\u003c32 | uint64(b[2])\u003c\u003c40 | uint64(b[1])\u003c\u003c48 | uint64(b[0])\u003c\u003c56\n}\n\n// bePutUint64() encodes a uint64 into a buffer of eight bytes.\n// binary.bigEndian.PutUint64, copied to avoid dependency\nfunc bePutUint64(b []byte, v uint64) {\n\t_ = b[7] // early bounds check to guarantee safety of writes below\n\tb[0] = byte(v \u003e\u003e 56)\n\tb[1] = byte(v \u003e\u003e 48)\n\tb[2] = byte(v \u003e\u003e 40)\n\tb[3] = byte(v \u003e\u003e 32)\n\tb[4] = byte(v \u003e\u003e 24)\n\tb[5] = byte(v \u003e\u003e 16)\n\tb[6] = byte(v \u003e\u003e 8)\n\tb[7] = byte(v)\n}\n\n// A label to identify the marshalled data.\nvar marshalXorshiftr128PlusLabel = []byte(\"xorshiftr128+:\")\n\n// MarshalBinary() returns a byte array that encodes the state of the PRNG. This can later be used\n// with UnmarshalBinary() to restore the state of the PRNG.\n// MarshalBinary implements the encoding.BinaryMarshaler interface.\nfunc (xs *Xorshiftr128Plus) MarshalBinary() ([]byte, error) {\n\tb := make([]byte, 30)\n\tcopy(b, marshalXorshiftr128PlusLabel)\n\tbePutUint64(b[14:], xs.seed[0])\n\tbePutUint64(b[22:], xs.seed[1])\n\treturn b, nil\n}\n\n// errUnmarshalXorshiftr128Plus is returned when unmarshalling fails.\nvar errUnmarshalXorshiftr128Plus = errors.New(\"invalid Xorshiftr128Plus encoding\")\n\n// UnmarshalBinary() restores the state of the PRNG from a byte array that was created with MarshalBinary().\n// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.\nfunc (xs *Xorshiftr128Plus) UnmarshalBinary(data []byte) error {\n\tif len(data) != 30 || string(data[:14]) != string(marshalXorshiftr128PlusLabel) {\n\t\treturn errUnmarshalXorshiftr128Plus\n\t}\n\txs.seed[0] = beUint64(data[14:])\n\txs.seed[1] = beUint64(data[22:])\n\treturn nil\n}\n\nfunc (x *Xorshiftr128Plus) Uint64() uint64 {\n\tx0 := x.seed[0]\n\tx1 := x.seed[1]\n\tx.seed[0] = x1\n\tx0 ^= x0 \u003c\u003c 23\n\tx0 ^= x0 \u003e\u003e 17\n\tx0 ^= x1\n\tx.seed[1] = x0 + x1\n\treturn x.seed[1]\n}\n\n// Until there is better benchmarking support in gno, you can test the performance of this PRNG with this function.\n// This isn't perfect, since it will include the startup time of gno in the results, but this will give you a timing\n// for generating a million random uint64 numbers on any unix based system:\n//\n// `time gno run -expr 'benchmarkXorshiftr128Plus()' xorshiftr128plus.gno\nfunc benchmarkXorshiftr128Plus(_iterations ...int) {\n\titerations := 1000000\n\tif len(_iterations) \u003e 0 {\n\t\titerations = _iterations[0]\n\t}\n\txs128p := New()\n\n\tfor i := 0; i \u003c iterations; i++ {\n\t\t_ = xs128p.Uint64()\n\t}\n\tufmt.Println(ufmt.Sprintf(\"Xorshiftr128Plus: generate %d uint64\\n\", iterations))\n}\n\n// The averageXorshiftr128Plus() function is a simple benchmarking helper to demonstrate\n// the most basic statistical property of the Xorshiftr128+ PRNG.\nfunc averageXorshiftr128Plus(_iterations ...int) {\n\ttarget := uint64(500000)\n\titerations := 1000000\n\tvar squares [1000000]uint64\n\n\tufmt.Println(\n\t\tufmt.Sprintf(\n\t\t\t\"Averaging %d random numbers. The average should be very close to %d.\\n\",\n\t\t\titerations,\n\t\t\ttarget))\n\n\tif len(_iterations) \u003e 0 {\n\t\titerations = _iterations[0]\n\t}\n\txs128p := New()\n\n\tvar average float64 = 0\n\tfor i := 0; i \u003c iterations; i++ {\n\t\tn := xs128p.Uint64()%(target*2) + 1\n\t\taverage += (float64(n) - average) / float64(i+1)\n\t\tsquares[i] = n\n\t}\n\n\tsum_of_squares := uint64(0)\n\t// transform numbers into their squares of the distance from the average\n\tfor i := 0; i \u003c iterations; i++ {\n\t\tdifference := average - float64(squares[i])\n\t\tsquare := uint64(difference * difference)\n\t\tsum_of_squares += square\n\t}\n\n\tufmt.Println(ufmt.Sprintf(\"Xorshiftr128+ average of %d uint64: %f\\n\", iterations, average))\n\tufmt.Println(ufmt.Sprintf(\"Xorshiftr128+ standard deviation : %f\\n\", math.Sqrt(float64(sum_of_squares)/float64(iterations))))\n\tufmt.Println(ufmt.Sprintf(\"Xorshiftr128+ theoretical perfect deviation: %f\\n\", (float64(target*2)-1)/math.Sqrt(12)))\n}\n"},{"name":"xorshiftr128plus_test.gno","body":"package xorshiftr128plus\n\nimport (\n\t\"math/rand\"\n\t\"testing\"\n)\n\nfunc TestXorshift64StarSeeding(t *testing.T) {\n\txs128p := New()\n\tvalue1 := xs128p.Uint64()\n\n\txs128p = New(987654321)\n\tvalue2 := xs128p.Uint64()\n\n\txs128p = New(987654321, 9876543210)\n\tvalue3 := xs128p.Uint64()\n\n\tif value1 != 13970141264473760763 ||\n\t\tvalue2 != 17031892808144362974 ||\n\t\tvalue3 != 8285073084540510 ||\n\t\tvalue1 == value2 ||\n\t\tvalue2 == value3 ||\n\t\tvalue1 == value3 {\n\t\tt.Errorf(\"Expected three different values: 13970141264473760763, 17031892808144362974, and 8285073084540510\\n got: %d, %d, %d\", value1, value2, value3)\n\t}\n}\n\nfunc TestXorshiftr128PlusRand(t *testing.T) {\n\tsource := New(987654321)\n\trng := rand.New(source)\n\n\t// Expected outputs for the first 5 random floats with the given seed\n\texpected := []float64{\n\t\t0.9199548549485674,\n\t\t0.0027491282372705816,\n\t\t0.31493362274701164,\n\t\t0.3531250819119609,\n\t\t0.09957852858060356,\n\t\t0.731941362705936,\n\t\t0.3476937688876708,\n\t\t0.1444018086140385,\n\t\t0.9106467321832331,\n\t\t0.8024870151488901,\n\t}\n\n\tfor i, exp := range expected {\n\t\tval := rng.Float64()\n\t\tif exp != val {\n\t\t\tt.Errorf(\"Rand.Float64() at iteration %d: got %g, expected %g\", i, val, exp)\n\t\t}\n\t}\n}\n\nfunc TestXorshiftr128PlusUint64(t *testing.T) {\n\txs128p := New(987654321, 9876543210)\n\n\texpected := []uint64{\n\t\t8285073084540510,\n\t\t97010855169053386,\n\t\t11353359435625603792,\n\t\t10289232744262291728,\n\t\t14019961444418950453,\n\t\t15829492476941720545,\n\t\t2764732928842099222,\n\t\t6871047144273883379,\n\t\t16142204260470661970,\n\t\t11803223757041229095,\n\t}\n\n\tfor i, exp := range expected {\n\t\tval := xs128p.Uint64()\n\t\tif exp != val {\n\t\t\tt.Errorf(\"Xorshiftr128Plus.Uint64() at iteration %d: got %d, expected %d\", i, val, exp)\n\t\t}\n\t}\n}\n\nfunc TestXorshiftr128PlusMarshalUnmarshal(t *testing.T) {\n\txs128p := New(987654321, 9876543210)\n\n\texpected1 := []uint64{\n\t\t8285073084540510,\n\t\t97010855169053386,\n\t\t11353359435625603792,\n\t\t10289232744262291728,\n\t\t14019961444418950453,\n\t}\n\n\texpected2 := []uint64{\n\t\t15829492476941720545,\n\t\t2764732928842099222,\n\t\t6871047144273883379,\n\t\t16142204260470661970,\n\t\t11803223757041229095,\n\t}\n\n\tfor i, exp := range expected1 {\n\t\tval := xs128p.Uint64()\n\t\tif exp != val {\n\t\t\tt.Errorf(\"Xorshiftr128Plus.Uint64() at iteration %d: got %d, expected %d\", i, val, exp)\n\t\t}\n\t}\n\n\tmarshalled, err := xs128p.MarshalBinary()\n\n\tt.Logf(\"Original State: [%x]\\n\", xs128p.seed)\n\tt.Logf(\"Marshalled State: [%x] -- %v\\n\", marshalled, err)\n\tstate_before := xs128p.seed\n\n\tif err != nil {\n\t\tt.Errorf(\"Xorshiftr128Plus.MarshalBinary() error: %v\", err)\n\t}\n\n\t// Advance state by one number; then check the next 5. The expectation is that they _will_ fail.\n\txs128p.Uint64()\n\n\tfor i, exp := range expected2 {\n\t\tval := xs128p.Uint64()\n\t\tif exp == val {\n\t\t\tt.Errorf(\" Iteration %d matched %d; which is from iteration %d; something strange is happening.\", (i + 6), val, (i + 5))\n\t\t}\n\t}\n\n\tt.Logf(\"State before unmarshall: [%x]\\n\", xs128p.seed)\n\n\t// Now restore the state of the PRNG\n\terr = xs128p.UnmarshalBinary(marshalled)\n\n\tt.Logf(\"State after unmarshall: [%x]\\n\", xs128p.seed)\n\n\tif state_before != xs128p.seed {\n\t\tt.Errorf(\"States before and after marshal/unmarshal are not equal; go %x and %x\", state_before, xs128p.seed)\n\t}\n\n\t// Now we should be back on track for the last 5 numbers\n\tfor i, exp := range expected2 {\n\t\tval := xs128p.Uint64()\n\t\tif exp != val {\n\t\t\tt.Errorf(\"Xorshiftr128Plus.Uint64() at iteration %d: got %d, expected %d\", (i + 5), val, exp)\n\t\t}\n\t}\n}\n"}]},"deposit":""}],"fee":{"gas_wanted":"50000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":null,"signature":null}],"memo":""}} {"tx":{"msg":[{"@type":"/vm.m_addpkg","creator":"g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5","package":{"name":"btree","path":"gno.land/p/demo/btree","files":[{"name":"btree.gno","body":"//////////\n//\n// Copyright 2014 Google Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n//\n// Copyright 2024 New Tendermint\n//\n// This Gno port of the original Go BTree is substantially rewritten/reimplemented\n// from the original, primarily for clarity of code, clarity of documentation,\n// and for compatibility with Gno.\n//\n// Authors:\n// Original version authors -- https://github.com/google/btree/graphs/contributors\n// Kirk Haines \u003cwyhaines@gmail.com\u003e\n//\n//////////\n\n// Package btree implements in-memory B-Trees of arbitrary degree.\n//\n// It has a flatter structure than an equivalent red-black or other binary tree,\n// which may yield better memory usage and/or performance.\npackage btree\n\nimport \"sort\"\n\n//////////\n//\n// Types\n//\n//////////\n\n// BTreeOption is a function interface for setting options on a btree with `New()`.\ntype BTreeOption func(*BTree)\n\n// BTree is an implementation of a B-Tree.\n//\n// BTree stores Record instances in an ordered structure, allowing easy insertion,\n// removal, and iteration.\ntype BTree struct {\n\tdegree int\n\tlength int\n\troot *node\n\tcowCtx *copyOnWriteContext\n}\n\n//\tAny type that implements this interface can be stored in the BTree. This allows considerable\n//\n// flexiblity in storage within the BTree.\ntype Record interface {\n\t// Less compares self to `than`, returning true if self is less than `than`\n\tLess(than Record) bool\n}\n\n// records is the storage within a node. It is expressed as a slice of Record, where a Record\n// is any struct that implements the Record interface.\ntype records []Record\n\n// node is an internal node in a tree.\n//\n// It must at all times maintain on of the two conditions:\n// - len(children) == 0, len(records) unconstrained\n// - len(children) == len(records) + 1\ntype node struct {\n\trecords records\n\tchildren children\n\tcowCtx *copyOnWriteContext\n}\n\n// children is the list of child nodes below the current node. It is a slice of nodes.\ntype children []*node\n\n// FreeNodeList represents a slice of nodes which are available for reuse. The default\n// behavior of New() is for each BTree instance to have its own FreeNodeList. However,\n// it is possible for multiple instances of BTree to share the same tree. If one uses\n// New(WithFreeNodeList()) to create a tree, one may pass an existing FreeNodeList, allowing\n// multiple trees to use a single list. In an application with multiple trees, it might\n// be more efficient to allocate a single FreeNodeList with a significant initial capacity,\n// and then have all of the trees use that same large FreeNodeList.\ntype FreeNodeList struct {\n\tnodes []*node\n}\n\n// copyOnWriteContext manages node ownership and ensures that cloned trees\n// maintain isolation from each other when a node is changed.\n//\n// Ownership Rules:\n// - Each node is associated with a specific copyOnWriteContext.\n// - A tree can modify a node directly only if the tree's context matches the node's context.\n// - If a tree attempts to modify a node with a different context, it must create a\n// new, writable copy of that node (i.e., perform a clone) before making changes.\n//\n// Write Operation Invariant:\n// - During any write operation, the current node being modified must have the same\n// context as the tree requesting the write.\n// - To maintain this invariant, before descending into a child node, the system checks\n// if the child’s context matches the tree's context.\n// - If the contexts match, the node can be modified in place.\n// - If the contexts do not match, a mutable copy of the child node is created with the\n// correct context before proceeding.\n//\n// Practical Implications:\n// - The node currently being modified inherits the requesting tree's context, allowing\n// in-place modifications.\n// - Child nodes may initially have different contexts. Before any modification, these\n// children are copied to ensure they share the correct context, enabling safe and\n// isolated updates without affecting other trees that might be referencing the original nodes.\n//\n// Example Usage:\n// When a tree performs a write operation (e.g., inserting or deleting a node), it uses\n// its copyOnWriteContext to determine whether it can modify nodes directly or needs to\n// create copies. This mechanism ensures that trees can share nodes efficiently while\n// maintaining data integrity.\ntype copyOnWriteContext struct {\n\tnodes *FreeNodeList\n}\n\n// Record implements an interface with a single function, Less. Any type that implements\n// RecordIterator allows callers of all of the iteration functions for the BTree\n// to evaluate an element of the tree as it is traversed. The function will receive\n// a stored element from the tree. The function must return either a true or a false value.\n// True indicates that iteration should continue, while false indicates that it should halt.\ntype RecordIterator func(i Record) bool\n\n//////////\n//\n// Functions\n//\n//////////\n\n// NewFreeNodeList creates a new free list.\n// size is the maximum size of the returned free list.\nfunc NewFreeNodeList(size int) *FreeNodeList {\n\treturn \u0026FreeNodeList{nodes: make([]*node, 0, size)}\n}\n\nfunc (freeList *FreeNodeList) newNode() (nodeInstance *node) {\n\tindex := len(freeList.nodes) - 1\n\tif index \u003c 0 {\n\t\treturn new(node)\n\t}\n\tnodeInstance = freeList.nodes[index]\n\tfreeList.nodes[index] = nil\n\tfreeList.nodes = freeList.nodes[:index]\n\n\treturn nodeInstance\n}\n\n// freeNode adds the given node to the list, returning true if it was added\n// and false if it was discarded.\n\nfunc (freeList *FreeNodeList) freeNode(nodeInstance *node) (nodeWasAdded bool) {\n\tif len(freeList.nodes) \u003c cap(freeList.nodes) {\n\t\tfreeList.nodes = append(freeList.nodes, nodeInstance)\n\t\tnodeWasAdded = true\n\t}\n\treturn\n}\n\n// A default size for the free node list. We might want to run some benchmarks to see if\n// there are any pros or cons to this size versus other sizes. This seems to be a reasonable\n// compromise to reduce GC pressure by reusing nodes where possible, without stacking up too\n// much baggage in a given tree.\nconst DefaultFreeNodeListSize = 32\n\n// WithDegree sets the degree of the B-Tree.\nfunc WithDegree(degree int) BTreeOption {\n\treturn func(bt *BTree) {\n\t\tif degree \u003c= 1 {\n\t\t\tpanic(\"Degrees less than 1 do not make any sense for a BTree. Please provide a degree of 1 or greater.\")\n\t\t}\n\t\tbt.degree = degree\n\t}\n}\n\n// WithFreeNodeList sets a custom free node list for the B-Tree.\nfunc WithFreeNodeList(freeList *FreeNodeList) BTreeOption {\n\treturn func(bt *BTree) {\n\t\tbt.cowCtx = \u0026copyOnWriteContext{nodes: freeList}\n\t}\n}\n\n// New creates a new B-Tree with optional configurations. If configuration is not provided,\n// it will default to 16 element nodes. Degree may not be less than 1 (which effectively\n// makes the tree into a binary tree).\n//\n// `New(WithDegree(2))`, for example, will create a 2-3-4 tree (each node contains 1-3 records\n// and 2-4 children).\n//\n// `New(WithFreeNodeList(NewFreeNodeList(64)))` will create a tree with a degree of 16, and\n// with a free node list with a size of 64.\nfunc New(options ...BTreeOption) *BTree {\n\tbtree := \u0026BTree{\n\t\tdegree: 16, // default degree\n\t\tcowCtx: \u0026copyOnWriteContext{nodes: NewFreeNodeList(DefaultFreeNodeListSize)},\n\t}\n\tfor _, opt := range options {\n\t\topt(btree)\n\t}\n\treturn btree\n}\n\n// insertAt inserts a value into the given index, pushing all subsequent values\n// forward.\nfunc (recordsSlice *records) insertAt(index int, newRecord Record) {\n\toriginalLength := len(*recordsSlice)\n\n\t// Extend the slice by one element\n\t*recordsSlice = append(*recordsSlice, nil)\n\n\t// Move elements from the end to avoid overwriting during the copy\n\t// TODO: Make this work with slice appends, instead. It should be faster?\n\tif index \u003c originalLength {\n\t\tfor position := originalLength; position \u003e index; position-- {\n\t\t\t(*recordsSlice)[position] = (*recordsSlice)[position-1]\n\t\t}\n\t}\n\n\t// Insert the new record\n\t(*recordsSlice)[index] = newRecord\n}\n\n// removeAt removes a Record from the records slice at the specified index.\n// It shifts subsequent records to fill the gap and returns the removed Record.\nfunc (recordSlicePointer *records) removeAt(index int) Record {\n\trecordSlice := *recordSlicePointer\n\tremovedRecord := recordSlice[index]\n\tcopy(recordSlice[index:], recordSlice[index+1:])\n\trecordSlice[len(recordSlice)-1] = nil\n\t*recordSlicePointer = recordSlice[:len(recordSlice)-1]\n\n\treturn removedRecord\n}\n\n// Pop removes and returns the last Record from the records slice.\n// It also clears the reference to the removed Record to aid garbage collection.\nfunc (r *records) pop() Record {\n\trecordSlice := *r\n\tlastIndex := len(recordSlice) - 1\n\tremovedRecord := recordSlice[lastIndex]\n\trecordSlice[lastIndex] = nil\n\t*r = recordSlice[:lastIndex]\n\treturn removedRecord\n}\n\n// This slice is intended only as a supply of records for the truncate function\n// that follows, and it should not be changed or altered.\nvar emptyRecords = make(records, 32)\n\n// truncate reduces the length of the slice to the specified index,\n// and clears the elements beyond that index to prevent memory leaks.\n// The index must be less than or equal to the current length of the slice.\nfunc (originalSlice *records) truncate(index int) {\n\t// Split the slice into the part to keep and the part to clear.\n\trecordsToKeep := (*originalSlice)[:index]\n\trecordsToClear := (*originalSlice)[index:]\n\n\t// Update the original slice to only contain the records to keep.\n\t*originalSlice = recordsToKeep\n\n\t// Clear the memory of the part that was truncated.\n\tfor len(recordsToClear) \u003e 0 {\n\t\t// Copy empty values from `emptyRecords` to the recordsToClear slice.\n\t\t// This effectively \"clears\" the memory by overwriting elements.\n\t\tnumCleared := copy(recordsToClear, emptyRecords)\n\t\trecordsToClear = recordsToClear[numCleared:]\n\t}\n}\n\n// Find determines the appropriate index at which a given Record should be inserted\n// into the sorted records slice. If the Record already exists in the slice,\n// the method returns its index and sets found to true.\n//\n// Parameters:\n// - record: The Record to search for within the records slice.\n//\n// Returns:\n// - insertIndex: The index at which the Record should be inserted.\n// - found: A boolean indicating whether the Record already exists in the slice.\nfunc (recordsSlice records) find(record Record) (insertIndex int, found bool) {\n\ttotalRecords := len(recordsSlice)\n\n\t// Perform a binary search to find the insertion point for the record\n\tinsertionPoint := sort.Search(totalRecords, func(currentIndex int) bool {\n\t\treturn record.Less(recordsSlice[currentIndex])\n\t})\n\n\tif insertionPoint \u003e 0 {\n\t\tpreviousRecord := recordsSlice[insertionPoint-1]\n\n\t\tif !previousRecord.Less(record) {\n\t\t\treturn insertionPoint - 1, true\n\t\t}\n\t}\n\n\treturn insertionPoint, false\n}\n\n// insertAt inserts a value into the given index, pushing all subsequent values\n// forward.\nfunc (childSlice *children) insertAt(index int, n *node) {\n\toriginalLength := len(*childSlice)\n\n\t// Extend the slice by one element\n\t*childSlice = append(*childSlice, nil)\n\n\t// Move elements from the end to avoid overwriting during the copy\n\tif index \u003c originalLength {\n\t\tfor i := originalLength; i \u003e index; i-- {\n\t\t\t(*childSlice)[i] = (*childSlice)[i-1]\n\t\t}\n\t}\n\n\t// Insert the new record\n\t(*childSlice)[index] = n\n}\n\n// removeAt removes a Record from the records slice at the specified index.\n// It shifts subsequent records to fill the gap and returns the removed Record.\nfunc (childSlicePointer *children) removeAt(index int) *node {\n\tchildSlice := *childSlicePointer\n\tremovedChild := childSlice[index]\n\tcopy(childSlice[index:], childSlice[index+1:])\n\tchildSlice[len(childSlice)-1] = nil\n\t*childSlicePointer = childSlice[:len(childSlice)-1]\n\n\treturn removedChild\n}\n\n// Pop removes and returns the last Record from the records slice.\n// It also clears the reference to the removed Record to aid garbage collection.\nfunc (childSlicePointer *children) pop() *node {\n\tchildSlice := *childSlicePointer\n\tlastIndex := len(childSlice) - 1\n\tremovedChild := childSlice[lastIndex]\n\tchildSlice[lastIndex] = nil\n\t*childSlicePointer = childSlice[:lastIndex]\n\treturn removedChild\n}\n\n// This slice is intended only as a supply of records for the truncate function\n// that follows, and it should not be changed or altered.\nvar emptyChildren = make(children, 32)\n\n// truncate reduces the length of the slice to the specified index,\n// and clears the elements beyond that index to prevent memory leaks.\n// The index must be less than or equal to the current length of the slice.\nfunc (originalSlice *children) truncate(index int) {\n\t// Split the slice into the part to keep and the part to clear.\n\tchildrenToKeep := (*originalSlice)[:index]\n\tchildrenToClear := (*originalSlice)[index:]\n\n\t// Update the original slice to only contain the records to keep.\n\t*originalSlice = childrenToKeep\n\n\t// Clear the memory of the part that was truncated.\n\tfor len(childrenToClear) \u003e 0 {\n\t\t// Copy empty values from `emptyChildren` to the recordsToClear slice.\n\t\t// This effectively \"clears\" the memory by overwriting elements.\n\t\tnumCleared := copy(childrenToClear, emptyChildren)\n\n\t\t// Slice recordsToClear to exclude the elements that were just cleared.\n\t\tchildrenToClear = childrenToClear[numCleared:]\n\t}\n}\n\n// mutableFor creates a mutable copy of the node if the current node does not\n// already belong to the provided copy-on-write context (COW). If the node is\n// already associated with the given COW context, it returns the current node.\n//\n// Parameters:\n// - cowCtx: The copy-on-write context that should own the returned node.\n//\n// Returns:\n// - A pointer to the mutable node associated with the given COW context.\n//\n// If the current node belongs to a different COW context, this function:\n// - Allocates a new node using the provided context.\n// - Copies the node’s records and children slices into the newly allocated node.\n// - Returns the new node which is now owned by the given COW context.\nfunc (n *node) mutableFor(cowCtx *copyOnWriteContext) *node {\n\t// If the current node is already owned by the provided context, return it as-is.\n\tif n.cowCtx == cowCtx {\n\t\treturn n\n\t}\n\n\t// Create a new node in the provided context.\n\tnewNode := cowCtx.newNode()\n\n\t// Copy the records from the current node into the new node.\n\tnewNode.records = append(newNode.records[:0], n.records...)\n\n\t// Copy the children from the current node into the new node.\n\tnewNode.children = append(newNode.children[:0], n.children...)\n\n\treturn newNode\n}\n\n// mutableChild ensures that the child node at the given index is mutable and\n// associated with the same COW context as the parent node. If the child node\n// belongs to a different context, a copy of the child is created and stored in the\n// parent node.\n//\n// Parameters:\n// - i: The index of the child node to be made mutable.\n//\n// Returns:\n// - A pointer to the mutable child node.\nfunc (n *node) mutableChild(i int) *node {\n\t// Ensure that the child at index `i` is mutable and belongs to the same context as the parent.\n\tmutableChildNode := n.children[i].mutableFor(n.cowCtx)\n\t// Update the child node reference in the current node to the mutable version.\n\tn.children[i] = mutableChildNode\n\treturn mutableChildNode\n}\n\n// split splits the given node at the given index. The current node shrinks,\n// and this function returns the record that existed at that index and a new node\n// containing all records/children after it.\nfunc (n *node) split(i int) (Record, *node) {\n\trecord := n.records[i]\n\tnext := n.cowCtx.newNode()\n\tnext.records = append(next.records, n.records[i+1:]...)\n\tn.records.truncate(i)\n\tif len(n.children) \u003e 0 {\n\t\tnext.children = append(next.children, n.children[i+1:]...)\n\t\tn.children.truncate(i + 1)\n\t}\n\treturn record, next\n}\n\n// maybeSplitChild checks if a child should be split, and if so splits it.\n// Returns whether or not a split occurred.\nfunc (n *node) maybeSplitChild(i, maxRecords int) bool {\n\tif len(n.children[i].records) \u003c maxRecords {\n\t\treturn false\n\t}\n\tfirst := n.mutableChild(i)\n\trecord, second := first.split(maxRecords / 2)\n\tn.records.insertAt(i, record)\n\tn.children.insertAt(i+1, second)\n\treturn true\n}\n\n// insert adds a record to the subtree rooted at the current node, ensuring that no node in the subtree\n// exceeds the maximum number of allowed records (`maxRecords`). If an equivalent record is already present,\n// it replaces the existing one and returns it; otherwise, it returns nil.\n//\n// Parameters:\n// - record: The record to be inserted.\n// - maxRecords: The maximum number of records allowed per node.\n//\n// Returns:\n// - The record that was replaced if an equivalent record already existed, otherwise nil.\nfunc (n *node) insert(record Record, maxRecords int) Record {\n\t// Find the position where the new record should be inserted and check if an equivalent record already exists.\n\tinsertionIndex, recordExists := n.records.find(record)\n\n\tif recordExists {\n\t\t// If an equivalent record is found, replace it and return the old record.\n\t\texistingRecord := n.records[insertionIndex]\n\t\tn.records[insertionIndex] = record\n\t\treturn existingRecord\n\t}\n\n\t// If the current node is a leaf (has no children), insert the new record at the calculated index.\n\tif len(n.children) == 0 {\n\t\tn.records.insertAt(insertionIndex, record)\n\t\treturn nil\n\t}\n\n\t// Check if the child node at the insertion index needs to be split due to exceeding maxRecords.\n\tif n.maybeSplitChild(insertionIndex, maxRecords) {\n\t\t// If a split occurred, compare the new record with the record moved up to the current node.\n\t\tsplitRecord := n.records[insertionIndex]\n\t\tswitch {\n\t\tcase record.Less(splitRecord):\n\t\t\t// The new record belongs to the first (left) split node; no change to insertion index.\n\t\tcase splitRecord.Less(record):\n\t\t\t// The new record belongs to the second (right) split node; move the insertion index to the next position.\n\t\t\tinsertionIndex++\n\t\tdefault:\n\t\t\t// If the record is equivalent to the split record, replace it and return the old record.\n\t\t\texistingRecord := n.records[insertionIndex]\n\t\t\tn.records[insertionIndex] = record\n\t\t\treturn existingRecord\n\t\t}\n\t}\n\n\t// Recursively insert the record into the appropriate child node, now guaranteed to have space.\n\treturn n.mutableChild(insertionIndex).insert(record, maxRecords)\n}\n\n// get finds the given key in the subtree and returns it.\nfunc (n *node) get(key Record) Record {\n\ti, found := n.records.find(key)\n\tif found {\n\t\treturn n.records[i]\n\t} else if len(n.children) \u003e 0 {\n\t\treturn n.children[i].get(key)\n\t}\n\treturn nil\n}\n\n// min returns the first record in the subtree.\nfunc min(n *node) Record {\n\tif n == nil {\n\t\treturn nil\n\t}\n\tfor len(n.children) \u003e 0 {\n\t\tn = n.children[0]\n\t}\n\tif len(n.records) == 0 {\n\t\treturn nil\n\t}\n\treturn n.records[0]\n}\n\n// max returns the last record in the subtree.\nfunc max(n *node) Record {\n\tif n == nil {\n\t\treturn nil\n\t}\n\tfor len(n.children) \u003e 0 {\n\t\tn = n.children[len(n.children)-1]\n\t}\n\tif len(n.records) == 0 {\n\t\treturn nil\n\t}\n\treturn n.records[len(n.records)-1]\n}\n\n// toRemove details what record to remove in a node.remove call.\ntype toRemove int\n\nconst (\n\tremoveRecord toRemove = iota // removes the given record\n\tremoveMin // removes smallest record in the subtree\n\tremoveMax // removes largest record in the subtree\n)\n\n// remove removes a record from the subtree rooted at the current node.\n//\n// Parameters:\n// - record: The record to be removed (can be nil when the removal type indicates min or max).\n// - minRecords: The minimum number of records a node should have after removal.\n// - typ: The type of removal operation to perform (removeMin, removeMax, or removeRecord).\n//\n// Returns:\n// - The record that was removed, or nil if no such record was found.\nfunc (n *node) remove(record Record, minRecords int, removalType toRemove) Record {\n\tvar targetIndex int\n\tvar recordFound bool\n\n\t// Determine the index of the record to remove based on the removal type.\n\tswitch removalType {\n\tcase removeMax:\n\t\t// If this node is a leaf, remove and return the last record.\n\t\tif len(n.children) == 0 {\n\t\t\treturn n.records.pop()\n\t\t}\n\t\ttargetIndex = len(n.records) // The last record index for removing max.\n\n\tcase removeMin:\n\t\t// If this node is a leaf, remove and return the first record.\n\t\tif len(n.children) == 0 {\n\t\t\treturn n.records.removeAt(0)\n\t\t}\n\t\ttargetIndex = 0 // The first record index for removing min.\n\n\tcase removeRecord:\n\t\t// Locate the index of the record to be removed.\n\t\ttargetIndex, recordFound = n.records.find(record)\n\t\tif len(n.children) == 0 {\n\t\t\tif recordFound {\n\t\t\t\treturn n.records.removeAt(targetIndex)\n\t\t\t}\n\t\t\treturn nil // The record was not found in the leaf node.\n\t\t}\n\n\tdefault:\n\t\tpanic(\"invalid removal type\")\n\t}\n\n\t// If the current node has children, handle the removal recursively.\n\tif len(n.children[targetIndex].records) \u003c= minRecords {\n\t\t// If the target child node has too few records, grow it before proceeding with removal.\n\t\treturn n.growChildAndRemove(targetIndex, record, minRecords, removalType)\n\t}\n\n\t// Get a mutable reference to the child node at the target index.\n\ttargetChild := n.mutableChild(targetIndex)\n\n\t// If the record to be removed was found in the current node:\n\tif recordFound {\n\t\t// Replace the current record with its predecessor from the child node, and return the removed record.\n\t\treplacedRecord := n.records[targetIndex]\n\t\tn.records[targetIndex] = targetChild.remove(nil, minRecords, removeMax)\n\t\treturn replacedRecord\n\t}\n\n\t// Recursively remove the record from the child node.\n\treturn targetChild.remove(record, minRecords, removalType)\n}\n\n// growChildAndRemove grows child 'i' to make sure it's possible to remove an\n// record from it while keeping it at minRecords, then calls remove to actually\n// remove it.\n//\n// Most documentation says we have to do two sets of special casing:\n// 1. record is in this node\n// 2. record is in child\n//\n// In both cases, we need to handle the two subcases:\n//\n//\tA) node has enough values that it can spare one\n//\tB) node doesn't have enough values\n//\n// For the latter, we have to check:\n//\n//\ta) left sibling has node to spare\n//\tb) right sibling has node to spare\n//\tc) we must merge\n//\n// To simplify our code here, we handle cases #1 and #2 the same:\n// If a node doesn't have enough records, we make sure it does (using a,b,c).\n// We then simply redo our remove call, and the second time (regardless of\n// whether we're in case 1 or 2), we'll have enough records and can guarantee\n// that we hit case A.\nfunc (n *node) growChildAndRemove(i int, record Record, minRecords int, typ toRemove) Record {\n\tif i \u003e 0 \u0026\u0026 len(n.children[i-1].records) \u003e minRecords {\n\t\t// Steal from left child\n\t\tchild := n.mutableChild(i)\n\t\tstealFrom := n.mutableChild(i - 1)\n\t\tstolenRecord := stealFrom.records.pop()\n\t\tchild.records.insertAt(0, n.records[i-1])\n\t\tn.records[i-1] = stolenRecord\n\t\tif len(stealFrom.children) \u003e 0 {\n\t\t\tchild.children.insertAt(0, stealFrom.children.pop())\n\t\t}\n\t} else if i \u003c len(n.records) \u0026\u0026 len(n.children[i+1].records) \u003e minRecords {\n\t\t// steal from right child\n\t\tchild := n.mutableChild(i)\n\t\tstealFrom := n.mutableChild(i + 1)\n\t\tstolenRecord := stealFrom.records.removeAt(0)\n\t\tchild.records = append(child.records, n.records[i])\n\t\tn.records[i] = stolenRecord\n\t\tif len(stealFrom.children) \u003e 0 {\n\t\t\tchild.children = append(child.children, stealFrom.children.removeAt(0))\n\t\t}\n\t} else {\n\t\tif i \u003e= len(n.records) {\n\t\t\ti--\n\t\t}\n\t\tchild := n.mutableChild(i)\n\t\t// merge with right child\n\t\tmergeRecord := n.records.removeAt(i)\n\t\tmergeChild := n.children.removeAt(i + 1).mutableFor(n.cowCtx)\n\t\tchild.records = append(child.records, mergeRecord)\n\t\tchild.records = append(child.records, mergeChild.records...)\n\t\tchild.children = append(child.children, mergeChild.children...)\n\t\tn.cowCtx.freeNode(mergeChild)\n\t}\n\treturn n.remove(record, minRecords, typ)\n}\n\ntype direction int\n\nconst (\n\tdescend = direction(-1)\n\tascend = direction(+1)\n)\n\n// iterate provides a simple method for iterating over elements in the tree.\n//\n// When ascending, the 'start' should be less than 'stop' and when descending,\n// the 'start' should be greater than 'stop'. Setting 'includeStart' to true\n// will force the iterator to include the first record when it equals 'start',\n// thus creating a \"greaterOrEqual\" or \"lessThanEqual\" rather than just a\n// \"greaterThan\" or \"lessThan\" queries.\nfunc (n *node) iterate(dir direction, start, stop Record, includeStart bool, hit bool, iter RecordIterator) (bool, bool) {\n\tvar ok, found bool\n\tvar index int\n\tswitch dir {\n\tcase ascend:\n\t\tif start != nil {\n\t\t\tindex, _ = n.records.find(start)\n\t\t}\n\t\tfor i := index; i \u003c len(n.records); i++ {\n\t\t\tif len(n.children) \u003e 0 {\n\t\t\t\tif hit, ok = n.children[i].iterate(dir, start, stop, includeStart, hit, iter); !ok {\n\t\t\t\t\treturn hit, false\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !includeStart \u0026\u0026 !hit \u0026\u0026 start != nil \u0026\u0026 !start.Less(n.records[i]) {\n\t\t\t\thit = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thit = true\n\t\t\tif stop != nil \u0026\u0026 !n.records[i].Less(stop) {\n\t\t\t\treturn hit, false\n\t\t\t}\n\t\t\tif !iter(n.records[i]) {\n\t\t\t\treturn hit, false\n\t\t\t}\n\t\t}\n\t\tif len(n.children) \u003e 0 {\n\t\t\tif hit, ok = n.children[len(n.children)-1].iterate(dir, start, stop, includeStart, hit, iter); !ok {\n\t\t\t\treturn hit, false\n\t\t\t}\n\t\t}\n\tcase descend:\n\t\tif start != nil {\n\t\t\tindex, found = n.records.find(start)\n\t\t\tif !found {\n\t\t\t\tindex = index - 1\n\t\t\t}\n\t\t} else {\n\t\t\tindex = len(n.records) - 1\n\t\t}\n\t\tfor i := index; i \u003e= 0; i-- {\n\t\t\tif start != nil \u0026\u0026 !n.records[i].Less(start) {\n\t\t\t\tif !includeStart || hit || start.Less(n.records[i]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(n.children) \u003e 0 {\n\t\t\t\tif hit, ok = n.children[i+1].iterate(dir, start, stop, includeStart, hit, iter); !ok {\n\t\t\t\t\treturn hit, false\n\t\t\t\t}\n\t\t\t}\n\t\t\tif stop != nil \u0026\u0026 !stop.Less(n.records[i]) {\n\t\t\t\treturn hit, false //\tcontinue\n\t\t\t}\n\t\t\thit = true\n\t\t\tif !iter(n.records[i]) {\n\t\t\t\treturn hit, false\n\t\t\t}\n\t\t}\n\t\tif len(n.children) \u003e 0 {\n\t\t\tif hit, ok = n.children[0].iterate(dir, start, stop, includeStart, hit, iter); !ok {\n\t\t\t\treturn hit, false\n\t\t\t}\n\t\t}\n\t}\n\treturn hit, true\n}\n\nfunc (tree *BTree) Iterate(dir direction, start, stop Record, includeStart bool, hit bool, iter RecordIterator) (bool, bool) {\n\treturn tree.root.iterate(dir, start, stop, includeStart, hit, iter)\n}\n\n// Clone creates a new BTree instance that shares the current tree's structure using a copy-on-write (COW) approach.\n//\n// How Cloning Works:\n// - The cloned tree (`clonedTree`) shares the current tree’s nodes in a read-only state. This means that no additional memory\n// is allocated for shared nodes, and read operations on the cloned tree are as fast as on the original tree.\n// - When either the original tree (`t`) or the cloned tree (`clonedTree`) needs to perform a write operation (such as an insert, delete, etc.),\n// a new copy of the affected nodes is created on-demand. This ensures that modifications to one tree do not affect the other.\n//\n// Performance Implications:\n// - **Clone Creation:** The creation of a clone is inexpensive since it only involves copying references to the original tree's nodes\n// and creating new copy-on-write contexts.\n// - **Read Operations:** Reading from either the original tree or the cloned tree has no additional performance overhead compared to the original tree.\n// - **Write Operations:** The first write operation on either tree may experience a slight slow-down due to the allocation of new nodes,\n// but subsequent write operations will perform at the same speed as if the tree were not cloned.\n//\n// Returns:\n// - A new BTree instance (`clonedTree`) that shares the original tree's structure.\nfunc (t *BTree) Clone() *BTree {\n\t// Create two independent copy-on-write contexts, one for the original tree (`t`) and one for the cloned tree.\n\toriginalContext := *t.cowCtx\n\tclonedContext := *t.cowCtx\n\n\t// Create a shallow copy of the current tree, which will be the new cloned tree.\n\tclonedTree := *t\n\n\t// Assign the new contexts to their respective trees.\n\tt.cowCtx = \u0026originalContext\n\tclonedTree.cowCtx = \u0026clonedContext\n\n\treturn \u0026clonedTree\n}\n\n// maxRecords returns the max number of records to allow per node.\nfunc (t *BTree) maxRecords() int {\n\treturn t.degree*2 - 1\n}\n\n// minRecords returns the min number of records to allow per node (ignored for the\n// root node).\nfunc (t *BTree) minRecords() int {\n\treturn t.degree - 1\n}\n\nfunc (c *copyOnWriteContext) newNode() (n *node) {\n\tn = c.nodes.newNode()\n\tn.cowCtx = c\n\treturn\n}\n\ntype freeType int\n\nconst (\n\tftFreelistFull freeType = iota // node was freed (available for GC, not stored in nodes)\n\tftStored // node was stored in the nodes for later use\n\tftNotOwned // node was ignored by COW, since it's owned by another one\n)\n\n// freeNode frees a node within a given COW context, if it's owned by that\n// context. It returns what happened to the node (see freeType const\n// documentation).\nfunc (c *copyOnWriteContext) freeNode(n *node) freeType {\n\tif n.cowCtx == c {\n\t\t// clear to allow GC\n\t\tn.records.truncate(0)\n\t\tn.children.truncate(0)\n\t\tn.cowCtx = nil\n\t\tif c.nodes.freeNode(n) {\n\t\t\treturn ftStored\n\t\t} else {\n\t\t\treturn ftFreelistFull\n\t\t}\n\t} else {\n\t\treturn ftNotOwned\n\t}\n}\n\n// Insert adds the given record to the B-tree. If a record already exists in the tree with the same value,\n// it is replaced, and the old record is returned. Otherwise, it returns nil.\n//\n// Notes:\n// - The function panics if a nil record is provided as input.\n// - If the root node is empty, a new root node is created and the record is inserted.\n//\n// Parameters:\n// - record: The record to be inserted into the B-tree.\n//\n// Returns:\n// - The replaced record if an equivalent record already exists, or nil if no replacement occurred.\nfunc (t *BTree) Insert(record Record) Record {\n\tif record == nil {\n\t\tpanic(\"nil record cannot be added to BTree\")\n\t}\n\n\t// If the tree is empty (no root), create a new root node and insert the record.\n\tif t.root == nil {\n\t\tt.root = t.cowCtx.newNode()\n\t\tt.root.records = append(t.root.records, record)\n\t\tt.length++\n\t\treturn nil\n\t}\n\n\t// Ensure that the root node is mutable (associated with the current tree's copy-on-write context).\n\tt.root = t.root.mutableFor(t.cowCtx)\n\n\t// If the root node is full (contains the maximum number of records), split the root.\n\tif len(t.root.records) \u003e= t.maxRecords() {\n\t\t// Split the root node, promoting the middle record and creating a new child node.\n\t\tmiddleRecord, newChildNode := t.root.split(t.maxRecords() / 2)\n\n\t\t// Create a new root node to hold the promoted middle record.\n\t\toldRoot := t.root\n\t\tt.root = t.cowCtx.newNode()\n\t\tt.root.records = append(t.root.records, middleRecord)\n\t\tt.root.children = append(t.root.children, oldRoot, newChildNode)\n\t}\n\n\t// Insert the new record into the subtree rooted at the current root node.\n\treplacedRecord := t.root.insert(record, t.maxRecords())\n\n\t// If no record was replaced, increase the tree's length.\n\tif replacedRecord == nil {\n\t\tt.length++\n\t}\n\n\treturn replacedRecord\n}\n\n// Delete removes an record equal to the passed in record from the tree, returning\n// it. If no such record exists, returns nil.\nfunc (t *BTree) Delete(record Record) Record {\n\treturn t.deleteRecord(record, removeRecord)\n}\n\n// DeleteMin removes the smallest record in the tree and returns it.\n// If no such record exists, returns nil.\nfunc (t *BTree) DeleteMin() Record {\n\treturn t.deleteRecord(nil, removeMin)\n}\n\n// Shift is identical to DeleteMin. If the tree is thought of as an ordered list, then Shift()\n// removes the element at the start of the list, the smallest element, and returns it.\nfunc (t *BTree) Shift() Record {\n\treturn t.deleteRecord(nil, removeMin)\n}\n\n// DeleteMax removes the largest record in the tree and returns it.\n// If no such record exists, returns nil.\nfunc (t *BTree) DeleteMax() Record {\n\treturn t.deleteRecord(nil, removeMax)\n}\n\n// Pop is identical to DeleteMax. If the tree is thought of as an ordered list, then Shift()\n// removes the element at the end of the list, the largest element, and returns it.\nfunc (t *BTree) Pop() Record {\n\treturn t.deleteRecord(nil, removeMax)\n}\n\n// deleteRecord removes a record from the B-tree based on the specified removal type (removeMin, removeMax, or removeRecord).\n// It returns the removed record if it was found, or nil if no matching record was found.\n//\n// Parameters:\n// - record: The record to be removed (can be nil if the removal type indicates min or max).\n// - removalType: The type of removal operation to perform (removeMin, removeMax, or removeRecord).\n//\n// Returns:\n// - The removed record if it existed in the tree, or nil if it was not found.\nfunc (t *BTree) deleteRecord(record Record, removalType toRemove) Record {\n\t// If the tree is empty or the root has no records, return nil.\n\tif t.root == nil || len(t.root.records) == 0 {\n\t\treturn nil\n\t}\n\n\t// Ensure the root node is mutable (associated with the tree's copy-on-write context).\n\tt.root = t.root.mutableFor(t.cowCtx)\n\n\t// Attempt to remove the specified record from the root node.\n\tremovedRecord := t.root.remove(record, t.minRecords(), removalType)\n\n\t// Check if the root node has become empty but still has children.\n\t// In this case, the tree height should be reduced, making the first child the new root.\n\tif len(t.root.records) == 0 \u0026\u0026 len(t.root.children) \u003e 0 {\n\t\toldRoot := t.root\n\t\tt.root = t.root.children[0]\n\t\t// Free the old root node, as it is no longer needed.\n\t\tt.cowCtx.freeNode(oldRoot)\n\t}\n\n\t// If a record was successfully removed, decrease the tree's length.\n\tif removedRecord != nil {\n\t\tt.length--\n\t}\n\n\treturn removedRecord\n}\n\n// AscendRange calls the iterator for every value in the tree within the range\n// [greaterOrEqual, lessThan), until iterator returns false.\nfunc (t *BTree) AscendRange(greaterOrEqual, lessThan Record, iterator RecordIterator) {\n\tif t.root == nil {\n\t\treturn\n\t}\n\tt.root.iterate(ascend, greaterOrEqual, lessThan, true, false, iterator)\n}\n\n// AscendLessThan calls the iterator for every value in the tree within the range\n// [first, pivot), until iterator returns false.\nfunc (t *BTree) AscendLessThan(pivot Record, iterator RecordIterator) {\n\tif t.root == nil {\n\t\treturn\n\t}\n\tt.root.iterate(ascend, nil, pivot, false, false, iterator)\n}\n\n// AscendGreaterOrEqual calls the iterator for every value in the tree within\n// the range [pivot, last], until iterator returns false.\nfunc (t *BTree) AscendGreaterOrEqual(pivot Record, iterator RecordIterator) {\n\tif t.root == nil {\n\t\treturn\n\t}\n\tt.root.iterate(ascend, pivot, nil, true, false, iterator)\n}\n\n// Ascend calls the iterator for every value in the tree within the range\n// [first, last], until iterator returns false.\nfunc (t *BTree) Ascend(iterator RecordIterator) {\n\tif t.root == nil {\n\t\treturn\n\t}\n\tt.root.iterate(ascend, nil, nil, false, false, iterator)\n}\n\n// DescendRange calls the iterator for every value in the tree within the range\n// [lessOrEqual, greaterThan), until iterator returns false.\nfunc (t *BTree) DescendRange(lessOrEqual, greaterThan Record, iterator RecordIterator) {\n\tif t.root == nil {\n\t\treturn\n\t}\n\tt.root.iterate(descend, lessOrEqual, greaterThan, true, false, iterator)\n}\n\n// DescendLessOrEqual calls the iterator for every value in the tree within the range\n// [pivot, first], until iterator returns false.\nfunc (t *BTree) DescendLessOrEqual(pivot Record, iterator RecordIterator) {\n\tif t.root == nil {\n\t\treturn\n\t}\n\tt.root.iterate(descend, pivot, nil, true, false, iterator)\n}\n\n// DescendGreaterThan calls the iterator for every value in the tree within\n// the range [last, pivot), until iterator returns false.\nfunc (t *BTree) DescendGreaterThan(pivot Record, iterator RecordIterator) {\n\tif t.root == nil {\n\t\treturn\n\t}\n\tt.root.iterate(descend, nil, pivot, false, false, iterator)\n}\n\n// Descend calls the iterator for every value in the tree within the range\n// [last, first], until iterator returns false.\nfunc (t *BTree) Descend(iterator RecordIterator) {\n\tif t.root == nil {\n\t\treturn\n\t}\n\tt.root.iterate(descend, nil, nil, false, false, iterator)\n}\n\n// Get looks for the key record in the tree, returning it. It returns nil if\n// unable to find that record.\nfunc (t *BTree) Get(key Record) Record {\n\tif t.root == nil {\n\t\treturn nil\n\t}\n\treturn t.root.get(key)\n}\n\n// Min returns the smallest record in the tree, or nil if the tree is empty.\nfunc (t *BTree) Min() Record {\n\treturn min(t.root)\n}\n\n// Max returns the largest record in the tree, or nil if the tree is empty.\nfunc (t *BTree) Max() Record {\n\treturn max(t.root)\n}\n\n// Has returns true if the given key is in the tree.\nfunc (t *BTree) Has(key Record) bool {\n\treturn t.Get(key) != nil\n}\n\n// Len returns the number of records currently in the tree.\nfunc (t *BTree) Len() int {\n\treturn t.length\n}\n\n// Clear removes all elements from the B-tree.\n//\n// Parameters:\n// - addNodesToFreelist:\n// - If true, the tree's nodes are added to the freelist during the clearing process,\n// up to the freelist's capacity.\n// - If false, the root node is simply dereferenced, allowing Go's garbage collector\n// to reclaim the memory.\n//\n// Benefits:\n// - **Performance:**\n// - Significantly faster than deleting each element individually, as it avoids the overhead\n// of searching and updating the tree structure for each deletion.\n// - More efficient than creating a new tree, since it reuses existing nodes by adding them\n// to the freelist instead of discarding them to the garbage collector.\n//\n// Time Complexity:\n// - **O(1):**\n// - When `addNodesToFreelist` is false.\n// - When `addNodesToFreelist` is true but the freelist is already full.\n// - **O(freelist size):**\n// - When adding nodes to the freelist up to its capacity.\n// - **O(tree size):**\n// - When iterating through all nodes to add to the freelist, but none can be added due to\n// ownership by another tree.\n\nfunc (tree *BTree) Clear(addNodesToFreelist bool) {\n\tif tree.root != nil \u0026\u0026 addNodesToFreelist {\n\t\ttree.root.reset(tree.cowCtx)\n\t}\n\ttree.root = nil\n\ttree.length = 0\n}\n\n// reset adds all nodes in the current subtree to the freelist.\n//\n// The function operates recursively:\n// - It first attempts to reset all child nodes.\n// - If the freelist becomes full at any point, the process stops immediately.\n//\n// Parameters:\n// - copyOnWriteCtx: The copy-on-write context managing the freelist.\n//\n// Returns:\n// - true: Indicates that the parent node should continue attempting to reset its nodes.\n// - false: Indicates that the freelist is full and no further nodes should be added.\n//\n// Usage:\n// This method is called during the `Clear` operation of the B-tree to efficiently reuse\n// nodes by adding them to the freelist, thereby avoiding unnecessary allocations and reducing\n// garbage collection overhead.\nfunc (currentNode *node) reset(copyOnWriteCtx *copyOnWriteContext) bool {\n\t// Iterate through each child node and attempt to reset it.\n\tfor _, childNode := range currentNode.children {\n\t\t// If any child reset operation signals that the freelist is full, stop the process.\n\t\tif !childNode.reset(copyOnWriteCtx) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t// Attempt to add the current node to the freelist.\n\t// If the freelist is full after this operation, indicate to the parent to stop.\n\tfreelistStatus := copyOnWriteCtx.freeNode(currentNode)\n\treturn freelistStatus != ftFreelistFull\n}\n"},{"name":"btree_test.gno","body":"package btree\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"gno.land/p/demo/btree\"\n)\n\n// Content represents a key-value pair where the Key can be either an int or string\n// and the Value can be any type.\ntype Content struct {\n\tKey interface{}\n\tValue interface{}\n}\n\n// Less compares two Content records by their Keys.\n// The Key must be either an int or a string.\nfunc (c Content) Less(than Record) bool {\n\tother, ok := than.(Content)\n\tif !ok {\n\t\tpanic(\"cannot compare: incompatible types\")\n\t}\n\n\tswitch key := c.Key.(type) {\n\tcase int:\n\t\tswitch otherKey := other.Key.(type) {\n\t\tcase int:\n\t\t\treturn key \u003c otherKey\n\t\tcase string:\n\t\t\treturn true // ints are always less than strings\n\t\tdefault:\n\t\t\tpanic(\"unsupported key type: must be int or string\")\n\t\t}\n\tcase string:\n\t\tswitch otherKey := other.Key.(type) {\n\t\tcase int:\n\t\t\treturn false // strings are always greater than ints\n\t\tcase string:\n\t\t\treturn key \u003c otherKey\n\t\tdefault:\n\t\t\tpanic(\"unsupported key type: must be int or string\")\n\t\t}\n\tdefault:\n\t\tpanic(\"unsupported key type: must be int or string\")\n\t}\n}\n\ntype ContentSlice []Content\n\nfunc (s ContentSlice) Len() int {\n\treturn len(s)\n}\n\nfunc (s ContentSlice) Less(i, j int) bool {\n\treturn s[i].Less(s[j])\n}\n\nfunc (s ContentSlice) Swap(i, j int) {\n\ts[i], s[j] = s[j], s[i]\n}\n\nfunc (s ContentSlice) Copy() ContentSlice {\n\tnewSlice := make(ContentSlice, len(s))\n\tcopy(newSlice, s)\n\treturn newSlice\n}\n\n// Ensure Content implements the Record interface.\nvar _ Record = Content{}\n\n// ****************************************************************************\n// Test helpers\n// ****************************************************************************\n\nfunc genericSeeding(tree *btree.BTree, size int) *btree.BTree {\n\tfor i := 0; i \u003c size; i++ {\n\t\ttree.Insert(Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)})\n\t}\n\treturn tree\n}\n\nfunc intSlicesCompare(left, right []int) int {\n\tif len(left) != len(right) {\n\t\tif len(left) \u003e len(right) {\n\t\t\treturn 1\n\t\t} else {\n\t\t\treturn -1\n\t\t}\n\t}\n\n\tfor position, leftInt := range left {\n\t\tif leftInt != right[position] {\n\t\t\tif leftInt \u003e right[position] {\n\t\t\t\treturn 1\n\t\t\t} else {\n\t\t\t\treturn -1\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 0\n}\n\n// ****************************************************************************\n// Tests\n// ****************************************************************************\n\nfunc TestLen(t *testing.T) {\n\tlength := genericSeeding(btree.New(WithDegree(10)), 7).Len()\n\tif length != 7 {\n\t\tt.Errorf(\"Length is incorrect. Expected 7, but got %d.\", length)\n\t}\n\n\tlength = genericSeeding(btree.New(WithDegree(5)), 111).Len()\n\tif length != 111 {\n\t\tt.Errorf(\"Length is incorrect. Expected 111, but got %d.\", length)\n\t}\n\n\tlength = genericSeeding(btree.New(WithDegree(30)), 123).Len()\n\tif length != 123 {\n\t\tt.Errorf(\"Length is incorrect. Expected 123, but got %d.\", length)\n\t}\n\n}\n\nfunc TestHas(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 40)\n\n\tif tree.Has(Content{Key: 7}) != true {\n\t\tt.Errorf(\"Has(7) reported false, but it should be true.\")\n\t}\n\tif tree.Has(Content{Key: 39}) != true {\n\t\tt.Errorf(\"Has(40) reported false, but it should be true.\")\n\t}\n\tif tree.Has(Content{Key: 1111}) == true {\n\t\tt.Errorf(\"Has(1111) reported true, but it should be false.\")\n\t}\n}\n\nfunc TestMin(t *testing.T) {\n\tmin := Content(genericSeeding(btree.New(WithDegree(10)), 53).Min())\n\n\tif min.Key != 0 {\n\t\tt.Errorf(\"Minimum should have been 0, but it was reported as %d.\", min)\n\t}\n}\n\nfunc TestMax(t *testing.T) {\n\tmax := Content(genericSeeding(btree.New(WithDegree(10)), 53).Min())\n\n\tif max.Key != 0 {\n\t\tt.Errorf(\"Minimum should have been 0, but it was reported as %d.\", max)\n\t}\n}\n\nfunc TestGet(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 40)\n\n\tif Content(tree.Get(Content{Key: 7})).Value != \"Value_7\" {\n\t\tt.Errorf(\"Get(7) should have returned 'Value_7', but it returned %v.\", tree.Get(Content{Key: 7}))\n\t}\n\tif Content(tree.Get(Content{Key: 39})).Value != \"Value_39\" {\n\t\tt.Errorf(\"Get(40) should have returnd 'Value_39', but it returned %v.\", tree.Get(Content{Key: 39}))\n\t}\n\tif tree.Get(Content{Key: 1111}) != nil {\n\t\tt.Errorf(\"Get(1111) returned %v, but it should be nil.\", Content(tree.Get(Content{Key: 1111})))\n\t}\n}\n\nfunc TestDescend(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 5)\n\n\texpected := []int{4, 3, 2, 1, 0}\n\tfound := []int{}\n\n\ttree.Descend(func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tfound = append(found, int(record.Key))\n\t\treturn true\n\t})\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"Descend returned the wrong sequence. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestDescendGreaterThan(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 10)\n\n\texpected := []int{9, 8, 7, 6, 5}\n\tfound := []int{}\n\n\ttree.DescendGreaterThan(Content{Key: 4}, func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tfound = append(found, int(record.Key))\n\t\treturn true\n\t})\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"DescendGreaterThan returned the wrong sequence. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestDescendLessOrEqual(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 10)\n\n\texpected := []int{4, 3, 2, 1, 0}\n\tfound := []int{}\n\n\ttree.DescendLessOrEqual(Content{Key: 4}, func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tfound = append(found, int(record.Key))\n\t\treturn true\n\t})\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"DescendLessOrEqual returned the wrong sequence. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestDescendRange(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 10)\n\n\texpected := []int{6, 5, 4, 3, 2}\n\tfound := []int{}\n\n\ttree.DescendRange(Content{Key: 6}, Content{Key: 1}, func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tfound = append(found, int(record.Key))\n\t\treturn true\n\t})\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"DescendRange returned the wrong sequence. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestAscend(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 5)\n\n\texpected := []int{0, 1, 2, 3, 4}\n\tfound := []int{}\n\n\ttree.Ascend(func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tfound = append(found, int(record.Key))\n\t\treturn true\n\t})\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"Ascend returned the wrong sequence. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestAscendGreaterOrEqual(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 10)\n\n\texpected := []int{5, 6, 7, 8, 9}\n\tfound := []int{}\n\n\ttree.AscendGreaterOrEqual(Content{Key: 5}, func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tfound = append(found, int(record.Key))\n\t\treturn true\n\t})\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"AscendGreaterOrEqual returned the wrong sequence. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestAscendLessThan(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 10)\n\n\texpected := []int{0, 1, 2, 3, 4}\n\tfound := []int{}\n\n\ttree.AscendLessThan(Content{Key: 5}, func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tfound = append(found, int(record.Key))\n\t\treturn true\n\t})\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"DescendLessOrEqual returned the wrong sequence. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestAscendRange(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(10)), 10)\n\n\texpected := []int{2, 3, 4, 5, 6}\n\tfound := []int{}\n\n\ttree.AscendRange(Content{Key: 2}, Content{Key: 7}, func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tfound = append(found, int(record.Key))\n\t\treturn true\n\t})\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"DescendRange returned the wrong sequence. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestDeleteMin(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(3)), 100)\n\n\texpected := []int{0, 1, 2, 3, 4}\n\tfound := []int{}\n\n\tfound = append(found, int(Content(tree.DeleteMin()).Key))\n\tfound = append(found, int(Content(tree.DeleteMin()).Key))\n\tfound = append(found, int(Content(tree.DeleteMin()).Key))\n\tfound = append(found, int(Content(tree.DeleteMin()).Key))\n\tfound = append(found, int(Content(tree.DeleteMin()).Key))\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"5 rounds of DeleteMin returned the wrong elements. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestShift(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(3)), 100)\n\n\texpected := []int{0, 1, 2, 3, 4}\n\tfound := []int{}\n\n\tfound = append(found, int(Content(tree.Shift()).Key))\n\tfound = append(found, int(Content(tree.Shift()).Key))\n\tfound = append(found, int(Content(tree.Shift()).Key))\n\tfound = append(found, int(Content(tree.Shift()).Key))\n\tfound = append(found, int(Content(tree.Shift()).Key))\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"5 rounds of Shift returned the wrong elements. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestDeleteMax(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(3)), 100)\n\n\texpected := []int{99, 98, 97, 96, 95}\n\tfound := []int{}\n\n\tfound = append(found, int(Content(tree.DeleteMax()).Key))\n\tfound = append(found, int(Content(tree.DeleteMax()).Key))\n\tfound = append(found, int(Content(tree.DeleteMax()).Key))\n\tfound = append(found, int(Content(tree.DeleteMax()).Key))\n\tfound = append(found, int(Content(tree.DeleteMax()).Key))\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"5 rounds of DeleteMin returned the wrong elements. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestPop(t *testing.T) {\n\ttree := genericSeeding(btree.New(WithDegree(3)), 100)\n\n\texpected := []int{99, 98, 97, 96, 95}\n\tfound := []int{}\n\n\tfound = append(found, int(Content(tree.Pop()).Key))\n\tfound = append(found, int(Content(tree.Pop()).Key))\n\tfound = append(found, int(Content(tree.Pop()).Key))\n\tfound = append(found, int(Content(tree.Pop()).Key))\n\tfound = append(found, int(Content(tree.Pop()).Key))\n\n\tif intSlicesCompare(expected, found) != 0 {\n\t\tt.Errorf(\"5 rounds of DeleteMin returned the wrong elements. Expected %v, but got %v.\", expected, found)\n\t}\n}\n\nfunc TestInsertGet(t *testing.T) {\n\ttree := btree.New(WithDegree(4))\n\n\texpected := []Content{}\n\n\tfor count := 0; count \u003c 20; count++ {\n\t\tvalue := fmt.Sprintf(\"Value_%d\", count)\n\t\ttree.Insert(Content{Key: count, Value: value})\n\t\texpected = append(expected, Content{Key: count, Value: value})\n\t}\n\n\tfor count := 0; count \u003c 20; count++ {\n\t\tif tree.Get(Content{Key: count}) != expected[count] {\n\t\t\tt.Errorf(\"Insert/Get doesn't appear to be working. Expected to retrieve %v with key %d, but got %v.\", expected[count], count, tree.Get(Content{Key: count}))\n\t\t}\n\t}\n}\n\nfunc TestClone(t *testing.T) {\n}\n\n// ***** The following tests are functional or stress testing type tests.\n\nfunc TestBTree(t *testing.T) {\n\t// Create a B-Tree of degree 3\n\ttree := btree.New(WithDegree(3))\n\n\t//insertData := []Content{}\n\tvar insertData ContentSlice\n\n\t// Insert integer keys\n\tintKeys := []int{10, 20, 5, 6, 12, 30, 7, 17}\n\tfor _, key := range intKeys {\n\t\tcontent := Content{Key: key, Value: fmt.Sprintf(\"Value_%d\", key)}\n\t\tinsertData = append(insertData, content)\n\t\tresult := tree.Insert(content)\n\t\tif result != nil {\n\t\t\tt.Errorf(\"**** Already in the tree? %v\", result)\n\t\t}\n\t}\n\n\t// Insert string keys\n\tstringKeys := []string{\"apple\", \"banana\", \"cherry\", \"date\", \"fig\", \"grape\"}\n\tfor _, key := range stringKeys {\n\t\tcontent := Content{Key: key, Value: fmt.Sprintf(\"Fruit_%s\", key)}\n\t\tinsertData = append(insertData, content)\n\t\ttree.Insert(content)\n\t}\n\n\tif tree.Len() != 14 {\n\t\tt.Errorf(\"Tree length wrong. Expected 14 but got %d\", tree.Len())\n\t}\n\n\t// Search for existing and non-existing keys\n\tsearchTests := []struct {\n\t\ttest Content\n\t\texpected bool\n\t}{\n\t\t{Content{Key: 10, Value: \"Value_10\"}, true},\n\t\t{Content{Key: 15, Value: \"\"}, false},\n\t\t{Content{Key: \"banana\", Value: \"Fruit_banana\"}, true},\n\t\t{Content{Key: \"kiwi\", Value: \"\"}, false},\n\t}\n\n\tt.Logf(\"Search Tests:\\n\")\n\tfor _, test := range searchTests {\n\t\tval := tree.Get(test.test)\n\n\t\tif test.expected {\n\t\t\tif val != nil \u0026\u0026 Content(val).Value == test.test.Value {\n\t\t\t\tt.Logf(\"Found expected key:value %v:%v\", test.test.Key, test.test.Value)\n\t\t\t} else {\n\t\t\t\tif val == nil {\n\t\t\t\t\tt.Logf(\"Didn't find %v, but expected\", test.test.Key)\n\t\t\t\t} else {\n\t\t\t\t\tt.Errorf(\"Expected key %v:%v, but found %v:%v.\", test.test.Key, test.test.Value, Content(val).Key, Content(val).Value)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif val != nil {\n\t\t\t\tt.Errorf(\"Did not expect key %v, but found key:value %v:%v\", test.test.Key, Content(val).Key, Content(val).Value)\n\t\t\t} else {\n\t\t\t\tt.Logf(\"Didn't find %v, but wasn't expected\", test.test.Key)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Iterate in order\n\tt.Logf(\"\\nIn-order Iteration:\\n\")\n\tpos := 0\n\n\tif tree.Len() != 14 {\n\t\tt.Errorf(\"Tree length wrong. Expected 14 but got %d\", tree.Len())\n\t}\n\n\tsortedInsertData := insertData.Copy()\n\tsort.Sort(sortedInsertData)\n\n\tt.Logf(\"Insert Data Length: %d\", len(insertData))\n\tt.Logf(\"Sorted Data Length: %d\", len(sortedInsertData))\n\tt.Logf(\"Tree Length: %d\", tree.Len())\n\n\ttree.Ascend(func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tt.Logf(\"Key:Value == %v:%v\", record.Key, record.Value)\n\t\tif record.Key != sortedInsertData[pos].Key {\n\t\t\tt.Errorf(\"Out of order! Expected %v, but got %v\", sortedInsertData[pos].Key, record.Key)\n\t\t}\n\t\tpos++\n\t\treturn true\n\t})\n\t// // Reverse Iterate\n\tt.Logf(\"\\nReverse-order Iteration:\\n\")\n\tpos = len(sortedInsertData) - 1\n\n\ttree.Descend(func(_record btree.Record) bool {\n\t\trecord := Content(_record)\n\t\tt.Logf(\"Key:Value == %v:%v\", record.Key, record.Value)\n\t\tif record.Key != sortedInsertData[pos].Key {\n\t\t\tt.Errorf(\"Out of order! Expected %v, but got %v\", sortedInsertData[pos].Key, record.Key)\n\t\t}\n\t\tpos--\n\t\treturn true\n\t})\n\n\tdeleteTests := []Content{\n\t\tContent{Key: 10, Value: \"Value_10\"},\n\t\tContent{Key: 15, Value: \"\"},\n\t\tContent{Key: \"banana\", Value: \"Fruit_banana\"},\n\t\tContent{Key: \"kiwi\", Value: \"\"},\n\t}\n\tfor _, test := range deleteTests {\n\t\tfmt.Printf(\"\\nDeleting %+v\\n\", test)\n\t\ttree.Delete(test)\n\t}\n\n\tif tree.Len() != 12 {\n\t\tt.Errorf(\"Tree length wrong. Expected 12 but got %d\", tree.Len())\n\t}\n\n\tfor _, test := range deleteTests {\n\t\tval := tree.Get(test)\n\t\tif val != nil {\n\t\t\tt.Errorf(\"Did not expect key %v, but found key:value %v:%v\", test.Key, Content(val).Key, Content(val).Value)\n\t\t} else {\n\t\t\tt.Logf(\"Didn't find %v, but wasn't expected\", test.Key)\n\t\t}\n\t}\n}\n\nfunc TestStress(t *testing.T) {\n\t// Loop through creating B-Trees with a range of degrees from 3 to 12, stepping by 3.\n\t// Insert 1000 records into each tree, then search for each record.\n\t// Delete half of the records, skipping every other one, then search for each record.\n\n\tfor degree := 3; degree \u003c= 12; degree += 3 {\n\t\tt.Logf(\"Testing B-Tree of degree %d\\n\", degree)\n\t\ttree := btree.New(WithDegree(degree))\n\n\t\t// Insert 1000 records\n\t\tt.Logf(\"Inserting 1000 records\\n\")\n\t\tfor i := 0; i \u003c 1000; i++ {\n\t\t\tcontent := Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\t\ttree.Insert(content)\n\t\t}\n\n\t\t// Search for all records\n\t\tfor i := 0; i \u003c 1000; i++ {\n\t\t\tcontent := Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\t\tval := tree.Get(content)\n\t\t\tif val == nil {\n\t\t\t\tt.Errorf(\"Expected key %v, but didn't find it\", content.Key)\n\t\t\t}\n\t\t}\n\n\t\t// Delete half of the records\n\t\tfor i := 0; i \u003c 1000; i += 2 {\n\t\t\tcontent := Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\t\ttree.Delete(content)\n\t\t}\n\n\t\t// Search for all records\n\t\tfor i := 0; i \u003c 1000; i++ {\n\t\t\tcontent := Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\t\tval := tree.Get(content)\n\t\t\tif i%2 == 0 {\n\t\t\t\tif val != nil {\n\t\t\t\t\tt.Errorf(\"Didn't expect key %v, but found key:value %v:%v\", content.Key, Content(val).Key, Content(val).Value)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif val == nil {\n\t\t\t\t\tt.Errorf(\"Expected key %v, but didn't find it\", content.Key)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Now create a very large tree, with 100000 records\n\t// Then delete roughly one third of them, using a very basic random number generation scheme\n\t// (implement it right here) to determine which records to delete.\n\t// Print a few lines using Logf to let the user know what's happening.\n\n\tt.Logf(\"Testing B-Tree of degree 10 with 100000 records\\n\")\n\ttree := btree.New(WithDegree(10))\n\n\t// Insert 100000 records\n\tt.Logf(\"Inserting 100000 records\\n\")\n\tfor i := 0; i \u003c 100000; i++ {\n\t\tcontent := Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\ttree.Insert(content)\n\t}\n\n\t// Implement a very basic random number generator\n\tseed := 0\n\trandom := func() int {\n\t\tseed = (seed*1103515245 + 12345) \u0026 0x7fffffff\n\t\treturn seed\n\t}\n\n\t// Delete one third of the records\n\tt.Logf(\"Deleting one third of the records\\n\")\n\tfor i := 0; i \u003c 35000; i++ {\n\t\tcontent := Content{Key: random() % 100000, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\ttree.Delete(content)\n\t}\n}\n\n// Write a test that populates a large B-Tree with 10000 records.\n// It should then `Clone` the tree, make some changes to both the original and the clone,\n// And then clone the clone, and make some changes to all three trees, and then check that the changes are isolated\n// to the tree they were made in.\n\nfunc TestBTreeCloneIsolation(t *testing.T) {\n\tt.Logf(\"Creating B-Tree of degree 10 with 10000 records\\n\")\n\ttree := genericSeeding(btree.New(WithDegree(10)), 10000)\n\n\t// Clone the tree\n\tt.Logf(\"Cloning the tree\\n\")\n\tclone := tree.Clone()\n\n\t// Make some changes to the original and the clone\n\tt.Logf(\"Making changes to the original and the clone\\n\")\n\tfor i := 0; i \u003c 10000; i += 2 {\n\t\tcontent := Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\ttree.Delete(content)\n\t\tcontent = Content{Key: i + 1, Value: fmt.Sprintf(\"Value_%d\", i+1)}\n\t\tclone.Delete(content)\n\t}\n\n\t// Clone the clone\n\tt.Logf(\"Cloning the clone\\n\")\n\tclone2 := clone.Clone()\n\n\t// Make some changes to all three trees\n\tt.Logf(\"Making changes to all three trees\\n\")\n\tfor i := 0; i \u003c 10000; i += 3 {\n\t\tcontent := Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\ttree.Delete(content)\n\t\tcontent = Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i+1)}\n\t\tclone.Delete(content)\n\t\tcontent = Content{Key: i + 2, Value: fmt.Sprintf(\"Value_%d\", i+2)}\n\t\tclone2.Delete(content)\n\t}\n\n\t// Check that the changes are isolated to the tree they were made in\n\tt.Logf(\"Checking that the changes are isolated to the tree they were made in\\n\")\n\tfor i := 0; i \u003c 10000; i++ {\n\t\tcontent := Content{Key: i, Value: fmt.Sprintf(\"Value_%d\", i)}\n\t\tval := tree.Get(content)\n\n\t\tif i%3 == 0 || i%2 == 0 {\n\t\t\tif val != nil {\n\t\t\t\tt.Errorf(\"Didn't expect key %v, but found key:value %v:%v\", content.Key, Content(val).Key, Content(val).Value)\n\t\t\t}\n\t\t} else {\n\t\t\tif val == nil {\n\t\t\t\tt.Errorf(\"Expected key %v, but didn't find it\", content.Key)\n\t\t\t}\n\t\t}\n\n\t\tval = clone.Get(content)\n\t\tif i%2 != 0 || i%3 == 0 {\n\t\t\tif val != nil {\n\t\t\t\tt.Errorf(\"Didn't expect key %v, but found key:value %v:%v\", content.Key, Content(val).Key, Content(val).Value)\n\t\t\t}\n\t\t} else {\n\t\t\tif val == nil {\n\t\t\t\tt.Errorf(\"Expected key %v, but didn't find it\", content.Key)\n\t\t\t}\n\t\t}\n\n\t\tval = clone2.Get(content)\n\t\tif i%2 != 0 || (i-2)%3 == 0 {\n\t\t\tif val != nil {\n\t\t\t\tt.Errorf(\"Didn't expect key %v, but found key:value %v:%v\", content.Key, Content(val).Key, Content(val).Value)\n\t\t\t}\n\t\t} else {\n\t\t\tif val == nil {\n\t\t\t\tt.Errorf(\"Expected key %v, but didn't find it\", content.Key)\n\t\t\t}\n\t\t}\n\t}\n}\n"}]},"deposit":""}],"fee":{"gas_wanted":"50000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":null,"signature":null}],"memo":""}} +{"tx":{"msg":[{"@type":"/bank.MsgSend","from_address":"g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5","to_address":"g14kdg0zn97xms3eza5xh6lcycmwm4zjksg4peum","amount":"5000000ugnot"}],"fee":{"gas_wanted":"100000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AwEKz9MRdU4MCAzB3el9G4wLJJQeWiRYtRquzEgm19Z2"},"signature":"5p/v1ZZyBAhjCkdKcPNBkOjlOi4cFRDlITrSPt3yo2EdKCiZp4NHPSg25tGsLQJGzw111ttUPY1d/TTeA/+glw=="}],"memo":""},"metadata":{"timestamp":"1733430096693"}} +{"tx":{"msg":[{"@type":"/bank.MsgSend","from_address":"g1qhuef2450xh7g7na8s865nreu2xw8j84kgkvt5","to_address":"g150gdvn780ws7a3s9qpdvv7h4d9hq6vh672ely0","amount":"10000000ugnot"}],"fee":{"gas_wanted":"100000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"AwEKz9MRdU4MCAzB3el9G4wLJJQeWiRYtRquzEgm19Z2"},"signature":"vmYm1GCzpzr9OAEDaqrc7eAIuaXb1ApqSzbCxRmv5NMfJksdWmsAbE0IOSFhCfSNuaRflWN/f9rQs+EibJ95ag=="}],"memo":""},"metadata":{"timestamp":"1733396486352"}} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g125em6arxsnj49vx35f0n0z34putv5ty3376fg5","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModAddPost","args":["audit-proposal-request","Call for Security Auditors - gno.land Audit","\n\nIn the gno.land ecosystem, security is one of our top priorities as we continue to develop our blockchain focused on smart contracts and decentralized applications. As we move closer to the beta launch, we're calling on experienced security auditors to help us ensure the robustness and security of our codebase.\nWe are now accepting proposals for the auditing of the gno repository on GitHub. This is a significant and complex task, so we are looking for qualified firms or individuals with experience auditing blockchain systems, virtual machines, and distributed systems.\nYou can find the full gno.land repository here:\n\nhttps://github.com/gnolang/gno\n\n## What's in Scope?\n\nWe've already done some work to narrow the scope of the code that requires auditing. For a more detailed list of which files are in scope and which are not, please refer to the [Google Spreadsheet](https://docs.google.com/spreadsheets/d/1rAvzvCH1TBZAykWCzKpefJnbx0SaCEgnP6IypzX8Xo4/edit?usp=sharing) that outlines the specifics.\n\n### Scope Breakdown by Directory\n\nHere's the estimated breakdown of the lines of code that are in scope for auditing as of November 11th. This code is largely written in Go:\n\n* **gnovm**: 43,452 lines (25% of total)\n* **gno.land**: 18,677 lines (10% of total)\n* **tm2**: 108,020 lines (65% of total)\n\nTotal lines in scope: 170,149 lines\n\nThis won't change drastically before the audit, so these line counts may help auditors estimate time costs. Many proposals we've received so far are simply time-boxing for a 4 engineer-week audit.\n\n## Auditing Approach\n\nDue to the size and complexity of the audit, we are considering breaking the task into sub-components. We may hire multiple firms to audit different parts of the codebase simultaneously. The proposed divisions are:\n\n1. Gno Virtual Machine (GnoVM) - contained in the \"gnovm\" subdirectory\n2. The Remaining Components - including the \"gno.land\" and \"tm2\" subdirectories\n\n### Key Focus Areas for Auditors\n\nWhile the full codebase needs a thorough review, we've identified several areas of potential security concern that require special attention:\n1. **Determinism in GnoVM** \n As a blockchain designed for deterministic execution, ensuring that the GnoVM executes contracts consistently across all nodes is crucial. Our goal is to eliminate non-deterministic components from Go, such as using AVL trees instead of Go maps. However, we may still have lingering issues that could lead to non-deterministic behavior. A prime example is the module within `gnovm/pkg/gnolang/values_string.go`, which should be carefully reviewed for any such issues. \n **Why this matters**: Non-determinism can lead to chain halts or splits, which could be exploited by attackers.\n2. **Other GnoVM Challenges** \n Gno.land contributor Morgan has detailed some additional areas of concern of the Virtual Machine here: https://github.com/gnolang/gno/issues/2886#issuecomment-2400274812 \n3. **Security in Realms (Smart Contracts)** \n Developers deploy smart contracts, called \"Realms,\" to the chain. Malicious Realms could attempt to inject harmful content that could affect other users of the chain, particularly in the `Render` function or supporting tools like **Gnoweb**, which displays Realms to end users. \n **Potential risk**: Cross-site scripting (XSS) and other injection attacks.\n4. **Security Risks Found in Other Blockchain VMs** \n Many blockchain VMs, such as Ethereum's EVM, have faced high-profile security issues. We expect that similar vulnerabilities could be targeted in the GnoVM, so it's crucial to audit and mitigate these risks in advance.\n5. **Key Management (gnokey)** \n Although this is a lower priority than some previously mentioned, auditors will need to review the gnokey package, which handles key generation and signing, to ensure that security best practices are being followed; for example, ensure our Ledger hardware wallet integration with gnokey uses the correct build flags.\n\n## How to Submit a Proposal\n\nWe're looking for proposals that include the following line items:\n\n- **Cost of auditing the entire \"gno\" repository** -- covering all directories and files in scope.\n- **Cost of auditing the Gno Virtual Machine (gnovm)** -- focused on the **gnovm** subdirectory.\n- **Cost of auditing the other components** -- covering the **gno.land** and **tm2** subdirectories.\n- **Fuzzing efforts** -- auditors are encouraged to include fuzzing as part of their code review process, though it should be listed as a separate line item.\n\nPlease include the timelines for auditing and your current availability beginning December 23rd. \n\n## Timeline \u0026 Contact Information\nWe expect the audit to consume at least 4 engineer weeks and conclude by the end of the day January 20th, giving us a week to remediate any high- or critical-severity issues by January 28th. Audit teams may decide to dedicate multiple auditors in parallel to meet our desired timeline. We are open to discussions with auditing teams to answer any questions about the scope of the project.\n\nPlease send your proposals via email to **security [at] tendermint [dot] com**. We are happy to meet with potential auditing teams to further discuss the details and answer any questions.\n\n\n## Conclusion\n\nThe gno.land community is committed to building a secure and resilient blockchain platform. We believe that involving experienced auditors in our development process is essential to ensure that we can deliver a secure environment for dApp developers. If you or your team have the expertise and are interested in contributing to gno.land's security, we encourage you to submit a proposal.\n\nLet's work together to make gno.land the most secure blockchain for smart contracts and decentralized applications!\n","2024-12-05T00:00:00Z","kristovatlas","gnoland,gnovm,security,audit"]}],"fee":{"gas_wanted":"100000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A4vbt/RTXs3UWUQZdg7W6glviY2ighS0MzoQ/HOb53Wy"},"signature":"uwXbEUagt9yfzeEqTf8C3pfaEiyPLeLhRdH8KaTrsOlRcMQKjjqFHsKptFT3EAc8cZ+vjdHmNlm6Z2vBhX5jhQ=="}],"memo":"Posted from gnoblog-cli"},"metadata":{"timestamp":"1733421726715"}} +{"tx":{"msg":[{"@type":"/vm.m_call","caller":"g125em6arxsnj49vx35f0n0z34putv5ty3376fg5","send":"","pkg_path":"gno.land/r/gnoland/blog","func":"ModEditPost","args":["audit-proposal-request","Call for Security Auditors - gno.land Audit","\n\nIn the gno.land ecosystem, security is one of our top priorities as we continue to develop our blockchain focused on smart contracts and decentralized applications. As we move closer to the beta launch, we're calling on experienced security auditors to help us ensure the robustness and security of our codebase.\nWe are now accepting proposals for the auditing of the gno repository on GitHub. This is a significant and complex task, so we are looking for qualified firms or individuals with experience auditing blockchain systems, virtual machines, and distributed systems.\nYou can find the full gno.land repository here:\n\nhttps://github.com/gnolang/gno\n\n## What's in Scope?\n\nWe've already done some work to narrow the scope of the code that requires auditing. For a more detailed list of which files are in scope and which are not, please refer to the [Google Spreadsheet](https://docs.google.com/spreadsheets/d/1rAvzvCH1TBZAykWCzKpefJnbx0SaCEgnP6IypzX8Xo4/edit?usp=sharing) that outlines the specifics.\n\n### Scope Breakdown by Directory\n\nHere's the estimated breakdown of the lines of code that are in scope for auditing as of November 11th. This code is largely written in Go:\n\n* **gnovm**: 43,452 lines (25% of total)\n* **gno.land**: 18,677 lines (10% of total)\n* **tm2**: 108,020 lines (65% of total)\n\nTotal lines in scope: 170,149 lines\n\nThis won't change drastically before the audit, so these line counts may help auditors estimate time costs. Many proposals we've received so far are simply time-boxing for a 4 engineer-week audit.\n\n## Auditing Approach\n\nDue to the size and complexity of the audit, we are considering breaking the task into sub-components. We may hire multiple firms to audit different parts of the codebase simultaneously. The proposed divisions are:\n\n1. Gno Virtual Machine (GnoVM) - contained in the \"gnovm\" subdirectory\n2. The Remaining Components - including the \"gno.land\" and \"tm2\" subdirectories\n\n### Key Focus Areas for Auditors\n\nWhile the full codebase needs a thorough review, we've identified several areas of potential security concern that require special attention:\n1. **Determinism in GnoVM** \n As a blockchain designed for deterministic execution, ensuring that the GnoVM executes contracts consistently across all nodes is crucial. Our goal is to eliminate non-deterministic components from Go, such as using AVL trees instead of Go maps. However, we may still have lingering issues that could lead to non-deterministic behavior. A prime example is the module within `gnovm/pkg/gnolang/values_string.go`, which should be carefully reviewed for any such issues. \n **Why this matters**: Non-determinism can lead to chain halts or splits, which could be exploited by attackers.\n2. **Other GnoVM Challenges** \n gno.land contributor [@thehowl](https://github.com/thehowl) has detailed some additional areas of concern of the Virtual Machine here: https://github.com/gnolang/gno/issues/2886#issuecomment-2400274812 \n3. **Security in Realms (Smart Contracts)** \n Developers deploy smart contracts, called \"Realms,\" to the chain. Malicious Realms could attempt to inject harmful content that could affect other users of the chain, particularly in the `Render` function or supporting tools like **Gnoweb**, which displays Realms to end users. \n **Potential risk**: Cross-site scripting (XSS) and other injection attacks.\n4. **Security Risks Found in Other Blockchain VMs** \n Many blockchain VMs, such as Ethereum's EVM, have faced high-profile security issues. We expect that similar vulnerabilities could be targeted in the GnoVM, so it's crucial to audit and mitigate these risks in advance.\n5. **Key Management (gnokey)** \n Although this is a lower priority than some previously mentioned, auditors will need to review the gnokey package, which handles key generation and signing, to ensure that security best practices are being followed; for example, ensure our Ledger hardware wallet integration with gnokey uses the correct build flags.\n\n## How to Submit a Proposal\n\nWe're looking for proposals that include the following line items:\n\n- **Cost of auditing the entire \"gno\" repository** -- covering all directories and files in scope.\n- **Cost of auditing the Gno Virtual Machine (gnovm)** -- focused on the **gnovm** subdirectory.\n- **Cost of auditing the other components** -- covering the **gno.land** and **tm2** subdirectories.\n- **Fuzzing efforts** -- auditors are encouraged to include fuzzing as part of their code review process, though it should be listed as a separate line item.\n\nPlease include the timelines for auditing and your current availability beginning December 23rd. \n\n## Timeline \u0026 Contact Information\nWe expect the audit to consume at least 4 engineer weeks and conclude by the end of the day January 20th, giving us a week to remediate any high- or critical-severity issues by January 28th. Audit teams may decide to dedicate multiple auditors in parallel to meet our desired timeline. We are open to discussions with auditing teams to answer any questions about the scope of the project.\n\nPlease send your proposals via email to **security [at] tendermint [dot] com**. We are happy to meet with potential auditing teams to further discuss the details and answer any questions.\n\n\n## Conclusion\n\nThe gno.land community is committed to building a secure and resilient blockchain platform. We believe that involving experienced auditors in our development process is essential to ensure that we can deliver a secure environment for dApp developers. If you or your team have the expertise and are interested in contributing to gno.land's security, we encourage you to submit a proposal.\n\nLet's work together to make gno.land the most secure blockchain for smart contracts and decentralized applications!\n","2024-12-05T00:00:00Z","kristovatlas","gnoland,gnovm,security,audit"]}],"fee":{"gas_wanted":"100000000","gas_fee":"1000000ugnot"},"signatures":[{"pub_key":{"@type":"/tm.PubKeySecp256k1","value":"A4vbt/RTXs3UWUQZdg7W6glviY2ighS0MzoQ/HOb53Wy"},"signature":"g6N6csY4M5jDhdZhfFvN1G7cbjJcxC8kHi370L2HvlQmS/g0fzs4pCUQJt1i2hNFzzHJLjNSAZKIS7P2c+r/ZA=="}],"memo":"Posted from gnoblog-cli"},"metadata":{"timestamp":"1733421842288"}}