const std = @import("std"); pub fn main() !void { var input = Signal{ .digital = 1, .analog = 0.5 }; var battery1 = Battery{ .value = -0.5 }; var battery2 = Battery{ .value = 0.5 }; var battery3 = Battery{ .value = -1.0 }; var or1_inputs = [_]*Signal{ &input, &input, undefined }; var or1 = Or{ .inputs = &or1_inputs, .arithmetic_mode = true }; var or2_inputs = [_]*Signal{ &or1.output, &or1.output, undefined }; var or2 = Or{ .inputs = &or2_inputs, .arithmetic_mode = true }; var or3_inputs = [_]*Signal{ &input, &battery1.output }; var or3 = Or{ .inputs = &or3_inputs }; var or4_inputs = [_]*Signal{ &or1.output, &battery1.output }; var or4 = Or{ .inputs = &or4_inputs }; var or5_inputs = [_]*Signal{ &or2.output, &battery1.output }; var or5 = Or{ .inputs = &or5_inputs }; var or6_inputs = [_]*Signal{ &battery2.output, &or3.output }; var or6 = Or{ .inputs = &or6_inputs, .arithmetic_mode = true }; var and1_inputs = [_]*Signal{ &battery3.output, &or6.output }; var and1 = And{ .inputs = &and1_inputs, .arithmetic_mode = true }; var or7_inputs = [_]*Signal{ &battery2.output, &or4.output }; var or7 = Or{ .inputs = &or7_inputs, .arithmetic_mode = true }; var and2_inputs = [_]*Signal{ &battery3.output, &or7.output }; var and2 = And{ .inputs = &and2_inputs, .arithmetic_mode = true }; var or8_inputs = [_]*Signal{ &battery2.output, &or5.output }; var or8 = Or{ .inputs = &or8_inputs, .arithmetic_mode = true }; or1_inputs[2] = &and1.output; or2_inputs[2] = &and2.output; battery1.process(); battery2.process(); battery3.process(); or3.process(); or6.process(); and1.process(); or1.process(); or4.process(); or7.process(); and2.process(); or2.process(); or5.process(); or8.process(); std.debug.print("Input:\n{}\n\n", .{input}); std.debug.print("{}\n{}\n\n", .{ or1.output, or2.output }); std.debug.print("{}\n{}\n{}\n\n", .{ or3.output, or4.output, or5.output }); std.debug.print("{}\n{}\n{}\n{}\n{}\n\n", .{ or6.output, and1.output, or7.output, and2.output, or8.output }); } pub const Signal = struct { digital: i2 = 0, analog: f32 = 0.0, color: u24 = 0, pub fn format( self: Signal, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { _ = .{ fmt, options }; try writer.writeAll("Signal("); if (self.digital < 0) try writer.writeByte('-') else try writer.writeByte('+'); try writer.print("{d} / {d:0>1.4})", .{ @abs(self.digital), self.analog, }); } }; pub const Battery = struct { value: f32, output: Signal = .{}, pub fn process(self: *Battery) void { self.output.digital = @intFromFloat(std.math.sign(self.value)); self.output.analog = self.value; } }; pub const Not = struct { input: *Signal, output: Signal = .{}, invert_output: bool = true, pub fn process(self: *Not) void { if (self.invert_output) { self.output.digital = 1 - @as(i2, @intCast(@abs(self.input.digital))); self.output.analog = 1.0 - @abs(self.input.analog); } else { self.output.digital = self.input.digital; self.output.analog = self.input.analog; } self.output.analog = std.math.clamp(self.output.analog, -1.0, 1.0); } }; pub const And = struct { inputs: []*Signal, output: Signal = .{}, // if false, is in Minimum Input mode // if true, is in Multiply Inputs mode arithmetic_mode: bool = false, // TODO check implementation pub fn process(self: *And) void { if (self.arithmetic_mode) { self.output.digital = self.inputs[0].digital; self.output.analog = self.inputs[0].analog; for (self.inputs[1..]) |input| { self.output.digital = 0; // TODO self.output.analog *= input.analog; } } else { self.output.digital = self.inputs[0].digital; self.output.analog = self.inputs[0].analog; for (self.inputs[1..]) |input| { self.output.digital = 0; // TODO self.output.analog = switch (std.math.order(@abs(self.output.analog), @abs(input.analog))) { .lt => self.output.analog, .eq => @min(self.output.analog, input.analog), // TODO what does this *actually* do? .gt => input.analog, }; } } self.output.analog = std.math.clamp(self.output.analog, -1.0, 1.0); } }; test "min" { var a = Signal{ .analog = 0.0 }; var b = Signal{ .analog = 1.0 }; var inputs = [_]*Signal{ &a, &b }; var and1 = And{ .inputs = &inputs }; and1.process(); try std.testing.expectEqual(0.0, and1.output.analog); a.analog = -0.5; b.analog = -0.2; and1.process(); try std.testing.expectEqual(-0.2, and1.output.analog); } pub const Or = struct { inputs: []*Signal, output: Signal = .{}, // if false, is in Maximum Input mode // if true, is in Add Inputs mode arithmetic_mode: bool = false, // TODO check implementation pub fn process(self: *Or) void { if (self.arithmetic_mode) { self.output.digital = self.inputs[0].digital; self.output.analog = self.inputs[0].analog; for (self.inputs[1..]) |input| { self.output.digital = 0; // TODO self.output.analog += input.analog; } } else { self.output.digital = self.inputs[0].digital; self.output.analog = self.inputs[0].analog; for (self.inputs[1..]) |input| { self.output.digital = 0; // TODO self.output.analog = switch (std.math.order(@abs(self.output.analog), @abs(input.analog))) { .lt => input.analog, .eq => @max(self.output.analog, input.analog), // TODO what does this *actually* do? .gt => self.output.analog, }; } } self.output.analog = std.math.clamp(self.output.analog, -1.0, 1.0); } }; test "max" { var a = Signal{ .analog = 0.0 }; var b = Signal{ .analog = 1.0 }; var inputs = [_]*Signal{ &a, &b }; var or1 = Or{ .inputs = &inputs }; or1.process(); try std.testing.expectEqual(1.0, or1.output.analog); a.analog = -0.5; b.analog = -0.2; or1.process(); try std.testing.expectEqual(-0.5, or1.output.analog); }