lib/machine/src/explore/input.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const explore = @import("root.zig");
2 const profile = @import("../profile/root.zig");
3 const std = @import("std");
4
5 const alternative_limit: u8 = 5;
6
7 pub fn Generator(comptime capacity_value: explore.InputCapacity) type {
8 return struct {
9 seed: explore.Seed,
10 plan: explore.TemporalPlan,
11 sequence: u16 = 0,
12 virtual_time_tick: u64,
13 phase: explore.InputPhase = .quiet,
14 burst_remaining: u8 = 0,
15
16 const Self = @This();
17
18 pub const capacity: explore.InputCapacity = capacity_value;
19 pub const Error: type = explore.GeneratorError;
20 pub const InitResult: type = explore.Generation(Self);
21 pub const Site: type = explore.ChoiceSite(
22 explore.InputAlternative,
23 capacity.alternatives,
24 );
25 pub const NextResult: type = explore.Generation(Site);
26
27 pub fn init(
28 selected: profile.Profile,
29 seed: explore.Seed,
30 plan: explore.TemporalPlan,
31 ) Error!InitResult {
32 try profile.validate(selected);
33 if (plan.end_tick < plan.start_tick) return error.InvalidPlan;
34 if (plan.steps == 0 or plan.start_tick == plan.end_tick) {
35 return .exhausted;
36 }
37 if (plan.steps > capacity.steps) {
38 return .{ .incomplete = .step_capacity };
39 }
40 if (capacity.alternatives < alternative_limit) {
41 return .{ .incomplete = .alternative_capacity };
42 }
43 return .{ .item = .{
44 .seed = seed,
45 .plan = plan,
46 .virtual_time_tick = plan.start_tick,
47 } };
48 }
49
50 pub fn next(self: *const Self) NextResult {
51 self.assertValid();
52 if (self.sequence == self.plan.steps or
53 self.virtual_time_tick == self.plan.end_tick)
54 {
55 return .exhausted;
56 }
57 var site = Site{
58 .id = self.siteId(),
59 .alternatives = undefined,
60 .count = 0,
61 .suggested = 0,
62 };
63 switch (self.phase) {
64 .quiet => self.quietAlternatives(&site),
65 .burst => self.burstAlternatives(&site),
66 }
67 std.debug.assert(site.count > 0);
68 site.suggested = self.seed.suggest(.input, self.sequence, site.count);
69 return .{ .item = site };
70 }
71
72 pub fn choose(
73 self: *Self,
74 site: Site,
75 index: u8,
76 ) Error!explore.GeneratedInput {
77 const current = switch (self.next()) {
78 .item => |value| value,
79 .exhausted, .incomplete => return error.StaleChoiceSite,
80 };
81 if (!sameSite(current, site)) return error.StaleChoiceSite;
82 if (index >= site.count) return error.ChoiceOutOfRange;
83 const selected = site.alternatives[index];
84 self.advance(selected);
85 self.assertValid();
86 return selected;
87 }
88
89 pub fn workload(self: *const Self) explore.InputPhase {
90 self.assertValid();
91 return self.phase;
92 }
93
94 fn quietAlternatives(self: *const Self, site: *Site) void {
95 self.addAt(site, self.longDelay(), .{ .wait = self.longDelay() });
96 self.addAt(site, 1, .{ .terminal = self.seed.word(.input, self.sequence, 1) });
97 self.addAt(site, 1, .{ .entropy = self.seed.word(.input, self.sequence, 2) });
98 self.addAt(site, 1, .{ .service_result = .failed });
99 self.addAt(site, 1, .{ .effect_result = .uncertain });
100 }
101
102 fn burstAlternatives(self: *const Self, site: *Site) void {
103 self.addAt(site, 1, .{ .terminal = self.seed.word(.input, self.sequence, 3) });
104 self.addAt(site, 1, .{ .packet = self.seed.word(.input, self.sequence, 4) });
105 self.addAt(site, 1, .{ .entropy = self.seed.word(.input, self.sequence, 5) });
106 self.addAt(site, 1, .{ .service_result = .succeeded });
107 self.addAt(site, self.longDelay(), .{ .wait = self.longDelay() });
108 }
109
110 fn addAt(self: *const Self, site: *Site, delta: u64, value: explore.InputValue) void {
111 const tick = self.futureTick(delta) orelse return;
112 std.debug.assert(site.count < site.alternatives.len);
113 site.alternatives[site.count] = .{
114 .origin = explore.origin(inputSource(value)),
115 .virtual_time_tick = tick,
116 .value = value,
117 };
118 site.count += 1;
119 }
120
121 fn advance(self: *Self, selected: explore.GeneratedInput) void {
122 std.debug.assert(selected.virtual_time_tick > self.virtual_time_tick);
123 std.debug.assert(selected.virtual_time_tick <= self.plan.end_tick);
124 self.virtual_time_tick = selected.virtual_time_tick;
125 self.sequence += 1;
126 switch (selected.value) {
127 .wait => {
128 self.phase = .quiet;
129 self.burst_remaining = 0;
130 },
131 .terminal, .entropy, .packet, .service_result, .effect_result => {
132 self.advanceBurst();
133 },
134 }
135 }
136
137 fn advanceBurst(self: *Self) void {
138 switch (self.phase) {
139 .quiet => {
140 const width = self.seed.word(.input, self.sequence, 6) % 3;
141 self.phase = .burst;
142 self.burst_remaining = 2 + @as(u8, @intCast(width));
143 },
144 .burst => {
145 std.debug.assert(self.burst_remaining > 0);
146 self.burst_remaining -= 1;
147 if (self.burst_remaining == 0) self.phase = .quiet;
148 },
149 }
150 }
151
152 fn futureTick(self: *const Self, delta: u64) ?u64 {
153 std.debug.assert(self.virtual_time_tick <= self.plan.end_tick);
154 const remaining = self.plan.end_tick - self.virtual_time_tick;
155 if (delta == 0 or delta > remaining) return null;
156 return self.virtual_time_tick + delta;
157 }
158
159 fn longDelay(self: *const Self) u64 {
160 return 4 + self.seed.word(.input, self.sequence, 7) % 13;
161 }
162
163 fn siteId(self: *const Self) explore.SiteId {
164 return .{
165 .stream = .input,
166 .sequence = self.sequence,
167 .virtual_time_tick = self.virtual_time_tick,
168 };
169 }
170
171 fn assertValid(self: *const Self) void {
172 std.debug.assert(self.sequence <= self.plan.steps);
173 std.debug.assert(self.sequence <= capacity.steps);
174 std.debug.assert(self.virtual_time_tick >= self.plan.start_tick);
175 std.debug.assert(self.virtual_time_tick <= self.plan.end_tick);
176 if (self.phase == .quiet) std.debug.assert(self.burst_remaining == 0);
177 }
178
179 fn sameSite(expected: Site, actual: Site) bool {
180 if (!std.meta.eql(expected.id, actual.id)) return false;
181 if (expected.count != actual.count) return false;
182 if (expected.suggested != actual.suggested) return false;
183 for (expected.values(), actual.values()) |left, right| {
184 if (!std.meta.eql(left, right)) return false;
185 }
186 return true;
187 }
188 };
189 }
190
191 fn inputSource(value: explore.InputValue) profile.DeterminismSource {
192 return switch (value) {
193 .wait => .virtual_time,
194 .terminal => .terminal_input,
195 .entropy => .entropy_input,
196 .packet => .packet_payload,
197 .service_result => .host_service_result,
198 .effect_result => .effect_result,
199 };
200 }