mirror of
https://github.com/arnaucube/arbo.git
synced 2026-01-08 15:01:29 +01:00
Add AddBatch CaseB
CASE B: ALMOST CASE A, Almost empty Tree --> if Tree has numLeafs < minLeafsThreshold
==============================================================================
- Get the Leafs (key & value) (iterate the tree from the current root getting
the leafs)
- Create a new empty Tree
- Do CASE A for the new Tree, giving the already existing key&values (leafs)
from the original Tree + the new key&values to be added from the AddBatch call
R R
/ \ / \
A * / \
/ \ / \
B C * *
/ | / \
/ | / \
/ | / \
L: A B G D
/ \
/ \
/ \
C *
/ \
/ \
/ \
... ... (nLeafs < minLeafsThreshold)
This commit is contained in:
61
addbatch.go
61
addbatch.go
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
||||||
AddBatch design
|
AddBatch design
|
||||||
===============
|
===============
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ CASE A: Empty Tree --> if tree is empty (root==0)
|
|||||||
- Build the full tree from bottom to top (from all the leaf to the root)
|
- Build the full tree from bottom to top (from all the leaf to the root)
|
||||||
|
|
||||||
|
|
||||||
CASE B: ALMOST CASE A, Almost empty Tree --> if Tree has numLeafs < numBuckets
|
CASE B: ALMOST CASE A, Almost empty Tree --> if Tree has numLeafs < minLeafsThreshold
|
||||||
==============================================================================
|
==============================================================================
|
||||||
- Get the Leafs (key & value) (iterate the tree from the current root getting
|
- Get the Leafs (key & value) (iterate the tree from the current root getting
|
||||||
the leafs)
|
the leafs)
|
||||||
@@ -33,7 +32,7 @@ from the original Tree + the new key&values to be added from the AddBatch call
|
|||||||
B C
|
B C
|
||||||
|
|
||||||
|
|
||||||
CASE C: ALMOST CASE B --> if Tree has few Leafs (but numLeafs>=numBuckets)
|
CASE C: ALMOST CASE B --> if Tree has few Leafs (but numLeafs>=minLeafsThreshold)
|
||||||
==============================================================================
|
==============================================================================
|
||||||
- Use A, B, G, F as Roots of subtrees
|
- Use A, B, G, F as Roots of subtrees
|
||||||
- Do CASE B for each subtree
|
- Do CASE B for each subtree
|
||||||
@@ -112,8 +111,8 @@ L: M1 * M2 * (where M1 and M2 are empty)
|
|||||||
Algorithm decision
|
Algorithm decision
|
||||||
==================
|
==================
|
||||||
- if nLeafs==0 (root==0): CASE A
|
- if nLeafs==0 (root==0): CASE A
|
||||||
- if nLeafs<nBuckets: CASE B
|
- if nLeafs<minLeafsThreshold: CASE B
|
||||||
- if nLeafs>=nBuckets && nLeafs < minLeafsThreshold: CASE C
|
- if nLeafs>=minLeafsThreshold && (nLeafs/nBuckets) < minLeafsThreshold: CASE C
|
||||||
- else: CASE D & CASE E
|
- else: CASE D & CASE E
|
||||||
|
|
||||||
|
|
||||||
@@ -148,14 +147,35 @@ func (t *Tree) AddBatchOpt(keys, values [][]byte) ([]int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if nLeafs==0 (root==0): CASE A
|
// if nLeafs==0 (root==0): CASE A
|
||||||
e := make([]byte, t.hashFunction.Len())
|
if bytes.Equal(t.root, t.emptyHash) {
|
||||||
if bytes.Equal(t.root, e) {
|
|
||||||
// CASE A
|
|
||||||
// sort keys & values by path
|
// sort keys & values by path
|
||||||
sortKvs(kvs)
|
sortKvs(kvs)
|
||||||
return t.buildTreeBottomUp(kvs)
|
return t.buildTreeBottomUp(kvs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if nLeafs<nBuckets: CASE B
|
||||||
|
nLeafs, err := t.GetNLeafs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
minLeafsThreshold := uint64(100) // nolint:gomnd // TMP WIP
|
||||||
|
if nLeafs < minLeafsThreshold {
|
||||||
|
// get already existing keys
|
||||||
|
aKs, aVs, err := t.getLeafs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aKvs, err := t.keysValuesToKvs(aKs, aVs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// add already existing key-values to the inputted key-values
|
||||||
|
kvs = append(kvs, aKvs...)
|
||||||
|
// proceed with CASE A
|
||||||
|
sortKvs(kvs)
|
||||||
|
return t.buildTreeBottomUp(kvs)
|
||||||
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("UNIMPLEMENTED")
|
return nil, fmt.Errorf("UNIMPLEMENTED")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +229,18 @@ func (t *Tree) keysValuesToKvs(ks, vs [][]byte) ([]kv, error) {
|
|||||||
return kvs, nil
|
return kvs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (t *Tree) kvsToKeysValues(kvs []kv) ([][]byte, [][]byte) {
|
||||||
|
ks := make([][]byte, len(kvs))
|
||||||
|
vs := make([][]byte, len(kvs))
|
||||||
|
for i := 0; i < len(kvs); i++ {
|
||||||
|
ks[i] = kvs[i].k
|
||||||
|
vs[i] = kvs[i].v
|
||||||
|
}
|
||||||
|
return ks, vs
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// keys & values must be sorted by path, and must be length multiple of 2
|
// keys & values must be sorted by path, and must be length multiple of 2
|
||||||
// TODO return index of failed keyvaules
|
// TODO return index of failed keyvaules
|
||||||
func (t *Tree) buildTreeBottomUp(kvs []kv) ([]int, error) {
|
func (t *Tree) buildTreeBottomUp(kvs []kv) ([]int, error) {
|
||||||
@@ -254,3 +286,16 @@ func (t *Tree) upFromKeys(ks [][]byte) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
return t.upFromKeys(rKs)
|
return t.upFromKeys(rKs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Tree) getLeafs() ([][]byte, [][]byte, error) {
|
||||||
|
var ks, vs [][]byte
|
||||||
|
err := t.Iterate(func(k, v []byte) {
|
||||||
|
if v[0] != PrefixValueLeaf {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
leafK, leafV := readLeafValue(v)
|
||||||
|
ks = append(ks, leafK)
|
||||||
|
vs = append(vs, leafV)
|
||||||
|
})
|
||||||
|
return ks, vs, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,3 +49,53 @@ func TestAddBatchCaseA(t *testing.T) {
|
|||||||
// check that both trees roots are equal
|
// check that both trees roots are equal
|
||||||
c.Check(tree2.Root(), qt.DeepEquals, tree.Root())
|
c.Check(tree2.Root(), qt.DeepEquals, tree.Root())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddBatchCaseB(t *testing.T) {
|
||||||
|
c := qt.New(t)
|
||||||
|
|
||||||
|
nLeafs := 1024
|
||||||
|
|
||||||
|
tree, err := NewTree(memory.NewMemoryStorage(), 100, HashFunctionPoseidon)
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
defer tree.db.Close()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
for i := 0; i < nLeafs; i++ {
|
||||||
|
k := BigIntToBytes(big.NewInt(int64(i)))
|
||||||
|
v := BigIntToBytes(big.NewInt(int64(i * 2)))
|
||||||
|
if err := tree.Add(k, v); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println(time.Since(start))
|
||||||
|
|
||||||
|
tree2, err := NewTree(memory.NewMemoryStorage(), 100, HashFunctionPoseidon)
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
defer tree2.db.Close()
|
||||||
|
|
||||||
|
// add the initial leafs to fill a bit the tree before calling the
|
||||||
|
// AddBatch method
|
||||||
|
for i := 0; i < 99; i++ { // TMP TODO use const minLeafsThreshold-1 once ready
|
||||||
|
k := BigIntToBytes(big.NewInt(int64(i)))
|
||||||
|
v := BigIntToBytes(big.NewInt(int64(i * 2)))
|
||||||
|
if err := tree2.Add(k, v); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys, values [][]byte
|
||||||
|
for i := 99; i < nLeafs; i++ {
|
||||||
|
k := BigIntToBytes(big.NewInt(int64(i)))
|
||||||
|
v := BigIntToBytes(big.NewInt(int64(i * 2)))
|
||||||
|
keys = append(keys, k)
|
||||||
|
values = append(values, v)
|
||||||
|
}
|
||||||
|
start = time.Now()
|
||||||
|
indexes, err := tree2.AddBatchOpt(keys, values)
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
fmt.Println(time.Since(start))
|
||||||
|
c.Check(len(indexes), qt.Equals, 0)
|
||||||
|
|
||||||
|
// check that both trees roots are equal
|
||||||
|
c.Check(tree2.Root(), qt.DeepEquals, tree.Root())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user