const std = @import("std");
const meta = @import("meta.zig");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
pub fn Node(comptime K: type, comptime V: type, comptime B: u32) type {
return struct {
const Self = @This();
keys: [2 * B - 1]K,
values: [2 * B - 1]V,
len: usize,
edges: [2 * B]?*Self,
// Return Type for Node's search method.
pub const SearchResult = struct {
found: bool,
index: usize,
};
pub const KV = struct {
key: K,
value: V,
};
const KVE = struct {
key: K,
value: V,
edge: ?*Self,
};
const Entry = struct {
key_ptr: *K,
value_ptr: *V,
};
pub fn createEmpty(allocator: Allocator) !*Self {
var out = try allocator.create(Self);
out.* = Self{
.keys = [_]K{undefined} ** (2 * B - 1),
.values = [_]V{undefined} ** (2 * B - 1),
.len = 0,
.edges = [_]?*Self{null} ** (2 * B),
};
return out;
}
pub fn createFromKV(allocator: Allocator, key: K, value: V) !*Self {
var out = try Self.createEmpty(allocator);
out.keys[0] = key;
out.values[0] = value;
out.len = 1;
return out;
}
/// Searches the node for a key. Returns a struct with two fields:
/// 1) .found: bool -> Wether the key was found or not.
/// 2) .index: usize -> The index of the found key or, if no key was found,
/// the index of the edge where the search path continues.
pub fn search(self: Self, key: K) SearchResult {
var i: usize = 0;
while (i < self.len) : (i += 1) {
if (meta.eq(key, self.keys[i])) {
return SearchResult{
.found = true,
.index = i,
};
} else if (meta.lt(key, self.keys[i])) {
return .{
.found = false,
.index = i,
};
}
}
return .{
.found = false,
.index = self.len,
};
}
/// Insert KVE if node has room. Return null in this case.
/// If node is full, take out the median KV and split off the the right part
/// into a new node and return this new node together with the median KV.
pub fn insertOrSplit(
self: *Self,
allocator: Allocator,
index: usize,
key: K,
value: V,
edge: ?*Self,
) !?KVE {
if (self.isFull()) {
// Node is full. Split Node.
var split_result = try self.split(allocator);
if (index < B) {
// Insert KVE in original node.
self.insert(index, key, value, edge);
} else {
// Insert KVE in the split off node.
split_result.edge.?.insert(index - B, key, value, edge);
}
return split_result;
} else {
// No split necessary.
self.insert(index, key, value, edge);
return null;
}
}
/// Swap value at index.
pub fn swapValue(self: *Self, index: usize, value: V) V {
const out = self.values[index];
self.values[index] = value;
return out;
}
/// Swap KV at index.
pub fn swapKV(self: *Self, index: usize, key: K, value: V) KV {
const out = KV{
.key = self.keys[index],
.value = self.values[index],
};
self.values[index] = value;
self.keys[index] = key;
return out;
}
/// Remove and return KVE at index.
/// The removed edge is right of the KV.
pub fn remove(self: *Self, index: usize) KVE {
const out = KVE{
.key = self.keys[index],
.value = self.values[index],
.edge = self.edges[index + 1],
};
std.mem.copy(
K,
self.keys[index..],
self.keys[index + 1 .. self.len],
);
std.mem.copy(
V,
self.values[index..],
self.values[index + 1 .. self.len],
);
self.keys[self.len - 1] = undefined;
self.values[self.len - 1] = undefined;
if (!self.isLeaf()) {
std.mem.copy(
?*Self,
self.edges[index + 1 ..],
self.edges[index + 2 .. self.len + 1],
);
self.edges[self.len] = null;
}
self.len -= 1;
return out;
}
/// Remove and return most right KVE.
fn removeFromEnd(self: *Self) KVE {
return self.remove(self.len - 1);
}
/// Remove and return most left KV and Edge.
/// Contrary to the methods above, this removes the edge left of the KV.
fn removeFromBeginning(self: *Self) KVE {
const out = KVE{
.key = self.keys[0],
.value = self.values[0],
.edge = self.edges[0],
};
std.mem.copy(
K,
self.keys[0..],
self.keys[1..self.len],
);
std.mem.copy(
V,
self.values[0..],
self.values[1..self.len],
);
self.keys[self.len - 1] = undefined;
self.values[self.len - 1] = undefined;
if (!self.isLeaf()) {
std.mem.copy(
?*Self,
self.edges[0..],
self.edges[1 .. self.len + 1],
);
self.edges[self.len] = null;
}
self.len -= 1;
return out;
}
// Shifts the arrays right after index and inserts new KVE.
// The new edge is right of the new KV.
// Does not check if insertion is at the correct position/node has space.
fn insert(self: *Self, index: usize, key: K, value: V, edge: ?*Self) void {
std.mem.copyBackwards(
K,
self.keys[index + 1 .. self.len + 1],
self.keys[index..self.len],
);
self.keys[index] = key;
std.mem.copyBackwards(
V,
self.values[index + 1 .. self.len + 1],
self.values[index..self.len],
);
self.values[index] = value;
if (!self.isLeaf()) {
std.mem.copyBackwards(
?*Self,
self.edges[index + 2 .. self.len + 2],
self.edges[index + 1 .. self.len + 1],
);
self.edges[index + 1] = edge;
}
self.len += 1;
}
/// Does not check if insertion is at the correct position/node has space.
fn insertAtEnd(self: *Self, key: K, value: V, edge: ?*Self) void {
self.keys[self.len] = key;
self.values[self.len] = value;
self.edges[self.len + 1] = edge;
self.len += 1;
}
/// This is different from the other inserts methods because it inserts the edge
/// left of the KV. I.e. it also puts the edge in the first position.
/// Does not check if insertion is at the correct position/node has space.
fn insertAtBeginning(self: *Self, key: K, value: V, edge: ?*Self) void {
std.mem.copyBackwards(
K,
self.keys[1 .. self.len + 1],
self.keys[0..self.len],
);
self.keys[0] = key;
std.mem.copyBackwards(
V,
self.values[1 .. self.len + 1],
self.values[0..self.len],
);
self.values[0] = value;
if (!self.isLeaf()) {
std.mem.copyBackwards(
?*Self,
self.edges[1 .. self.len + 2],
self.edges[0 .. self.len + 1],
);
self.edges[0] = edge;
}
self.len += 1;
}
/// The borrowing methods happen from the perspective of the parent.
/// This means, the parent distributes from one edge to another.
/// The edge at `index` is underflowed and needs compensation.
/// Try to borrow from the edge at one side of `index` and rotate.
/// Returns true on success, else false.
pub fn borrowFromRight(self: *Self, index: usize) bool {
// No edge right of index.
if (index == self.len) return false;
var giver = self.edges[index + 1].?;
if (giver.len > B - 1) {
// Right edge can spare one.
var taker = self.edges[index].?;
const from_giver: KVE = giver.removeFromBeginning();
taker.insertAtEnd(self.keys[index], self.values[index], from_giver.edge);
_ = self.swapKV(index, from_giver.key, from_giver.value);
return true;
} else return false;
}
/// The borrowing methods happen from the perspective of the parent.
/// This means, the parent distributes from one edge to another.
/// The edge at `index` is underflowed and needs compensation.
/// Try to borrow from the edge at one side of `index` and rotate.
/// Returns true on success, else false.
pub fn borrowFromLeft(self: *Self, index: usize) bool {
// No edge left of index.
if (index == 0) return false;
var giver = self.edges[index - 1].?;
if (giver.len > B - 1) {
// Right edge can spare one.
var taker = self.edges[index].?;
const from_giver: KVE = giver.removeFromEnd();
taker.insertAtBeginning(self.keys[index - 1], self.values[index - 1], from_giver.edge);
_ = self.swapKV(index - 1, from_giver.key, from_giver.value);
return true;
} else return false;
}
/// Merging happend from the perspective of the parent.
/// It merges two edges together and puts the middle KV of the parent in between.
/// The right node is merged into the left and the right is destroyed afterwards.
pub fn mergeEdges(self: *Self, allocator: Allocator, left_edge_index: usize) void {
var left = self.edges[left_edge_index].?;
const removed = self.remove(left_edge_index);
left.insertAtEnd(removed.key, removed.value, null);
std.mem.copyBackwards(
K,
left.keys[left.len..],
removed.edge.?.keys[0..removed.edge.?.len],
);
std.mem.copyBackwards(
V,
left.values[left.len..],
removed.edge.?.values[0..removed.edge.?.len],
);
std.mem.copyBackwards(
?*Self,
left.edges[left.len..],
removed.edge.?.edges[0 .. removed.edge.?.len + 1],
);
left.len += removed.edge.?.len;
allocator.destroy(removed.edge.?);
}
/// Split operation for a full node.
/// Returns a struct with three fields:
/// 1) and 2) -> Key and value of the median.
/// 3) -> The right part of the median as a new node (pointer).
/// These parts are erased from the original node.
fn split(self: *Self, allocator: Allocator) !KVE {
const median: usize = B - 1;
var new_key = self.keys[median];
var new_value = self.values[median];
var new_node = try Self.createFromSlices(
allocator,
self.keys[median + 1 .. self.len],
self.values[median + 1 .. self.len],
self.edges[median + 1 .. self.len + 1],
);
// shrink original node
std.mem.set(K, self.keys[median..], undefined);
std.mem.set(V, self.values[median..], undefined);
std.mem.set(?*Self, self.edges[median + 1 ..], null);
self.len = median;
return KVE{
.key = new_key,
.value = new_value,
.edge = new_node,
};
}
fn createFromSlices(allocator: Allocator, keys: []K, values: []V, edges: []?*Self) !*Self {
var out = try Self.createEmpty(allocator);
std.mem.copyBackwards(K, out.keys[0..], keys);
std.mem.copyBackwards(V, out.values[0..], values);
std.mem.copyBackwards(?*Self, out.edges[0..], edges);
out.len = keys.len;
return out;
}
pub fn isLeaf(self: Self) bool {
return self.edges[0] == null;
}
pub fn isFull(self: Self) bool {
return self.len == 2 * B - 1;
}
pub fn isLacking(self: Self) bool {
return self.len < B - 1;
}
/// This is intended for testing and debugging.
fn assertValidityExceptLength(self: *const Self) void {
// Keys increasing
for (self.keys[0 .. self.len - 1]) |_, i| {
assert(meta.lt(self.keys[i], self.keys[i + 1]));
}
// Number of edges
var count: u32 = 0;
var encountered_null = false;
for (self.edges) |edge| {
if (edge) |_| {
assert(encountered_null == false);
count += 1;
} else {
encountered_null = true;
}
}
assert(count == self.len + 1 or count == 0);
// If node is leaf we are done here.
if (self.isLeaf()) return;
// Edges left smaller and right larger
for (self.keys[0..self.len]) |key, i| {
const left_edge = self.edges[i].?;
const imm_left_key = left_edge.keys[left_edge.len - 1];
assert(meta.gt(key, imm_left_key));
const right_edge = self.edges[i + 1].?;
const imm_right_key = right_edge.keys[0];
assert(meta.lt(key, imm_right_key));
}
}
pub fn assertValidity(self: *const Self) void {
// Length
assert(self.len <= 2 * B - 1);
assert(self.len >= B - 1);
self.assertValidityExceptLength();
}
pub fn assertValidityRoot(self: *const Self) void {
// Length
assert(self.len <= 2 * B - 1);
self.assertValidityExceptLength();
}
};
}