#include <memory>
#include <deque>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <string>
#include <utility>
#include <algorithm>
#include <limits>
#include <iostream>
using Pin = size_t;
class Circuit;
class LogicComponent;
struct Connection {
LogicComponent *component;
Pin pin;
Connection() : component(nullptr), pin(std::numeric_limits<Pin>::max()) {}
Connection(LogicComponent *comp, Pin p) : component(comp), pin(p) {}
};
class LogicComponent {
friend class Circuit;
protected:
std::unordered_map<Pin, Connection> inputs;
std::unordered_map<Pin, Connection> outputs;
public:
LogicComponent() : inputs(), outputs() {}
virtual ~LogicComponent() {}
virtual void update() = 0;
virtual bool state(Pin output_pin) = 0;
virtual Pin max_inputs() = 0;
virtual Pin max_outputs() = 0;
};
class InputGate : public LogicComponent {
bool input;
public:
void update() override {}
bool state(Pin output_pin) override {
return input;
}
Pin max_inputs() override {
return 0;
}
Pin max_outputs() override {
return std::numeric_limits<Pin>::max();
}
void set(bool value) {
input = value;
}
};
class OutputGate : public LogicComponent {
public:
void update() override {}
bool state(Pin output_pin) override {
throw std::runtime_error("no output pins");
}
Pin max_inputs() override {
return 1;
}
Pin max_outputs() override {
return 0;
}
bool get() {
auto in = inputs[0];
if(in.component != nullptr) {
return in.component->state(in.pin);
}
return false;
}
};
class Circuit {
std::unordered_map<std::string, std::unique_ptr<LogicComponent>> components;
std::vector<std::unique_ptr<InputGate>> inputs;
std::vector<std::unique_ptr<OutputGate>> outputs;
std::deque<LogicComponent*> changed; // would use std::queue if it supported iterators
bool check_input(LogicComponent* current, const std::unordered_set<LogicComponent*>& visited, const std::deque<LogicComponent*>& queue) {
for(auto entry : current->inputs) {
auto connection = entry.second;
if(std::find(std::begin(queue), std::end(queue), connection.component) != std::end(queue)) return false;
}
if(visited.find(current) != visited.end()) return false;
return true;
}
void perform_update(std::deque<LogicComponent*> queue) {
auto visited = std::unordered_set<LogicComponent*>();
while(!queue.empty()) {
auto current = queue.front();
queue.pop_front();
if(!check_input(current, visited, queue)) continue; // already processed current or an input is waiting to be processed
current->update();
visited.insert(current);
for(auto entry : current->outputs) {
auto connection = entry.second;
if(visited.find(connection.component) != visited.end()) { // if already processed connected component...
changed.push_back(connection.component); // ...update it again during the next partial update
} else {
queue.push_back(connection.component); // else update it during this update
}
}
}
}
LogicComponent *lookup(std::string name) {
auto component = components[name].get();
if(component == nullptr) throw std::runtime_error("unknown component");
return component;
}
public:
Circuit() : components(), inputs(), outputs(), changed() {}
void update_all() {
changed.clear();
auto queue = std::deque<LogicComponent*>();
for(auto& input : inputs) {
queue.push_back(input.get());
}
perform_update(queue);
}
void update() {
auto queue = std::deque<LogicComponent*>(std::begin(changed), std::end(changed));
changed.clear();
perform_update(queue);
}
void create_inputs(size_t count) {
for(auto i = 0; i < count; ++i) {
inputs.push_back(std::make_unique<InputGate>());
}
}
void create_outputs(size_t count) {
for(auto i = 0; i < count; ++i) {
outputs.push_back(std::make_unique<OutputGate>());
}
}
void set_input(size_t input, bool value) {
inputs[input]->set(value);
changed.push_back(inputs[input].get());
}
bool get_output(size_t output) {
return outputs[output]->get();
}
template<typename TComponentType, typename... Args>
void create_component(std::string name, Args&&... args) {
if(components.find(name) != components.end()) throw std::runtime_error("component with same name already exists!");
components[name] = std::make_unique<TComponentType>(std::forward<Args>(args)...);
}
bool can_connect(std::string name_a, Pin output_pin, std::string name_b, Pin input_pin) {
auto compA = lookup(name_a);
auto compB = lookup(name_b);
bool a_has_output = (compA->outputs.find(output_pin) != compA->outputs.end());
bool b_has_input = (compB->inputs.find(input_pin) != compB->inputs.end());
bool valid_connection = output_pin < compA->max_outputs() && input_pin < compB->max_inputs();
return !a_has_output && !b_has_input;
}
void connect(std::string name_a, Pin output_pin, std::string name_b, Pin input_pin) {
if(!can_connect(name_a, output_pin, name_b, input_pin)) throw std::runtime_error("cannot connect components");
auto compA = lookup(name_a);
auto compB = lookup(name_b);
compA->outputs[output_pin] = Connection{ compB, input_pin };
compB->inputs[input_pin] = Connection{ compA, output_pin };
}
void connect_input(size_t input, std::string name, Pin input_pin) {
auto component = lookup(name);
inputs[input]->outputs[0] = Connection{ component, input_pin };
component->inputs[input_pin] = Connection{ inputs[input].get(), 0 };
}
void connect_output(size_t output, std::string name, Pin output_pin) {
auto component = lookup(name);
outputs[output]->inputs[0] = Connection{ component, output_pin };
component->outputs[output_pin] = Connection{ outputs[output].get(), 0 };
}
void disconnect_output(std::string name, Pin output_pin) {
auto component = lookup(name);
auto connection = component->outputs[output_pin];
component->outputs.erase(output_pin);
if(connection.component != nullptr) {
connection.component->inputs.erase(connection.pin);
}
}
void disconnect_input(std::string name, Pin input_pin) {
auto component = lookup(name);
auto connection = component->inputs[input_pin];
component->inputs.erase(input_pin);
if(connection.component != nullptr) {
connection.component->outputs.erase(connection.pin);
}
}
};
// implement other logic components as needed, e.g. and:
template<size_t Arity>
class AndGate : public LogicComponent {
bool value = false;
public:
void update() override {
value = true;
for(auto i = 0u; i < Arity; ++i) {
auto input = inputs[i];
if(input.component != nullptr) {
value = value && input.component->state(input.pin);
} else {
value = false; // change this if unset input should be assumed to be set
}
}
}
bool state(Pin output_pin) override {
if(output_pin >= max_outputs()) throw std::runtime_error("output pin too high!");
return value;
}
Pin max_inputs() override {
return Arity;
}
Pin max_outputs() override {
return 1;
}
};
class NotGate : public LogicComponent {
bool value = false;
public:
void update() override {
auto input = inputs[0];
if(input.component != nullptr) {
value = !input.component->state(input.pin);
}
}
bool state(Pin output_pin) override {
return value;
}
Pin max_inputs() override {
return 1;
}
Pin max_outputs() override {
return 1;
}
};
class Wire : public LogicComponent {
bool value = false;
public:
void update() override {
value = false;
for(auto entry : inputs) {
auto connection = entry.second;
if(connection.component != nullptr) value = value || connection.component->state(connection.pin);
}
}
bool state(Pin output_pin) override {
return value;
}
Pin max_inputs() override {
return std::numeric_limits<Pin>::max();
}
Pin max_outputs() override {
return std::numeric_limits<Pin>::max();
}
};
// same for OrGate, XorGate, Clocks, ...
// usage example
int main() {
Circuit circuit;
circuit.create_component<AndGate<2>>("and1");
circuit.create_component<NotGate>("not1");
circuit.create_component<Wire>("wire1");
circuit.create_inputs(1);
circuit.create_outputs(1);
circuit.connect("and1", 0, "not1", 0);
circuit.connect("not1", 0, "wire1", 0);
circuit.connect("wire1", 0, "and1", 0);
circuit.connect_input(0, "and1", 1);
circuit.connect_output(0, "wire1", 1);
circuit.set_input(0, true);
for(auto i = 0; i < 10; ++i) {
circuit.update();
std::cout << "output 0: " << (circuit.get_output(0) ? "1" : "0") << "\n";
}
}