lib/accy/src/axis/roles.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const Failure = enum {
4 out_of_range,
5 duplicate,
6 };
7
8 fn appearsInPriorGroup(
9 groups: []const []const i64,
10 group_index: usize,
11 axis: i64,
12 ) bool {
13 for (groups[0..group_index]) |prior| {
14 for (prior) |seen| {
15 if (seen == axis) return true;
16 }
17 }
18 return false;
19 }
20
21 pub fn check(
22 rank: usize,
23 groups: []const []const i64,
24 ) ?Failure {
25 for (groups, 0..) |group, group_index| {
26 for (group, 0..) |axis, axis_index| {
27 const index = std.math.cast(usize, axis) orelse
28 return .out_of_range;
29 if (index >= rank) return .out_of_range;
30 if (appearsInPriorGroup(groups, group_index, axis)) {
31 return .duplicate;
32 }
33 for (group[0..axis_index]) |seen| {
34 if (seen == axis) return .duplicate;
35 }
36 }
37 }
38 return null;
39 }
40
41 pub fn groupsOverlap(groups: []const []const i64) bool {
42 for (groups, 0..) |group, group_index| {
43 for (group) |axis| {
44 if (appearsInPriorGroup(groups, group_index, axis)) return true;
45 }
46 }
47 return false;
48 }
49
50 test "axis role groups form a partial permutation" {
51 try std.testing.expect(check(4, &.{ &.{ 2, 0 }, &.{3} }) == null);
52 try std.testing.expectEqual(
53 Failure.out_of_range,
54 check(2, &.{&.{-1}}).?,
55 );
56 try std.testing.expectEqual(
57 Failure.out_of_range,
58 check(2, &.{&.{2}}).?,
59 );
60 try std.testing.expectEqual(
61 Failure.duplicate,
62 check(2, &.{&.{ 0, 0 }}).?,
63 );
64 try std.testing.expectEqual(
65 Failure.duplicate,
66 check(2, &.{ &.{0}, &.{0} }).?,
67 );
68 try std.testing.expect(!groupsOverlap(&.{ &.{ 2, 0 }, &.{3} }));
69 try std.testing.expect(groupsOverlap(&.{ &.{0}, &.{0} }));
70 }