diff --git a/src/main.zig b/src/main.zig index 3065525..cce0a46 100644 --- a/src/main.zig +++ b/src/main.zig @@ -18,24 +18,37 @@ const BTree = struct { } fn insert(self: *Self, value: u32) !void { + std.debug.print("attempting to insert {} into ", .{value}); + self.dbg(); + if (self.root) |*root| { - switch (root.*) { - .internal => |node| { - std.debug.print("can't insert values into {?} yet :|\n", .{node}); - }, - .leaf => |leaf| { - try leaf.insert_value(value); - }, + const result = try root.as_leaf().insert_value(value); + if (result) |split| { + // create new node which will replace self. + const parent = try Node.create(self.ally); + parent.leaf.level = split.left.level + 1; + parent.leaf.push_value(split.middle); + parent.insert_node(NodeOrLeaf.from_leaf(split.left)); + parent.insert_node(NodeOrLeaf.from_leaf(split.right)); + + self.root = .{ .internal = parent }; } } else { var leaf: *Leaf = try self.ally.create(Leaf); errdefer self.ally.destroy(leaf); leaf.init(self.ally); - try leaf.insert_value(value); + leaf.push_value(value); self.root = NodeOrLeaf{ .leaf = leaf }; } } + fn dbg(self: *Self) void { + if (self.root) |root| { + root.dbg(); + std.debug.print("\n", .{}); + } + } + fn destroy(self: *Self) void { if (self.root) |*root| { root.destroy(); @@ -51,12 +64,19 @@ const BTree = struct { internal: *Node, leaf: *Leaf, - fn destroy(self: *NodeOrLeaf) void { - self.as_leaf().destroy(); + fn destroy(self: NodeOrLeaf) void { + switch (self) { + .internal => |node| { + node.destroy(); + }, + .leaf => |leaf| { + leaf.destroy(); + }, + } } - fn as_leaf(self: *NodeOrLeaf) *Leaf { - switch (self.*) { + fn as_leaf(self: NodeOrLeaf) *Leaf { + switch (self) { .internal => |node| { return node.as_leaf(); }, @@ -70,7 +90,18 @@ const BTree = struct { if (leaf.level == 0) { return .{ .leaf = leaf }; } else { - return .{ .node = @ptrCast(Node, leaf) }; + return .{ .internal = @ptrCast(*Node, leaf) }; + } + } + + fn dbg(self: NodeOrLeaf) void { + switch (self) { + .internal => |node| { + node.dbg(); + }, + .leaf => |node| { + node.dbg(); + }, } } }; @@ -78,16 +109,25 @@ const BTree = struct { const Node = struct { leaf: Leaf, - edges: [NUM_EDGES]?NodeOrLeaf = undefined, + edges: [NUM_EDGES]?NodeOrLeaf = [_]?NodeOrLeaf{null} ** NUM_EDGES, - fn create(ally: std.mem.Allocator) !Leaf { + fn create(ally: std.mem.Allocator) !*Node { var node = try ally.create(Node); node.init(ally); return node; } - fn init(self: *Leaf, ally: std.mem.Allocator) void { + fn destroy(self: *Node) void { + for (self.get_edges()) |edge| { + if (edge) |*edg| { + edg.destroy(); + } + } + self.leaf.ally.destroy(self); + } + + fn init(self: *Node, ally: std.mem.Allocator) void { self.* = Node{ .leaf = Leaf{ .ally = ally } }; } @@ -99,16 +139,18 @@ const BTree = struct { const self_leaf = self.as_leaf(); const ls = child.as_leaf().get_values()[0]; - var idx: u16 = 0; + var idx: u16 = self_leaf.len; for (self_leaf.get_values(), 0..) |v, i| { - idx = @intCast(u16, i); if (v > ls) { + idx = @intCast(u16, i); break; } } - if (self.get_edges()[idx]) |edge| { - std.debug.print("edge already present?: {?}", .{edge}); + if (self.get_edges()[idx]) |_| { + std.debug.print("edge already present?:", .{}); + child.dbg(); + std.debug.print("\n", .{}); } else { child.as_leaf().parent = .{ .parent = self, .idx = idx }; self.get_edges()[idx] = child; @@ -116,14 +158,39 @@ const BTree = struct { } fn get_edges(self: *Node) []?NodeOrLeaf { - const len = self.leaf.len; + const len = self.leaf.len + 1; return self.edges[0..len]; } + + fn dbg(self: *Node) void { + const values = self.leaf.get_values(); + const edges = self.get_edges()[0..values.len]; + std.debug.print("{{ ", .{}); + for (values, edges) |v, e| { + if (e) |edge| { + edge.dbg(); + std.debug.print(", ", .{}); + } + std.debug.print("{}, ", .{v}); + } + if (self.get_edges()[values.len]) |edge| { + edge.dbg(); + } + std.debug.print(" }}", .{}); + } }; const ParentPtr = struct { parent: *Node, idx: u16, + + fn into_node_or_leaf(self: ?ParentPtr) ?NodeOrLeaf { + if (self) |ptr| { + return .{ .node = ptr.parent }; + } else { + return null; + } + } }; const Leaf = struct { @@ -141,6 +208,11 @@ const BTree = struct { return leaf; } + fn dbg(self: *Leaf) void { + const values = self.get_values(); + std.debug.print("{any}", .{values}); + } + fn init(self: *Leaf, ally: std.mem.Allocator) void { self.* = Leaf{ .ally = ally }; } @@ -212,12 +284,41 @@ const BTree = struct { return .{ .left = self, .middle = middle, .right = new }; } - fn insert_value(self: *Leaf, value: u32) !void { - if (self.len < CAPACITY) { - self.push_value(value); + // returns null on success, or a split result which could not be merged + // up because we are at the root node + fn insert_value(self: *Leaf, value: u32) !?SplitResult { + const leaf = self; + + if (leaf.len < CAPACITY) { + std.debug.print("pushing value {} into leaf ", .{value}); + self.dbg(); + std.debug.print("\n", .{}); + + leaf.push_value(value); } else { - return error.LeafAtCapacity; + const split = try leaf.split_at(value); + std.debug.print("splitting node ", .{}); + self.dbg(); + std.debug.print("into [", .{}); + split.left.dbg(); + std.debug.print(", {}, ", .{split.middle}); + split.right.dbg(); + std.debug.print("]\n", .{}); + + if (leaf.parent) |parent| { + // parent can only throw split if it has no parent, so we just pass it back along to the top? + const result = try parent.parent.as_leaf().insert_value(split.middle); + parent.parent.insert_node(NodeOrLeaf.from_leaf(split.right)); + + if (result) |for_root| { + return for_root; + } + } else { + return split; + } } + + return null; } fn get_values(self: *Leaf) []u32 { @@ -279,6 +380,20 @@ test "leaf split" { std.debug.print("split: {?}\n", .{split}); tree.ally.destroy(split.right); + return error.SkipZigTest; +} + +test "btree insert" { + std.debug.print("testing insertion\n", .{}); + var tree = BTree.create(std.testing.allocator); + defer tree.destroy(); + try tree.insert(10); + try tree.insert(4); + try tree.insert(6); + try tree.insert(3); + try tree.insert(9); + try tree.insert(8); + tree.dbg(); } test "btree new" {