Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 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
4518717
feat: integrating art with dir.go
aivillio May 27, 2026
3591e9f
feat: art integration
aivillio Jun 1, 2026
c9b5a04
feat: ART testing ,feat:rename fn testing , bug: imporper update fixed
aivillio Jun 5, 2026
fac8501
chore : fixing minor bugs concerning mutex locks
aivillio Jun 10, 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.New()

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
58 changes: 58 additions & 0 deletions internal/art/art.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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 interface{}) {
t.root = insert(t.root, value, key, 0)
}

func (t *Tree) Search(key []byte) (interface{}, 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 nil, false
}
Comment on lines +17 to +23
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]
}
func (t *Tree) ForEach(fn func([]byte, interface{})) {
traverse(t.root, 0, fn)
}
func New() *Tree {
return &Tree{}
}
func (t *Tree) Empty() bool {
return t.root == nil
}
184 changes: 184 additions & 0 deletions internal/art/art_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package art

import (
"fmt"
"testing"
)

func TestInsertAndSearch(t *testing.T) {
var tree Tree

tree.Insert([]byte("hello"), "world")

v, ok := tree.Search([]byte("hello"))
if !ok {
t.Fatal("key not found")
}
if v != "world" {
t.Fatalf("got %v wanted world", v)
}

}
func TestSearchMissing(t *testing.T) {
var tree Tree

_, ok := tree.Search([]byte("ghost"))

if ok {
t.Fatal("expected key not found")
}
}

func TestDelete(t *testing.T) {
var tree Tree

tree.Insert([]byte("hello"), "world")

if !tree.Delete([]byte("hello")) {
t.Fatal("delete failed")
}

_, ok := tree.Search([]byte("hello"))

if ok {
t.Fatal("key still exists")
}
}

func TestPrefixOverlap(t *testing.T) {
var tree Tree

keys := []string{
"a",
"ab",
"abc",
"abcd",
}

for _, k := range keys {
tree.Insert([]byte(k), k)
}

for _, k := range keys {
_, ok := tree.Search([]byte(k))

if !ok {
t.Fatalf("missing key %s", k)
}
}
}

func TestDeletePrefix(t *testing.T) {
var tree Tree

tree.Insert([]byte("a"), "a")
tree.Insert([]byte("ab"), "ab")
tree.Insert([]byte("abc"), "abc")

tree.Delete([]byte("ab"))

if _, ok := tree.Search([]byte("ab")); ok {
t.Fatal("ab still exists")
}

if _, ok := tree.Search([]byte("a")); !ok {
t.Fatal("a missing")
}

if _, ok := tree.Search([]byte("abc")); !ok {
t.Fatal("abc missing")
}
}
func TestUpdate(t *testing.T) {
var tree Tree

tree.Insert([]byte("hello"), "goob")

tree.Insert([]byte("hello"), "new")

v, _ := tree.Search([]byte("hello"))
fmt.Printf("%v", v)

if v != "new" {
t.Fatalf("got %v want new", v)
}
}
func TestForEach(t *testing.T) {
var tree Tree

tree.Insert([]byte("cat"), "cat")
tree.Insert([]byte("apple"), "apple")
tree.Insert([]byte("banana"), "banana")

var result []string

tree.ForEach(func(k []byte, v interface{}) {
result = append(result, string(k))
})

expected := []string{
"apple",
"banana",
"cat",
}

for i := range expected {
if result[i] != expected[i] {
t.Fatalf("got %v want %v", result, expected)
}
}
}

func TestNodeGrowth(t *testing.T) {
var tree Tree

for i := 0; i < 5; i++ {
tree.Insert([]byte{byte(i)}, "x")
}

if GetNodeTypeName(tree.Root()) != "Node16" {
t.Fatal("expected Node16")
}
for i := 0; i < 17; i++ {
tree.Insert([]byte{byte(i)}, "x")
}

if GetNodeTypeName(tree.Root()) != "Node48" {
t.Fatal("expected Node48")
}
for i := 0; i < 49; i++ {
tree.Insert([]byte{byte(i)}, "x")
}

if GetNodeTypeName(tree.Root()) != "Node256" {
t.Fatal("expected Node256")
}

}

func TestNodeShrink(t *testing.T) {
var tree Tree

for i := 0; i < 49; i++ {
tree.Insert([]byte{byte(i)}, "x")
}
tree.Delete([]byte{byte(48)})
if GetNodeTypeName(tree.Root()) != "Node48" {
t.Fatal("expected Node48")
}
for i := 47; i > 15; i-- {
tree.Delete([]byte{byte(i)})

}
if GetNodeTypeName(tree.Root()) != "Node16" {
t.Fatal("expected Node16")
}
for i := 15; i > 3; i-- {
tree.Delete([]byte{byte(i)})

}
if GetNodeTypeName(tree.Root()) != "Node4" {
t.Fatal("expected Node4")
}

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

import (
"bytes"
)

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

if isleaf(n) {
if bytes.Equal(n.leaf.key, 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
}
Loading