creates new project

This commit is contained in:
2025-03-30 02:22:02 +09:00
parent 1b89a05bbf
commit 0bfa0f1098
7 changed files with 434 additions and 0 deletions

249
src/hncore/Digraph.zig Normal file
View File

@@ -0,0 +1,249 @@
const std = @import("std");
/// A data type to store relations of nodes in directional-graph.
pub fn Digraph(comptime T: type) type {
return struct {
const Node = T;
const Conn = struct { from: T, to: T, };
const ConnList = std.ArrayList(Conn);
const Error = error {
AlreadyConnected,
NotConnected,
};
map: ConnList,
///
pub fn init(alloc: std.mem.Allocator, map_unsorted: []const Conn) !@This() {
var map_sorted = ConnList.init(alloc);
try map_sorted.ensureTotalCapacity(map_unsorted.len);
for (map_unsorted) |conn| {
try map_sorted.append(conn);
}
std.mem.sort(Conn, map_sorted.items, {}, compare_conn);
return .{
.map = map_sorted,
};
}
///
pub fn deinit(self: *@This()) void {
self.map.deinit();
}
///
pub fn connect_if(self: *@This(), from: T, to: T) !bool {
const begin, const end = self.find_segment(from);
if (self.find_connection_in_segment(from, to, begin, end)) |_| {
return false;
} else {
try self.map.insert(end, Conn { .from = from, .to = to, });
return true;
}
}
/// Same to `connect_if`, but returns an error if it's already connected.
pub fn connect(self: *@This(), from: T, to: T) !void {
if (!try self.connect_if(from, to)) {
return Error.AlreadyConnected;
}
}
///
pub fn disconnect_if(self: *@This(), from: T, to: T) bool {
const begin, const end = self.find_segment(from);
if (self.find_connection_in_segment(from, to, begin, end)) |idx| {
_ = self.map.orderedRemove(idx);
return true;
} else {
return false;
}
}
/// Same to `disconnect_if`, but returns an error if it's not connected.
pub fn disconnect(self: *@This(), from: T, to: T) !void {
if (!self.disconnect_if(from, to)) {
return Error.NotConnected;
}
}
///
pub fn is_connected(self: *const @This(), from: T, to: T) bool {
const begin, const end = self.find_segment(from);
return self.find_connection_in_segment(from, to, begin, end) != null;
}
fn find_connection_in_segment(self: *const @This(), from: T, to: T, begin: usize, end: usize) ?usize {
for (self.map.items[begin..end], begin..) |v, idx| {
if ((v.from == from) and (v.to == to)) {
return idx;
}
}
return null;
}
fn find_segment(self: *const @This(), from: T) struct { usize, usize } {
const n = self.map.items.len;
if (n == 0) {
return .{ 0, 0, };
}
const base_idx = self.binsearch(from).?;
const base_from = self.map.items[base_idx].from;
var begin: usize = undefined;
var end : usize = undefined;
if (base_from < from) {
begin = base_idx;
while ((begin < n) and (self.map.items[begin].from != from)) { begin += 1; }
end = begin;
while ((end < n) and (self.map.items[end].from == from)) { end += 1; }
} else if (base_from > from) {
end = base_idx;
while ((end > 0) and (self.map.items[end-1].from != from)) { end -= 1; }
begin = end;
while ((begin > 0) and (self.map.items[begin-1].from == from)) { begin -= 1; }
} else {
begin = base_idx;
while ((begin > 0) and (self.map.items[begin-1].from == from)) { begin -= 1; }
end = base_idx;
while ((end < n) and (self.map.items[end].from == from)) { end += 1; }
}
return .{ begin, end, };
}
fn binsearch(self: *const @This(), from: T) ?usize {
if (self.map.items.len == 0) {
return null;
}
var left : usize = 0;
var right: usize = self.map.items.len;
var idx: usize = undefined;
while (left < right) {
idx = (left + right) / 2;
const target = self.map.items[idx].from;
if (target < from) {
left = idx + 1;
} else if (target > from) {
right = idx -| 1;
} else {
break;
}
}
return idx;
}
fn compare_conn(_: void, a: Conn, b: Conn) bool {
return a.from < b.from;
}
};
}
test "check if connected" {
const Sut = Digraph(u8);
const map = [_]Sut.Conn {
.{ .from = 3, .to = 0, },
.{ .from = 0, .to = 1, },
.{ .from = 1, .to = 3, },
};
var sut = try Sut.init(std.testing.allocator, map[0..]);
defer sut.deinit();
try std.testing.expect(sut.is_connected(0, 1));
try std.testing.expect(!sut.is_connected(1, 0));
try std.testing.expect(sut.is_connected(1, 3));
try std.testing.expect(!sut.is_connected(3, 1));
try std.testing.expect(sut.is_connected(3, 0));
try std.testing.expect(!sut.is_connected(0, 3));
try std.testing.expect(!sut.is_connected(0, 2));
try std.testing.expect(!sut.is_connected(2, 0));
try std.testing.expect(!sut.is_connected(1, 2));
try std.testing.expect(!sut.is_connected(2, 1));
}
test "make new connection" {
const Sut = Digraph(u8);
var sut = try Sut.init(std.testing.allocator, &.{});
defer sut.deinit();
try std.testing.expect(try sut.connect_if(2, 1));
try std.testing.expect(sut.is_connected(2, 1));
try std.testing.expect(!sut.is_connected(1, 2));
try sut.connect(3, 1);
try std.testing.expect(sut.is_connected(3, 1));
try std.testing.expect(!sut.is_connected(1, 3));
}
test "making an existing connection fails" {
const Sut = Digraph(u8);
const map = [_]Sut.Conn {
.{ .from = 0, .to = 1, },
};
var sut = try Sut.init(std.testing.allocator, map[0..]);
defer sut.deinit();
try std.testing.expect(!try sut.connect_if(0, 1));
try std.testing.expectError(Sut.Error.AlreadyConnected, sut.connect(0, 1));
}
test "disconnect an existing connection" {
const Sut = Digraph(u8);
const map = [_]Sut.Conn {
.{ .from = 0, .to = 1, },
.{ .from = 2, .to = 3, },
};
var sut = try Sut.init(std.testing.allocator, map[0..]);
defer sut.deinit();
try std.testing.expect(sut.disconnect_if(0, 1));
try std.testing.expect(!sut.is_connected(0, 1));
try sut.disconnect(2, 3);
try std.testing.expect(!sut.is_connected(2, 3));
}
test "disconnecting a missing connection fails" {
const Sut = Digraph(u8);
var sut = try Sut.init(std.testing.allocator, &.{});
defer sut.deinit();
try std.testing.expect(!sut.disconnect_if(0, 1));
try std.testing.expectError(Sut.Error.NotConnected, sut.disconnect(1, 0));
}
test "chaotic operation" {
const Sut = Digraph(u16);
var sut = try Sut.init(std.testing.allocator, &.{});
defer sut.deinit();
const N = 100;
for (0..N) |v| {
const x: Sut.Node = @intCast(v);
try sut.connect(x*%7, x*%13);
}
for (N/2..N) |v| {
const x: Sut.Node = @intCast(v);
try sut.disconnect(x*%7, x*%13);
}
for (0..N/2) |v| {
const x: Sut.Node = @intCast(v);
try std.testing.expect(sut.is_connected(x*%7, x*%13));
}
for (N/2..N) |v| {
const x: Sut.Node = @intCast(v);
try std.testing.expect(!sut.is_connected(x*%7, x*%13));
}
}

5
src/hncore/root.zig Normal file
View File

@@ -0,0 +1,5 @@
pub const Digraph = @import("./Digraph.zig").Digraph;
test {
@import("std").testing.refAllDecls(@This());
}