initial commit
This commit is contained in:
		
						commit
						b088e7ba55
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | /zig-cache/ | ||||||
|  | /zig-out/ | ||||||
							
								
								
									
										71
									
								
								build.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								build.zig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | ||||||
|  | const std = @import("std"); | ||||||
|  | 
 | ||||||
|  | // Although this function looks imperative, note that its job is to | ||||||
|  | // declaratively construct a build graph that will be executed by an external | ||||||
|  | // runner. | ||||||
|  | pub fn build(b: *std.Build) void { | ||||||
|  |     // Standard target options allows the person running `zig build` to choose | ||||||
|  |     // what target to build for. Here we do not override the defaults, which | ||||||
|  |     // means any target is allowed, and the default is native. Other options | ||||||
|  |     // for restricting supported target set are available. | ||||||
|  |     const target = b.standardTargetOptions(.{}); | ||||||
|  | 
 | ||||||
|  |     // Standard optimization options allow the person running `zig build` to select | ||||||
|  |     // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not | ||||||
|  |     // set a preferred release mode, allowing the user to decide how to optimize. | ||||||
|  |     const optimize = b.standardOptimizeOption(.{}); | ||||||
|  | 
 | ||||||
|  |     const exe = b.addExecutable(.{ | ||||||
|  |         .name = "datastructures", | ||||||
|  |         // In this case the main source file is merely a path, however, in more | ||||||
|  |         // complicated build scripts, this could be a generated file. | ||||||
|  |         .root_source_file = .{ .path = "src/main.zig" }, | ||||||
|  |         .target = target, | ||||||
|  |         .optimize = optimize, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     exe.linkLibC(); | ||||||
|  | 
 | ||||||
|  |     // This declares intent for the executable to be installed into the | ||||||
|  |     // standard location when the user invokes the "install" step (the default | ||||||
|  |     // step when running `zig build`). | ||||||
|  |     exe.install(); | ||||||
|  | 
 | ||||||
|  |     // This *creates* a RunStep in the build graph, to be executed when another | ||||||
|  |     // step is evaluated that depends on it. The next line below will establish | ||||||
|  |     // such a dependency. | ||||||
|  |     const run_cmd = exe.run(); | ||||||
|  | 
 | ||||||
|  |     // By making the run step depend on the install step, it will be run from the | ||||||
|  |     // installation directory rather than directly from within the cache directory. | ||||||
|  |     // This is not necessary, however, if the application depends on other installed | ||||||
|  |     // files, this ensures they will be present and in the expected location. | ||||||
|  |     run_cmd.step.dependOn(b.getInstallStep()); | ||||||
|  | 
 | ||||||
|  |     // This allows the user to pass arguments to the application in the build | ||||||
|  |     // command itself, like this: `zig build run -- arg1 arg2 etc` | ||||||
|  |     if (b.args) |args| { | ||||||
|  |         run_cmd.addArgs(args); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // This creates a build step. It will be visible in the `zig build --help` menu, | ||||||
|  |     // and can be selected like this: `zig build run` | ||||||
|  |     // This will evaluate the `run` step rather than the default, which is "install". | ||||||
|  |     const run_step = b.step("run", "Run the app"); | ||||||
|  |     run_step.dependOn(&run_cmd.step); | ||||||
|  | 
 | ||||||
|  |     // Creates a step for unit testing. | ||||||
|  |     const exe_tests = b.addTest(.{ | ||||||
|  |         .root_source_file = .{ .path = "src/main.zig" }, | ||||||
|  |         .target = target, | ||||||
|  |         .optimize = optimize, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     exe_tests.linkLibC(); | ||||||
|  | 
 | ||||||
|  |     // Similar to creating the run step earlier, this exposes a `test` step to | ||||||
|  |     // the `zig build --help` menu, providing a way for the user to request | ||||||
|  |     // running the unit tests. | ||||||
|  |     const test_step = b.step("test", "Run unit tests"); | ||||||
|  |     test_step.dependOn(&exe_tests.step); | ||||||
|  | } | ||||||
							
								
								
									
										306
									
								
								src/main.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								src/main.zig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,306 @@ | ||||||
|  | const std = @import("std"); | ||||||
|  | 
 | ||||||
|  | const BTree = struct { | ||||||
|  |     const Self = @This(); | ||||||
|  | 
 | ||||||
|  |     const B: usize = 3; | ||||||
|  |     const CAPACITY: usize = 2 * B - 1; | ||||||
|  |     const NUM_EDGES: usize = 2 * B; | ||||||
|  | 
 | ||||||
|  |     ally: std.mem.Allocator = std.heap.c_allocator, | ||||||
|  |     root: ?NodeOrLeaf, | ||||||
|  | 
 | ||||||
|  |     fn create(ally: std.mem.Allocator) Self { | ||||||
|  |         return Self{ | ||||||
|  |             .ally = ally, | ||||||
|  |             .root = null, | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn insert(self: *Self, value: u32) !void { | ||||||
|  |         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); | ||||||
|  |                 }, | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             var leaf: *Leaf = try self.ally.create(Leaf); | ||||||
|  |             errdefer self.ally.destroy(leaf); | ||||||
|  |             leaf.init(self.ally); | ||||||
|  |             try leaf.insert_value(value); | ||||||
|  |             self.root = NodeOrLeaf{ .leaf = leaf }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn destroy(self: *Self) void { | ||||||
|  |         if (self.root) |*root| { | ||||||
|  |             root.destroy(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const NodeOrLeafTag = enum { | ||||||
|  |         internal, | ||||||
|  |         leaf, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const NodeOrLeaf = union(NodeOrLeafTag) { | ||||||
|  |         internal: *Node, | ||||||
|  |         leaf: *Leaf, | ||||||
|  | 
 | ||||||
|  |         fn destroy(self: *NodeOrLeaf) void { | ||||||
|  |             self.as_leaf().destroy(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn as_leaf(self: *NodeOrLeaf) *Leaf { | ||||||
|  |             switch (self.*) { | ||||||
|  |                 .internal => |node| { | ||||||
|  |                     return node.as_leaf(); | ||||||
|  |                 }, | ||||||
|  |                 .leaf => |leaf| { | ||||||
|  |                     return leaf; | ||||||
|  |                 }, | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn from_leaf(leaf: *Leaf) NodeOrLeaf { | ||||||
|  |             if (leaf.level == 0) { | ||||||
|  |                 return .{ .leaf = leaf }; | ||||||
|  |             } else { | ||||||
|  |                 return .{ .node = @ptrCast(Node, leaf) }; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const Node = struct { | ||||||
|  |         leaf: Leaf, | ||||||
|  | 
 | ||||||
|  |         edges: [NUM_EDGES]?NodeOrLeaf = undefined, | ||||||
|  | 
 | ||||||
|  |         fn create(ally: std.mem.Allocator) !Leaf { | ||||||
|  |             var node = try ally.create(Node); | ||||||
|  |             node.init(ally); | ||||||
|  | 
 | ||||||
|  |             return node; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn init(self: *Leaf, ally: std.mem.Allocator) void { | ||||||
|  |             self.* = Node{ .leaf = Leaf{ .ally = ally } }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn as_leaf(self: *Node) *Leaf { | ||||||
|  |             return &self.leaf; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn insert_node(self: *Node, child: NodeOrLeaf) void { | ||||||
|  |             const self_leaf = self.as_leaf(); | ||||||
|  |             const ls = child.as_leaf().get_values()[0]; | ||||||
|  | 
 | ||||||
|  |             var idx: u16 = 0; | ||||||
|  |             for (self_leaf.get_values(), 0..) |v, i| { | ||||||
|  |                 idx = @intCast(u16, i); | ||||||
|  |                 if (v > ls) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (self.get_edges()[idx]) |edge| { | ||||||
|  |                 std.debug.print("edge already present?: {?}", .{edge}); | ||||||
|  |             } else { | ||||||
|  |                 child.as_leaf().parent = .{ .parent = self, .idx = idx }; | ||||||
|  |                 self.get_edges()[idx] = child; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn get_edges(self: *Node) []?NodeOrLeaf { | ||||||
|  |             const len = self.leaf.len; | ||||||
|  |             return self.edges[0..len]; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const ParentPtr = struct { | ||||||
|  |         parent: *Node, | ||||||
|  |         idx: u16, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const Leaf = struct { | ||||||
|  |         ally: std.mem.Allocator, | ||||||
|  |         level: usize = 0, | ||||||
|  |         parent: ?ParentPtr = null, | ||||||
|  | 
 | ||||||
|  |         len: u16 = 0, | ||||||
|  |         values: [CAPACITY]u32 = undefined, | ||||||
|  | 
 | ||||||
|  |         fn create(ally: std.mem.Allocator) !*Leaf { | ||||||
|  |             var leaf = try ally.create(Leaf); | ||||||
|  |             leaf.init(ally); | ||||||
|  | 
 | ||||||
|  |             return leaf; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn init(self: *Leaf, ally: std.mem.Allocator) void { | ||||||
|  |             self.* = Leaf{ .ally = ally }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn destroy(self: *Leaf) void { | ||||||
|  |             self.ally.destroy(self); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn push_value(self: *Leaf, value: u32) void { | ||||||
|  |             std.debug.assert(self.len < CAPACITY); | ||||||
|  |             var tmp = value; | ||||||
|  |             for (self.get_values()) |*val| { | ||||||
|  |                 if (val.* < value) { | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 const t = val.*; | ||||||
|  |                 val.* = tmp; | ||||||
|  |                 tmp = t; | ||||||
|  |             } | ||||||
|  |             self.values[self.len] = tmp; | ||||||
|  |             self.len = self.len + 1; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const SplitResult = struct { | ||||||
|  |             // attached | ||||||
|  |             left: *Leaf, | ||||||
|  |             // lose value, previously attacked, must be inserted | ||||||
|  |             middle: u32, | ||||||
|  |             // free floating leaf, must be attached | ||||||
|  |             right: *Leaf, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         fn split_at(self: *Leaf, value: u32) !SplitResult { | ||||||
|  |             var idx: u16 = 0; | ||||||
|  |             for (self.get_values(), 0..) |v, i| { | ||||||
|  |                 idx = @intCast(u16, i); | ||||||
|  |                 if (v > value) { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             std.debug.assert(idx > 0 and idx < CAPACITY - 1); | ||||||
|  | 
 | ||||||
|  |             var new = try Leaf.create(self.ally); | ||||||
|  |             new.level = self.level; | ||||||
|  |             var middle: u32 = undefined; | ||||||
|  | 
 | ||||||
|  |             // take from right half | ||||||
|  |             if (idx > B) { | ||||||
|  |                 new.len = self.len - (idx + 1); | ||||||
|  |                 std.mem.copy(u32, &new.values, self.values[(idx + 1)..self.len]); | ||||||
|  | 
 | ||||||
|  |                 middle = self.values[idx]; | ||||||
|  | 
 | ||||||
|  |                 self.len = idx; | ||||||
|  |                 self.push_value(value); | ||||||
|  |             } else { | ||||||
|  |                 // take from left half | ||||||
|  |                 new.len = self.len - (idx); | ||||||
|  |                 std.mem.copy(u32, &new.values, self.values[idx..self.len]); | ||||||
|  | 
 | ||||||
|  |                 new.push_value(value); | ||||||
|  | 
 | ||||||
|  |                 middle = self.values[idx - 1]; | ||||||
|  | 
 | ||||||
|  |                 self.len = idx - 1; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return .{ .left = self, .middle = middle, .right = new }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn insert_value(self: *Leaf, value: u32) !void { | ||||||
|  |             if (self.len < CAPACITY) { | ||||||
|  |                 self.push_value(value); | ||||||
|  |             } else { | ||||||
|  |                 return error.LeafAtCapacity; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fn get_values(self: *Leaf) []u32 { | ||||||
|  |             const len = self.len; | ||||||
|  |             return self.values[0..len]; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pub fn main() !void { | ||||||
|  |     // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) | ||||||
|  |     std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); | ||||||
|  | 
 | ||||||
|  |     // stdout is for the actual output of your application, for example if you | ||||||
|  |     // are implementing gzip, then only the compressed bytes should be sent to | ||||||
|  |     // stdout, not any debugging messages. | ||||||
|  |     const stdout_file = std.io.getStdOut().writer(); | ||||||
|  |     var bw = std.io.bufferedWriter(stdout_file); | ||||||
|  |     const stdout = bw.writer(); | ||||||
|  | 
 | ||||||
|  |     try stdout.print("Run `zig build test` to run the tests.\n", .{}); | ||||||
|  | 
 | ||||||
|  |     try bw.flush(); // don't forget to flush! | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "btree leaf" { | ||||||
|  |     std.testing.refAllDeclsRecursive(BTree); | ||||||
|  |     std.testing.refAllDeclsRecursive(BTree.Leaf); | ||||||
|  | 
 | ||||||
|  |     var leaf = BTree.Leaf{ .ally = std.testing.allocator, .parent = null, .len = 2, .values = [_]u32{ 5, 6, undefined, undefined, undefined } }; | ||||||
|  |     const values = leaf.get_values(); | ||||||
|  | 
 | ||||||
|  |     std.debug.print("{?}\n", .{leaf}); | ||||||
|  |     std.debug.print("{any}\n", .{values}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn printValues(leaf: *BTree.Leaf) void { | ||||||
|  |     const values = leaf.get_values(); | ||||||
|  |     std.debug.print("{any}\n", .{values}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "leaf split" { | ||||||
|  |     std.debug.print("testing splitting\n", .{}); | ||||||
|  | 
 | ||||||
|  |     var tree = BTree.create(std.testing.allocator); | ||||||
|  |     defer tree.destroy(); | ||||||
|  |     try tree.insert(2); | ||||||
|  |     try tree.insert(4); | ||||||
|  |     try tree.insert(6); | ||||||
|  |     try tree.insert(3); | ||||||
|  |     try tree.insert(7); | ||||||
|  |     std.debug.print("before split:", .{}); | ||||||
|  |     printValues(tree.root.?.as_leaf()); | ||||||
|  | 
 | ||||||
|  |     const split = try tree.root.?.as_leaf().split_at(5); | ||||||
|  | 
 | ||||||
|  |     std.debug.print("after split:", .{}); | ||||||
|  |     printValues(tree.root.?.as_leaf()); | ||||||
|  | 
 | ||||||
|  |     std.debug.print("split: {?}\n", .{split}); | ||||||
|  |     tree.ally.destroy(split.right); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "btree new" { | ||||||
|  |     std.debug.print("testing insertion\n", .{}); | ||||||
|  |     var tree = BTree.create(std.testing.allocator); | ||||||
|  |     defer tree.destroy(); | ||||||
|  |     try tree.insert(5); | ||||||
|  |     printValues(tree.root.?.as_leaf()); | ||||||
|  |     try tree.insert(4); | ||||||
|  |     printValues(tree.root.?.as_leaf()); | ||||||
|  |     try tree.insert(6); | ||||||
|  |     printValues(tree.root.?.as_leaf()); | ||||||
|  |     try tree.insert(3); | ||||||
|  |     printValues(tree.root.?.as_leaf()); | ||||||
|  |     try tree.insert(7); | ||||||
|  |     printValues(tree.root.?.as_leaf()); | ||||||
|  |     //try tree.insert(8); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "simple test" { | ||||||
|  |     var list = std.ArrayList(i32).init(std.testing.allocator); | ||||||
|  |     defer list.deinit(); // try commenting this out and see if zig detects the memory leak! | ||||||
|  |     try list.append(42); | ||||||
|  |     try std.testing.expectEqual(@as(i32, 42), list.pop()); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue