improve code, tests, and break Selector

This commit is contained in:
Jeeves 2025-04-30 08:16:36 -06:00
parent 1affc9d797
commit ee3b09191a

View file

@ -544,7 +544,7 @@ pub const Not = struct {
pub fn update(component: *Component) AllocatorError!void {
const self: *Not = @fieldParentPtr("component", component);
const output = component.getOutput(0);
const input = if (component.getInput(0)) |s| s else &Signal{};
const input = component.getInput(0) orelse &Signal{};
if (self.invert_output) {
output.digital = 1 - @as(i2, @intCast(@abs(input.digital)));
output.analog = 1.0 - @abs(input.analog);
@ -580,12 +580,12 @@ pub const And = struct {
pub fn update(component: *Component) AllocatorError!void {
const self: *And = @fieldParentPtr("component", component);
const output = component.getOutput(0);
const input0 = if (component.getInput(0)) |s| s else &Signal{};
const input0 = component.getInput(0) orelse &Signal{};
if (self.arithmetic_mode) {
output.digital = input0.digital;
output.analog = input0.analog;
for (component.inputs.items[1..]) |opt_input| {
const input = if (opt_input.signal) |s| s else &Signal{};
const input = opt_input.signal orelse &Signal{};
output.digital = 0; // TODO
output.analog *= input.analog;
}
@ -593,7 +593,7 @@ pub const And = struct {
output.digital = input0.digital;
output.analog = input0.analog;
for (component.inputs.items[1..]) |opt_input| {
const input = if (opt_input.signal) |s| s else &Signal{};
const input = opt_input.signal orelse &Signal{};
output.digital = 0; // TODO
output.analog = switch (std.math.order(@abs(output.analog), @abs(input.analog))) {
.lt => output.analog,
@ -606,15 +606,20 @@ pub const And = struct {
}
};
test "min" {
var a = Signal{ .analog = 0.0 };
var b = Signal{ .analog = 1.0 };
test "And" {
var a = Signal{};
var b = Signal{};
var and1 = try And.init(std.testing.allocator);
defer and1.component.deinit(std.testing.allocator);
and1.component.inputs.items[0].signal = &a;
and1.component.inputs.items[1].signal = &b;
// TODO test digital part
// if arithmetic_mode is false, should output the input value furthest from 0
a.analog = 0.0;
b.analog = 1.0;
try and1.component.update();
try std.testing.expectEqual(0.0, and1.component.outputs.items[0].signal.analog);
@ -622,6 +627,14 @@ test "min" {
b.analog = -0.2;
try and1.component.update();
try std.testing.expectEqual(-0.2, and1.component.outputs.items[0].signal.analog);
// if arithmetic_mode is true, should output the product of all inputs
and1.arithmetic_mode = true;
a.analog = 0.1;
b.analog = 0.1;
try and1.component.update();
try std.testing.expectApproxEqAbs(0.01, and1.component.outputs.items[0].signal.analog, 0.00001);
}
pub const Or = struct {
@ -648,12 +661,12 @@ pub const Or = struct {
pub fn update(component: *Component) AllocatorError!void {
const self: *Or = @fieldParentPtr("component", component);
const output = component.getOutput(0);
const input0 = if (component.getInput(0)) |s| s else &Signal{};
const input0 = component.getInput(0) orelse &Signal{};
if (self.arithmetic_mode) {
output.digital = input0.digital;
output.analog = input0.analog;
for (component.inputs.items[1..]) |opt_input| {
const input = if (opt_input.signal) |s| s else &Signal{};
const input = opt_input.signal orelse &Signal{};
output.digital = 0; // TODO
output.analog += input.analog;
}
@ -661,7 +674,7 @@ pub const Or = struct {
output.digital = input0.digital;
output.analog = input0.analog;
for (component.inputs.items[1..]) |opt_input| {
const input = if (opt_input.signal) |s| s else &Signal{};
const input = opt_input.signal orelse &Signal{};
output.digital = 0; // TODO
output.analog = switch (std.math.order(@abs(output.analog), @abs(input.analog))) {
.lt => input.analog,
@ -674,7 +687,7 @@ pub const Or = struct {
}
};
test "max" {
test "Or" {
var a = Signal{ .analog = 0.0 };
var b = Signal{ .analog = 1.0 };
@ -683,6 +696,8 @@ test "max" {
or1.component.inputs.items[0].signal = &a;
or1.component.inputs.items[1].signal = &b;
// TODO test digital part
try or1.component.update();
try std.testing.expectEqual(1.0, or1.component.getOutput(0).analog);
@ -690,6 +705,8 @@ test "max" {
b.analog = -0.2;
try or1.component.update();
try std.testing.expectEqual(-0.5, or1.component.getOutput(0).analog);
// TODO arithmetic mode
}
pub const Selector = struct {
@ -702,7 +719,7 @@ pub const Selector = struct {
var self = try allocator.create(Selector);
errdefer allocator.destroy(self);
self.* = .{ .component = undefined };
try Component.init(&self.component, allocator, "Selector", 2, 2, &update, &deinit);
try Component.init(&self.component, allocator, "Selector", 5, 4, &update, &deinit);
return self;
}
@ -712,55 +729,126 @@ pub const Selector = struct {
}
// TODO check implementation
// TODO broken, needs to be completely rewritten
pub fn update(component: *Component) AllocatorError!void {
const self: *Selector = @fieldParentPtr("component", component);
var last_idx: ?usize = null;
for (component.inputs.items, 0..) |input, idx| {
if (input.signal) |signal| {
if (signal.digital == 1) last_idx = idx;
const cycle = component.getInput(0) orelse &Signal{};
std.debug.print("len {d}\n", .{component.inputs.items.len});
if (cycle.digital > 0) {
self.increment();
} else if (cycle.digital < 0) {
self.decrement();
} else {
var last_idx: ?usize = null;
for (component.inputs.items[1..], 0..) |input, idx| {
if (input.signal) |signal| {
if (signal.digital == 1) last_idx = idx;
}
}
if (last_idx) |lidx| {
for (component.outputs.items[0..], 0..) |*output, idx| {
const flip = if (self.invert_output)
idx != lidx
else
idx == lidx;
output.signal = if (flip)
.{ .digital = 1, .analog = 1.0 }
else
.{ .digital = 0, .analog = 0.0 };
}
} else {
for (component.outputs.items[0..], 0..) |*output, idx| {
const flip = if (self.invert_output)
idx != self.current_output
else
idx == self.current_output;
std.debug.print("{d} flip {}\n", .{ idx, flip });
output.signal = if (flip)
.{ .digital = 1, .analog = 1.0 }
else
.{ .digital = 0, .analog = 0.0 };
}
}
}
if (last_idx) |lidx| {
for (component.outputs.items, 0..) |*output, idx| {
output.signal = if (if (self.invert_output) idx != lidx else idx == lidx)
.{ .digital = 1, .analog = 1.0 }
else
.{ .digital = 0, .analog = 0.0 };
}
std.debug.print("final values", .{});
for (component.outputs.items) |output| {
std.debug.print(" {}", .{output.signal});
}
std.debug.print("\n", .{});
}
pub fn resize(self: *Selector, allocator: std.mem.Allocator, new_len: usize) !void {
try self.component.resizeInputs(allocator, new_len + 1);
try self.component.resizeOutputs(allocator, new_len);
}
fn increment(self: *Selector) void {
const len = self.component.outputs.items.len;
if (self.current_output >= len)
self.current_output = 0
else
self.current_output += 1;
}
fn decrement(self: *Selector) void {
const len = self.component.outputs.items.len;
if (self.current_output >= 0)
self.current_output = len
else
self.current_output -= 1;
}
};
test "selector" {
var a = Signal{ .digital = 0 };
var b = Signal{ .digital = 1 };
test "Selector" {
var a = Signal{};
var b = Signal{};
var c = Signal{};
var selector = try Selector.init(std.testing.allocator);
defer selector.component.deinit(std.testing.allocator);
selector.component.inputs.items[0].signal = &a;
selector.component.inputs.items[1].signal = &b;
try selector.resize(std.testing.allocator, 3);
try selector.component.update();
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(0).*);
try std.testing.expectEqual(Signal{ .digital = 1, .analog = 1.0 }, selector.component.getOutput(1).*);
a.digital = 0;
b.digital = 0;
try selector.component.update();
try selector.component.update();
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(0).*);
try std.testing.expectEqual(Signal{ .digital = 1, .analog = 1.0 }, selector.component.getOutput(1).*);
a.digital = 1;
b.digital = 0;
// default state, output 0 should be active
try selector.component.update();
try std.testing.expectEqual(Signal{ .digital = 1, .analog = 1.0 }, selector.component.getOutput(0).*);
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(1).*);
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(2).*);
selector.invert_output = true;
// should remember last active output
selector.component.inputs.items[1].signal = &a;
selector.component.inputs.items[2].signal = &b;
selector.component.inputs.items[3].signal = &c;
a.digital = 0;
b.digital = 1;
c.digital = 0;
try selector.component.update();
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(0).*);
try std.testing.expectEqual(Signal{ .digital = 1, .analog = 1.0 }, selector.component.getOutput(1).*);
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(2).*);
a.digital = 0;
b.digital = 0;
c.digital = 0;
try selector.component.update();
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(0).*);
try std.testing.expectEqual(Signal{ .digital = 1, .analog = 1.0 }, selector.component.getOutput(1).*);
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(2).*);
selector.invert_output = true;
try selector.component.update();
try std.testing.expectEqual(Signal{ .digital = 1, .analog = 1.0 }, selector.component.getOutput(0).*);
try std.testing.expectEqual(Signal{ .digital = 0, .analog = 0.0 }, selector.component.getOutput(1).*);
try std.testing.expectEqual(Signal{ .digital = 1, .analog = 1.0 }, selector.component.getOutput(2).*);
// TODO cycle input
}
const AllocatorError = std.mem.Allocator.Error;