#AVL Tree implementation with TealScript

118 messages · Page 1 of 1 (latest)

hoary olive
#

https://github.com/VKappaKV/avlTree/blob/main/projects/avlTree/contracts/AvlTree.algo.ts

Testing out how to implement an AVL Tree in Tealscript.

current error:
throw Error(Cannot resolve type ${type.getText()} (${typeString}));

implementation problem:
AVL tree balance uses <-1 and > +1 to understand balancing rotations for the tree, since negative numbers aren't supported there needs to be a workaround to go about Right case rotations

GitHub

Contribute to VKappaKV/avlTree development by creating an account on GitHub.

#

@rapid crow

rapid crow
#

Hit me with that full stack trace 😎

hoary olive
#

Error: Cannot resolve type null (null)
at Compiler.getTypeInfo (C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:714:15)
at Compiler.getTypeInfo (C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:706:36)
at C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:650:61
at Array.forEach (<anonymous>)
at C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:647:37
at Array.forEach (<anonymous>)
at Compiler.getTypeInfo (C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:646:34)
at C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:2079:33
at Array.forEach (<anonymous>)
at C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:2078:58

rapid crow
#

looks like node is definitely null somewhere. Might take a look at some of the non-null (!)assertions in your code, e.g.,

  private getBalanceFactor(node: Node): uint64 {
    const res = this.getHeight(node.left!) - this.getHeight(node.right!);
    return node ? res : 0;
  }
hoary olive
#

Gotcha, tomorrow I'll refactor a bit to handle those cases ty

rapid crow
#

you bet, let us know how it goes

hoary olive
#

Btw, I want to make a test to check performances with the AVL tree vs a standard FIFO array

How would you suggest to go about this test?
Opcode cost per txn? Just to see Big O, Big Omega and Big Theta

#

For lookup, insertion, modification and delete

rapid crow
#

yes - focusing on op codes makes sense. You could write a test that sums up the op code budget used for transactions processed via the tree or array.

hoary olive
#

C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:714
throw Error(Cannot resolve type ${type.getText()} (${typeString}));
^

Error: Cannot resolve type null (null)
at Compiler.getTypeInfo (C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:714:15)
at Compiler.getTypeInfo (C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:706:36)
at C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:650:61
at Array.forEach (<anonymous>)
at C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:647:37
at Array.forEach (<anonymous>)
at Compiler.getTypeInfo (C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:646:34)
at C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:2079:33
at Array.forEach (<anonymous>)
at C:\Users\Jacopo\AlgoDevving\avlTree\projects\avlTree\node_modules@algorandfoundation\tealscript\dist\lib\compiler.js:2078:58

still getting this error. I did some refactoring to account for uint https://github.com/VKappaKV/avlTree/blob/main/projects/avlTree/contracts/AvlTree.algo.ts

GitHub

Contribute to VKappaKV/avlTree development by creating an account on GitHub.

#

I've also done a typescript one to compare if something was a problem but the TS one is working, while Tealscript returns me this error #1299070704603496448 message

#

btw I'm also getting this error in the tsconfig.json

No inputs were found in config file 'c:/Users/Jacopo/Algodevving/avlTree/projects/avlTree/node_modules/@algorandfoundation/tealscript/tsconfig.json'. Specified 'include' paths were '["src/**/*"]' and 'exclude' paths were '["c:/Users/Jacopo/Algodevving/avlTree/projects/avlTree/node_modules/@algorandfoundation/tealscript/dist"]'.

#

@rapid crow

ripe fern
hoary olive
#

so I guess that's not related to the compiler error

#

Does TealScript support null type?

ripe fern
hoary olive
ripe fern
#

That sounds right, but I have not pushed the boundaries on this

#

But at runtime you can't play with null

hoary olive
ripe fern
#
type Node = {
  data: uint64;
  left: Node | null;
  right: Node | null;
  leftHeight: uint64;
  rightHeight: uint64;
};```
#

@winter chasm Can you do dis in TealScript?

#

I would imagine you would need to define a non-null value that can serve as null in the logic

#

This endeavor does beg the question of if this logic needs to be performed on chain or off

#

What are you trying to achieve?

hoary olive
#

can dm you more details if you want

ripe fern
#

Is it a secret?

winter chasm
#

Neither null or undefined are supported in TEALScript. It also doesn't support union types in general

hoary olive
hoary olive
ripe fern
#

Could you just have a Node that represents null?

hoary olive
#

yeah at that point maybe I can get away with using bytes and setting an empty string

#

so maybe something like Node | Bytes

#

and check "" => treat it as null

#

?

ripe fern
#

No union types

#

Just have a Node struct that represents null?

hoary olive
hoary olive
#

gotta think about it

ripe fern
#

Check for equivalence vs the special "null" Node struct value

#

Or

#

Do the Node structs sit in boxes?

hoary olive
#

with typeof(NullNode)?

ripe fern
#

You could have the absence of a box represent a null node

#

and do box existence checks

hoary olive
#

pepethink at first I thought about using one box, because if each node is a box then the txn might run out of box references?

ripe fern
#

True

#

You could pack box refs on to other outer txns up to a point

hoary olive
#

mmh not the ideal to scale the structure to 100s + of nodes

ripe fern
#

OK so that won't work, then

hoary olive
ripe fern
#

What if you just had a property of the Node struct that was a boolean which indicated if it was a null node?

#
type Node = {
  data: uint64;
  left: Node;
  right: Node;
  leftHeight: uint64;
  rightHeight: uint64;
  isNullNode: boolean
}
hoary olive
#

if (!node) return { data, left: null, right: null, leftHeight: 1, rightHeight: 1 };

considered this case, where when I insert a new node I have to declare his 2 leafs nulls as it's a new leaf insertion. Instead of null what should I use? It will expect a type Node

#

I can do the boolean, but wouldn't it return an error?

#

maybe something like

type Node = {
  data: uint64;
  left?: Node;
  right?: Node;
  leftHeight: uint64;
  rightHeight: uint64;
  isNullNode: boolean
}
ripe fern
#

I need a snack and then will fiddle with this to see if I can get what I have in mind to work

hoary olive
#

peepolove ty man

rapid crow
#

left: Node || 0 ? catching up, disregard if this suggestion makes no sense ;p

hoary olive
hoary olive
#

what if...yes unions Okayge

ripe fern
#

OK hold up

hoary olive
#

@winter chasm plz

ripe fern
#

Your Node type is infinitely deep

hoary olive
ripe fern
#

This won't work because you don't have a way to terminate at 2 leaves deep

hoary olive
#

In-order Traversal of AVL Tree:
1
2
3
4
5
7
15
17
18
20
25
27
30
35
40
45
48
50
55
60
{"data":15,"left":{"data":5,"left":{"data":3,"left":{"data":1,"left":null,"right":{"data":2,"left":null,"right":null}},"right":{"data":4,"left":null,"right":null}},"right":{"data":7,"left":null,"right":null}},"right":{"data":40,"left":{"data":25,"left":{"data":20,"left":{"data":17,"left":null,"right":{"data":18,"left":null,"right":null}},"right":null},"right":{"data":30,"left":{"data":27,"left":null,"right":null},"right":{"data":35,"left":null,"right":null}}},"right":{"data":50,"left":{"data":45,"left":null,"right":{"data":48,"left":null,"right":null}},"right":{"data":60,"left":{"data":55,"left":null,"right":null},"right":null}}}}

#

this is a print for the Typescript implementation

hoary olive
rapid crow
#

what about representing it as a terminal node? e.g., sentinel pattern

type Node = {
    ...
    isNullNode: boolean; // whatever value works
};
ripe fern
#

I think you could figure out the byte length of a Node struct in storage and then have an array of zero bytes of the same size to use as a placeholder value in the terminal leaves

hoary olive
#

ok so if I init the AVL tree with nothing it's a

null

and if I want to start with one it will be :

{"data":1,"left":null,"right":null}

So what you mean is that I should make it look like:

{"data":1, "left": // an empty struct with just the fields , "right": // empty struct}

#

would that pass the compiler check for Node type?

#

I'll try it on typescript first to see if it works

ripe fern
#

Neither a null value nor a recursive type is going to work here

#

Also, you mentioned the limit on box references

hoary olive
#

yeah...

ripe fern
#
    maxKeys: 1,
    allowPotentialCollisions: false,
  });```
hoary olive
ripe fern
#

Does this thing cap out at 64 Nodes?

hoary olive
#

isn't the limit for 1 box 32K?

ripe fern
#

You are using a GlobalStateMap

#

global storage is capped at 64 k/v pairs

hoary olive
#

oh right, will fix it

ripe fern
#

And max 128 box refs for an atomic group of 16 app calls

#

So you want to pack Nodes into a big box?

hoary olive
#

I'm not sure if I should find another data structure to use. But AVL tree made the most sense

ripe fern
#

How will you get them out of a big box?

#

Read up to 4kb and then index into an array of Nodes?

hoary olive
#

why should the contract do that? the operation it purely needs the AVL for is to understand the address linked to a certain node in the Tree

#

The lookup shouldn't read more than log n. Actually this is an interesting thing to look into, there might be a max node limit that would require another box to represent a subtree

#

But considered the limit on recursive types I might need to look into different types of solutions. Bit of a bummer

ripe fern
#

I suspect you are going to find this to be highly constrained by box read/write mechanics

#

If you get max 4kb out of a box, that could be many Nodes, and now you need to find the node you need, so you probably have to iterate over them

#

You'll burn up opcode in a loop trying to find the node you want to modify

hoary olive
#

yeah but it's not an array where you have to iterate over 4kb...

ripe fern
#

4kb/Node struct byte length

#

If you want a box to be the storage array, you will need to locate whatever Node you want to read or write

#

TypeScript abstracts all this away normally 😉

hoary olive
#

Made some calculation, 100 Nodes would result in 7 depths for worst case of lookup. Which for 100 nodes that are represented in 3453 bytes​ a worst case of approximated 242 bytes. It could then be split in more subtrees in each Box

ripe fern
#

Now I think you can start to work out how to work with the box data, which will be extracted as one blob and you'll have to parse it to find any particular Node for read/write ops

hoary olive
#

mmh yeah but either way it's not implementable with recursive types so it's probably better if I use another data structure to represent the priority queue

ripe fern
#

What if the Node struct was like

type Node = {
  data: uint64;
  leftID: uint64;
  rightID: uint64;
  leftHeight: uint64;
  rightHeight: uint64;

}```
hoary olive
#

so the ID would most likely be just the position in the array and the connection is changed with rotations etc.

#

pepethink am I right?

#

Btw will think it through. But currently I'm also just thinking if I can shift to another type, either a bitmap or a Merkle tree, but just thoughts

ripe fern
winter chasm
#

You can create a nullNode like this: const nullNode: Node = castBytes<Node>(bzero(len<Node>()));. You'd probably want to save this in global state or a scratch var each call to use in equality checks.