Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b1a487c
chore:Initializing project structure and node files
Bhuviiiii-prog Mar 19, 2026
058b2e5
feat:insert+utility functions
aivillio Mar 19, 2026
3577de9
Feat: Add Search/lookup function and remove insert.go
Bhuviiiii-prog Mar 20, 2026
68dbdbe
chore: fixed bug 'variable size keys index out of bounds error'
aivillio Mar 20, 2026
94bf757
Merge branch 'artDev' of github.com:Bhuviiiii-prog/radFS into dev
aivillio Mar 20, 2026
db64be6
feat:added sorting to addchild
aivillio Mar 20, 2026
3f86edc
chore: put insert and search in different files
aivillio Mar 20, 2026
72a7ead
Chore:Delete insert_test.go and added comments for search function
Bhuviiiii-prog Mar 20, 2026
f1c491b
feat: now can update values and fixed duplicate key bug in addchild
aivillio Mar 21, 2026
e5b9dde
chore: merged changes from bhuvigna
aivillio Mar 21, 2026
dfd6c14
feature: added grow, updated findchild , added num_children feild for…
aivillio Mar 24, 2026
56761fd
feat: add delete support-recursive deletekey and removechild utility …
Bhuviiiii-prog Mar 24, 2026
0352d08
chore :the tree now handles the case when prefix is longer than maxp…
aivillio Mar 27, 2026
b2cd7ec
chore: resolve merge conflicts
Bhuviiiii-prog Mar 28, 2026
278149a
feat: removed the use of sentinal value and stores the leaf inside in…
aivillio Mar 28, 2026
34e0389
chore :week4 log
aivillio Mar 28, 2026
d85da6a
Update docs/weekly/angelo/week_4.md
aivillio Mar 28, 2026
5c3fb51
Update docs/weekly/angelo/week_4.md
aivillio Mar 28, 2026
975d086
refactor(insert): remove sentinel values and fix key exhaustion bug
Bhuviiiii-prog Mar 30, 2026
2504374
fix(insert): remove sentinel values and fix key exhaustion bug in ins…
Bhuviiiii-prog Mar 30, 2026
1385920
o
Bhuviiiii-prog Mar 30, 2026
f12534e
fix: correct key exhaustion bug in deletekey and remove keycheck func…
Bhuviiiii-prog Mar 30, 2026
ab066c6
chore: added deep copy to prevent to make new_node and current node i…
aivillio Mar 31, 2026
277c89c
chore:forgot to add deepcopy func from utilty
aivillio Mar 31, 2026
b423a0c
chore : made addchild and fetchleaf compatible with multiple node types
aivillio Mar 31, 2026
ffc8e5c
fix: Perform deepcopy of metadata -update copymeta func
Bhuviiiii-prog Apr 1, 2026
3e65865
chore:Growth logic and Deepcopy implementation-Merged conflicts from …
Bhuviiiii-prog Apr 2, 2026
e256dfd
fix: upgrade removechild func to handle different nodeTypes while shr…
Bhuviiiii-prog Apr 2, 2026
6309e6c
feat: Defined shrink func , need to fix node4-> leaf collapse logic
Bhuviiiii-prog Apr 2, 2026
a24f54e
fix: Capturing the shrunk node (n=removechild(n,k) and returning n
Bhuviiiii-prog Apr 3, 2026
a8aa252
chore: Update main.go to test Growth and shrink functioning of diff n…
Bhuviiiii-prog Apr 3, 2026
35d2dca
feat: Define GetNodeTypeName func to check the current nodetype in ma…
Bhuviiiii-prog Apr 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions cmd/radFS/arttest/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package main

import (
"fmt"

"github.com/acmpesuecc/radFS/internal/art"
)

func main() {
t := &art.Tree{}

fmt.Println("====== PHASE 1: STRESS TESTING GROWTH (4 -> 16 -> 48 -> 256) ======")

// We use keys with a single byte difference to ensure they all go into the SAME internal node
for i := 0; i < 256; i++ {
key := []byte{byte(i)}
t.Insert(key, fmt.Sprintf("%d", i))

switch i + 1 {

case 5:
fmt.Printf("[Check] Inserted 5 keys. (Current: %d children)\n", i+1)
fmt.Printf("NodeType: %s\n", art.GetNodeTypeName(t.Root()))
case 17:
fmt.Printf("[Check] Inserted 17 keys. (Current: %d children)\n", i+1)
fmt.Printf("NodeType: %s\n", art.GetNodeTypeName(t.Root()))
case 49:
fmt.Printf("[Check] Inserted 49 keys. (Current: %d children)\n", i+1)
fmt.Printf("NodeType: %s\n", art.GetNodeTypeName(t.Root()))
}
}

fmt.Printf("Final Growth State: %d children in root.\n", 256)

fmt.Println("\n===== PHASE 2: SEARCH VERIFICATION =====")
// Verify we didn't lose data during the messy pointer copying in grow/shrink
testKeys := []int{0, 15, 47, 100, 255}
for _, tk := range testKeys {
val, ok := t.Search([]byte{byte(tk)})
fmt.Printf("Searching for key %d: Found=%v, Value=%v\n", tk, ok, val)
}

fmt.Println("\n===== PHASE 3: STRESS TESTING SHRINK (256 -> 48 -> 16 -> 4) =====")

for i := 255; i >= 37; i-- {
t.Delete([]byte{byte(i)})
}
fmt.Println("[Check] Deleted down to 37 keys")
fmt.Printf("NodeType: %s\n", art.GetNodeTypeName(t.Root()))

for i := 36; i >= 12; i-- {
t.Delete([]byte{byte(i)})
}
fmt.Println("[Check] Deleted down to 12 keys. ")
fmt.Printf("NodeType: %s\n", art.GetNodeTypeName(t.Root()))

for i := 11; i >= 3; i-- {
t.Delete([]byte{byte(i)})
}
fmt.Println("[Check] Deleted down to 3 keys. ")
fmt.Printf("NodeType: %s\n", art.GetNodeTypeName(t.Root()))

fmt.Println("\n===== PHASE 4: FULL COLLAPSE (Node4 -> Leaf -> Nil) =====")

// Delete until only 1 key remains (should collapse Node4 into a Leaf)
for i := 2; i >= 1; i-- {
t.Delete([]byte{byte(i)})
}
fmt.Println("[Check] Deleted down to 1 key. Tree should be a single Leaf (no Internal Node).")
art.PrintTree(t.Root(), 0, 0)

// Delete the very last key
t.Delete([]byte{byte(0)})
fmt.Println("[Check] Deleted last key. Root should be nil.")

if t.Root() == nil {
fmt.Println("SUCCESS: Tree is fully empty (Root is nil).")
} else {
fmt.Println("WARNING: Root is not nil. Check your Node4 -> Leaf collapse logic.")
art.PrintTree(t.Root(), 0, 0)
}

fmt.Println("\n===== FINAL TREE STRUCTURE =====")
fmt.Printf("NodeType: %s\n", art.GetNodeTypeName(t.Root()))

}
8 changes: 8 additions & 0 deletions docs/weekly/angelo/week_4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Angelo's Progress
* implemented the insert function
* implemented update function in addchild


## What's next?
* implement grow to transition to larger nodes
* fix the bugs in insert
49 changes: 49 additions & 0 deletions internal/art/art.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package art

// TODO: Public API (Tree struct, Insert, Search, Delete)

type Tree struct {
root *Node
}

func (t *Tree) Root() *Node {
return t.root
}

func (t *Tree) Insert(key []byte, value string) {
t.root = insert(t.root, value, key, 0)
}

func (t *Tree) Search(key []byte) (string, bool) {
leaf := search(t.root, key, 0) // start from root and depth 0
if leaf != nil && isleaf(leaf) {
return leaf.leaf.values, true //Node->innerleaf->values
}
return "", false
}
func (t *Tree) Delete(key []byte) bool {
if t.root == nil {
return false
}

newRoot, deleted := deletekey(t.root, key, 0)

if deleted {
t.root = newRoot
return true
}

return false
}

func GetNodeTypeName(n *Node) string {
if n == nil {
return "Nil"
}
if isleaf(n) {
return "Leaf"
}

types := []string{"Node4", "Node16", "Node48", "Node256"}
return types[n.innerNode.nodeType]
}
49 changes: 49 additions & 0 deletions internal/art/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package art

func deletekey(n *Node, key []byte, depth int) (*Node, bool) {
if n == nil {
return nil, false
}

if isleaf(n) {
if string(n.leaf.key) == string(key) {
return nil, true
}
return n, false
}

p := checkprefix(n, key, depth)
if p != n.innerNode.meta.prefixlen {
return n, false
}
depth += n.innerNode.meta.prefixlen

// 3. KEY EXHAUSTION (The Fix)
// If the key ends here, the value is in the inner node's leaf field
if depth == len(key) {
if n.innerNode.leaf != nil {
n.innerNode.leaf = nil // Remove the value
return n, true
}
return n, false
}

k := key[depth]
child, pos := findchild(k, n)
if child == nil {
return n, false
}

newChild, deleted := deletekey(child, key, depth+1)
if !deleted {
return n, false
}

if newChild == nil {
n = removechild(n, k)
} else {
n.innerNode.children[pos] = newChild
}

return n, true
}
104 changes: 104 additions & 0 deletions internal/art/insert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package art

func insert(n *Node, value string, key []byte, depth int) *Node {

if n == nil {
return newleaf(value, key)
}
if isleaf(n) {
new_node := newNode4()
oldkey := n.leaf.key
i := depth

for i < len(oldkey) && i < len(key) && oldkey[i] == key[i] {
prefix_index := i - depth
if prefix_index < maxprefixlen {
new_node.innerNode.meta.prefix[prefix_index] = key[i] // stores only the till max prefix

}

i++
}

new_node.innerNode.meta.prefixlen = i - depth // stores full prefix len even after maxprefixlen
depth = i
if depth == len(key) {
new_node.innerNode.leaf = newleaf(value, key)

} else {
new_node = addchild(new_node, key[depth], newleaf(value, key))

}
if depth == len(oldkey) {
new_node.innerNode.leaf = n

} else {
new_node = addchild(new_node, oldkey[depth], n)

}

return new_node

}
p := checkprefix(n, key, depth)

if p != n.innerNode.meta.prefixlen {

new_node := newNode4()
if p+depth == len(key) {
new_node.innerNode.leaf = newleaf(value, key)

} else {
new_node = addchild(new_node, key[depth+p], newleaf(value, key))

}
leaf := fetchleaf(n) // either its an actual leaf or innernode leaf
oldkey := leaf.leaf.key

var oldkeybyte byte
if p < maxprefixlen {
oldkeybyte = n.innerNode.meta.prefix[p]
} else {
oldkeybyte = oldkey[depth+p]
}

new_node = addchild(new_node, oldkeybyte, n)

new_node.innerNode.meta.prefixlen = p
if p < maxprefixlen {
new_node.innerNode.meta.prefix = deepcopy(n.innerNode.meta.prefix[:p])

} else {
new_node.innerNode.meta.prefix = deepcopy(n.innerNode.meta.prefix[:maxprefixlen])
}

oldprefixlen := n.innerNode.meta.prefixlen
n.innerNode.meta.prefixlen = oldprefixlen - (p + 1)
if oldprefixlen < maxprefixlen {
n.innerNode.meta.prefix = deepcopy(n.innerNode.meta.prefix[p+1 : oldprefixlen])

} else {
leaf := fetchleaf(n)
n.innerNode.meta.prefix = deepcopy(leaf.leaf.key[depth+p+1 : depth+p+1+maxprefixlen])
}

return new_node
}

depth += n.innerNode.meta.prefixlen
if depth == len(key) {
n.innerNode.leaf = newleaf(value, key)
return n
}
next, pos := findchild(key[depth], n)
if next != nil {
n.innerNode.children[pos] = insert(next, value, key, depth+1)
return n

} else {
n = addchild(n, key[depth], newleaf(value, key))
return n

}

}
18 changes: 18 additions & 0 deletions internal/art/leaf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package art

// TODO: Leaf node structure for storing values

type leaf struct {
key []byte
values string
}

func newleaf(value string, key []byte) *Node {
return &Node{
leaf: &leaf{key: key, values: value},
}

}
func isleaf(n *Node) bool {
return n.leaf != nil
}
41 changes: 41 additions & 0 deletions internal/art/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package art

// TODO: Interfaces and shared node header (meta)

type NodeType int

const (
Node4 NodeType = iota
Node16
Node48
Node256
)
const (
Node4max = 4
Node16Max = 16
Node48Max = 48
Node256Max = 256

maxprefixlen = 8
)

type Node struct {
innerNode *innerNode
leaf *leaf
}

type innerNode struct {
nodeType NodeType
keys []byte
children []*Node
leaf *Node

num_children int

meta meta
}

type meta struct {
prefix []byte
prefixlen int
}
15 changes: 15 additions & 0 deletions internal/art/node16.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package art

// TODO: Node16 implementation
func newNode16() *Node {
in := &innerNode{
nodeType: Node16,
keys: make([]byte, Node16Max),
children: make([]*Node, Node16Max),
num_children: 0,
meta: meta{
prefix: make([]byte, maxprefixlen),
},
}
return &Node{innerNode: in}
}
16 changes: 16 additions & 0 deletions internal/art/node256.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package art

// TODO: Node256 implementation (direct map)

func newNode256() *Node {
in := &innerNode{
nodeType: Node256,
children: make([]*Node, Node256Max),
num_children: 0,

meta: meta{
prefix: make([]byte, maxprefixlen),
},
}
return &Node{innerNode: in}
}
Loading