component implementation

This commit is contained in:
Jeeves 2025-02-12 04:52:27 -07:00
parent 33e730a5a8
commit 900c2c0b12

View file

@ -1,58 +1,173 @@
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
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 not1 = try Not.init(allocator);
defer not1.deinit(allocator);
not1.invert_output = false;
not1.component.inputs.items[0] = &input; // manually set the input here
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 battery1 = try Battery.init(allocator);
defer battery1.deinit(allocator);
var battery2 = try Battery.init(allocator);
defer battery2.deinit(allocator);
var battery3 = try Battery.init(allocator);
defer battery3.deinit(allocator);
battery1.value = -0.5;
battery2.value = 0.5;
battery3.value = -1.0;
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 or1 = try Or.init(allocator);
defer or1.deinit(allocator);
var or2 = try Or.init(allocator);
defer or2.deinit(allocator);
or1.arithmetic_mode = true;
or2.arithmetic_mode = true;
try or1.component.setNumInputs(allocator, 3);
try or2.component.setNumInputs(allocator, 3);
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 };
var or3 = try Or.init(allocator);
defer or3.deinit(allocator);
var or4 = try Or.init(allocator);
defer or4.deinit(allocator);
var or5 = try Or.init(allocator);
defer or5.deinit(allocator);
or1_inputs[2] = &and1.output;
or2_inputs[2] = &and2.output;
var or6 = try Or.init(allocator);
defer or6.deinit(allocator);
var or7 = try Or.init(allocator);
defer or7.deinit(allocator);
var or8 = try Or.init(allocator);
defer or8.deinit(allocator);
var and1 = try And.init(allocator);
defer and1.deinit(allocator);
var and2 = try And.init(allocator);
defer and2.deinit(allocator);
or6.arithmetic_mode = true;
or7.arithmetic_mode = true;
or8.arithmetic_mode = true;
and1.arithmetic_mode = true;
and2.arithmetic_mode = true;
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();
battery1.component.connect(0, &or3.component, 1);
battery1.component.connect(0, &or4.component, 1);
battery1.component.connect(0, &or5.component, 1);
battery2.component.connect(0, &or6.component, 0);
battery2.component.connect(0, &or7.component, 0);
battery2.component.connect(0, &or8.component, 0);
battery3.component.connect(0, &and1.component, 0);
battery3.component.connect(0, &and2.component, 0);
not1.component.connect(0, &or3.component, 0);
not1.component.connect(0, &or1.component, 0);
not1.component.connect(0, &or1.component, 1);
or1.component.connect(0, &or4.component, 0);
or1.component.connect(0, &or2.component, 0);
or1.component.connect(0, &or2.component, 1);
or2.component.connect(0, &or5.component, 0);
or3.component.connect(0, &or6.component, 1);
or4.component.connect(0, &or7.component, 1);
or5.component.connect(0, &or8.component, 1);
or6.component.connect(0, &and1.component, 1);
and1.component.connect(0, &or1.component, 2);
or7.component.connect(0, &and2.component, 1);
and2.component.connect(0, &or2.component, 2);
battery1.component.process();
battery2.component.process();
battery3.component.process();
not1.component.process();
or3.component.process();
or6.component.process();
and1.component.process();
or1.component.process();
or4.component.process();
or7.component.process();
and2.component.process();
or2.component.process();
or5.component.process();
or8.component.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 });
std.debug.print("{}\n{}\n\n", .{ or1.component.outputs.items[0], or2.component.outputs.items[0] });
std.debug.print("{}\n{}\n{}\n\n", .{ or3.component.outputs.items[0], or4.component.outputs.items[0], or5.component.outputs.items[0] });
std.debug.print("{}\n{}\n{}\n{}\n{}\n\n", .{ or6.component.outputs.items[0], and1.component.outputs.items[0], or7.component.outputs.items[0], and2.component.outputs.items[0], or8.component.outputs.items[0] });
}
var null_signal = Signal{};
pub const Component = struct {
inputs: Inputs,
outputs: Outputs,
processFn: *const fn (*Component) void,
pub fn init(allocator: std.mem.Allocator, inputs_len: usize, outputs_len: usize, processFn: *const fn (*Component) void) !Component {
var inputs = Inputs.empty;
errdefer inputs.deinit(allocator);
try inputs.resize(allocator, inputs_len);
for (0..inputs.items.len) |i| inputs.items[i] = &null_signal;
var outputs = Outputs.empty;
errdefer outputs.deinit(allocator);
try outputs.resize(allocator, outputs_len);
for (0..outputs.items.len) |i| outputs.items[i] = Signal{};
return .{
.inputs = inputs,
.outputs = outputs,
.processFn = processFn,
};
}
pub fn deinit(self: *Component, allocator: std.mem.Allocator) void {
self.inputs.deinit(allocator);
self.outputs.deinit(allocator);
}
pub fn process(self: *Component) void {
self.processFn(self);
}
// TODO allow inserting the new elements at an arbitrary index
pub fn setNumInputs(self: *Component, allocator: std.mem.Allocator, new_len: usize) !void {
const old_len = self.inputs.items.len;
try self.inputs.resize(allocator, new_len);
if (new_len > old_len) for (old_len..new_len) |i| {
self.inputs.items[i] = &null_signal;
};
}
// TODO allow inserting the new elements at an arbitrary index
// TODO is this allocating the new output Signals on the stack or dumbthing?
pub fn setNumOutputs(self: *Component, allocator: std.mem.Allocator, new_len: usize) !void {
const old_len = self.outputs.items.len;
try self.outputs.resize(allocator, new_len);
if (new_len > old_len) for (old_len..new_len) |i| {
self.outputs.items[i] = Signal{};
};
}
pub fn connect(self: *Component, self_idx: usize, to: *Component, to_idx: usize) void {
to.inputs.items[to_idx] = &self.outputs.items[self_idx];
}
pub const Input = *Signal;
pub const Output = Signal;
const Inputs = std.ArrayListUnmanaged(Input);
const Outputs = std.ArrayListUnmanaged(Output);
};
pub const Signal = struct {
digital: i2 = 0,
analog: f32 = 0.0,
@ -75,67 +190,99 @@ pub const Signal = struct {
};
pub const Battery = struct {
value: f32,
component: Component,
output: Signal = .{},
value: f32 = 1.0,
pub fn process(self: *Battery) void {
self.output.digital = @intFromFloat(std.math.sign(self.value));
self.output.analog = self.value;
pub fn init(allocator: std.mem.Allocator) !Battery {
return .{
.component = try Component.init(allocator, 0, 1, &process),
};
}
pub fn deinit(self: *Battery, allocator: std.mem.Allocator) void {
self.component.deinit(allocator);
}
pub fn process(component: *Component) void {
const self: *Battery = @fieldParentPtr("component", component);
component.outputs.items[0].digital = @intFromFloat(std.math.sign(self.value));
component.outputs.items[0].analog = self.value;
}
};
pub const Not = struct {
input: *Signal,
output: Signal = .{},
component: Component,
invert_output: bool = true,
pub fn process(self: *Not) void {
pub fn init(allocator: std.mem.Allocator) !Not {
return .{
.component = try Component.init(allocator, 1, 1, &process),
};
}
pub fn deinit(self: *Not, allocator: std.mem.Allocator) void {
self.component.deinit(allocator);
}
pub fn process(component: *Component) void {
const self: *Not = @fieldParentPtr("component", component);
if (self.invert_output) {
self.output.digital = 1 - @as(i2, @intCast(@abs(self.input.digital)));
self.output.analog = 1.0 - @abs(self.input.analog);
component.outputs.items[0].digital = 1 - @as(i2, @intCast(@abs(component.inputs.items[0].digital)));
component.outputs.items[0].analog = 1.0 - @abs(component.inputs.items[0].analog);
} else {
self.output.digital = self.input.digital;
self.output.analog = self.input.analog;
component.outputs.items[0].digital = component.inputs.items[0].digital;
component.outputs.items[0].analog = component.inputs.items[0].analog;
}
self.output.analog = std.math.clamp(self.output.analog, -1.0, 1.0);
component.outputs.items[0].analog = std.math.clamp(component.outputs.items[0].analog, -1.0, 1.0);
}
};
pub const And = struct {
inputs: []*Signal,
output: Signal = .{},
component: Component,
// if false, is in Minimum Input mode
// if true, is in Multiply Inputs mode
arithmetic_mode: bool = false,
pub fn init(allocator: std.mem.Allocator) !And {
return .{
.component = try Component.init(allocator, 2, 1, &process),
};
}
pub fn deinit(self: *And, allocator: std.mem.Allocator) void {
self.component.deinit(allocator);
}
// TODO check implementation
pub fn process(self: *And) void {
pub fn process(component: *Component) void {
const self: *And = @fieldParentPtr("component", component);
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;
component.outputs.items[0].digital = component.inputs.items[0].digital;
component.outputs.items[0].analog = component.inputs.items[0].analog;
for (component.inputs.items[1..]) |input| {
component.outputs.items[0].digital = 0; // TODO
component.outputs.items[0].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?
component.outputs.items[0].digital = component.inputs.items[0].digital;
component.outputs.items[0].analog = component.inputs.items[0].analog;
for (component.inputs.items[1..]) |input| {
component.outputs.items[0].digital = 0; // TODO
component.outputs.items[0].analog = switch (std.math.order(@abs(component.outputs.items[0].analog), @abs(input.analog))) {
.lt => component.outputs.items[0].analog,
.eq => @min(component.outputs.items[0].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);
component.outputs.items[0].analog = std.math.clamp(component.outputs.items[0].analog, -1.0, 1.0);
}
};
// TODO update test to use new Component interface
test "min" {
var a = Signal{ .analog = 0.0 };
var b = Signal{ .analog = 1.0 };
@ -153,38 +300,49 @@ test "min" {
}
pub const Or = struct {
inputs: []*Signal,
output: Signal = .{},
component: Component,
// if false, is in Maximum Input mode
// if true, is in Add Inputs mode
arithmetic_mode: bool = false,
pub fn init(allocator: std.mem.Allocator) !Or {
return .{
.component = try Component.init(allocator, 2, 1, &process),
};
}
pub fn deinit(self: *Or, allocator: std.mem.Allocator) void {
self.component.deinit(allocator);
}
// TODO check implementation
pub fn process(self: *Or) void {
pub fn process(component: *Component) void {
const self: *Or = @fieldParentPtr("component", component);
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;
component.outputs.items[0].digital = component.inputs.items[0].digital;
component.outputs.items[0].analog = component.inputs.items[0].analog;
for (component.inputs.items[1..]) |input| {
component.outputs.items[0].digital = 0; // TODO
component.outputs.items[0].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))) {
component.outputs.items[0].digital = component.inputs.items[0].digital;
component.outputs.items[0].analog = component.inputs.items[0].analog;
for (component.inputs.items[1..]) |input| {
component.outputs.items[0].digital = 0; // TODO
component.outputs.items[0].analog = switch (std.math.order(@abs(component.outputs.items[0].analog), @abs(input.analog))) {
.lt => input.analog,
.eq => @max(self.output.analog, input.analog), // TODO what does this *actually* do?
.gt => self.output.analog,
.eq => @max(component.outputs.items[0].analog, input.analog), // TODO what does this *actually* do?
.gt => component.outputs.items[0].analog,
};
}
}
self.output.analog = std.math.clamp(self.output.analog, -1.0, 1.0);
component.outputs.items[0].analog = std.math.clamp(component.outputs.items[0].analog, -1.0, 1.0);
}
};
// TODO update test to use new Component interface
test "max" {
var a = Signal{ .analog = 0.0 };
var b = Signal{ .analog = 1.0 };