Last active
July 4, 2020 10:24
-
-
Save cleoold/c386a198583714e3816e3da932fc1c93 to your computer and use it in GitHub Desktop.
simple publisher-observer logic gate simulation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <vector> | |
#include <string> | |
// observer-based logic gate simulation | |
// things are done in a naive way. one may have stack overflow | |
// if events invoke each other | |
// interfaces | |
struct LUnit { | |
static long count; | |
std::string name; | |
LUnit() : name{std::string{"unnamed."} + std::to_string(count++)} {} | |
virtual void set(bool, int) {} | |
virtual ~LUnit() = 0; | |
}; | |
long LUnit::count {0}; | |
LUnit::~LUnit() {} | |
struct SingleOutputUnit : LUnit { | |
LUnit *to; | |
int to_serial; | |
virtual void wired(LUnit *to_, int s) { | |
to = to_; | |
to_serial = s; | |
} | |
virtual ~SingleOutputUnit() = 0; | |
}; | |
SingleOutputUnit::~SingleOutputUnit() {} | |
struct MultiOutputUnit : LUnit { | |
std::vector<std::pair<LUnit *, int>> to; | |
virtual void wired(LUnit *to_, int to_port, int my_out_port) { | |
to[my_out_port] = { to_, to_port }; | |
} | |
virtual ~MultiOutputUnit() = 0; | |
}; | |
MultiOutputUnit::~MultiOutputUnit() {} | |
// simple logic gates implementations | |
struct Terminal : LUnit { | |
void set(bool b, int in_serial) override { | |
if (in_serial != 0) throw std::runtime_error {"invalid input port"}; | |
std::cout << "gate: <" << name << "> value: <" << b << ">" << std::endl; | |
} | |
}; | |
struct Wire : SingleOutputUnit { | |
void set(bool b, int in_serial) override { | |
if (!to) throw std::runtime_error {"wire disconnected"}; | |
if (in_serial != 0) throw std::runtime_error {"invalid input port"}; | |
to->set(b, to_serial); | |
} | |
}; | |
struct NotGate : SingleOutputUnit { | |
void set(bool b, int in_serial) override { | |
if (!to) throw std::runtime_error {"wire disconnected"}; | |
if (in_serial != 0) throw std::runtime_error {"invalid input port"}; | |
to->set(!b, to_serial); | |
} | |
}; | |
struct AndGate : SingleOutputUnit { | |
bool in1, in2; | |
bool in1set = false, in2set = false; | |
void set(bool b, int in_serial) override { | |
if (!to) throw std::runtime_error {"wire disconnected"}; | |
if (in_serial == 0) in1 = b, in1set = true; | |
else if (in_serial == 1) in2 = b, in2set = true; | |
else throw std::runtime_error {"invalid input port"}; | |
if (in1set && in2set) to->set(in1 && in2, to_serial); | |
} | |
}; | |
struct OrGate : SingleOutputUnit { | |
bool in1, in2; | |
bool in1set = false, in2set = false; | |
void set(bool b, int in_serial) override { | |
if (!to) throw std::runtime_error {"wire disconnected"}; | |
if (in_serial == 0) in1 = b, in1set = true; | |
else if (in_serial == 1) in2 = b, in2set = true; | |
else throw std::runtime_error {"invalid input port"}; | |
if (in1set && in2set) to->set(in1 || in2, to_serial); | |
} | |
}; | |
template<int size = 2> | |
struct Splitter : MultiOutputUnit { | |
Splitter() { | |
static_assert(size >= 2); | |
to.resize(size); | |
} | |
void wired(LUnit *to_, int to_port, int my_out_port) override { | |
if (static_cast<unsigned>(my_out_port) >= size) | |
throw std::runtime_error {"invalid output port"}; | |
MultiOutputUnit::wired(to_, to_port, my_out_port); | |
} | |
void set(bool b, int in_serial) override { | |
if (in_serial != 0) throw std::runtime_error {"invalid input port"}; | |
for (auto &t : to) { | |
if (!t.first) throw std::runtime_error {"wire disconnected"}; | |
t.first->set(b, t.second); | |
} | |
} | |
}; | |
// some compound logic gate implementations | |
struct NandGate : SingleOutputUnit { | |
AndGate andg; | |
NotGate notg; | |
NandGate() { | |
andg.wired(¬g, 0); | |
} | |
void wired(LUnit *to_, int s) override { | |
SingleOutputUnit::wired(to_, s); | |
notg.wired(to_, s); | |
} | |
void set(bool b, int in_serial) override { | |
andg.set(b, in_serial); | |
} | |
}; | |
// A^B = (A+B)~(AB) | |
struct XorGate : SingleOutputUnit { | |
Splitter<2> ain, bin; | |
NandGate nandg; | |
OrGate org; | |
AndGate andg; | |
XorGate() { | |
ain.wired(&nandg, 0, 0); | |
ain.wired(&org, 0, 1); | |
bin.wired(&nandg, 1, 0); | |
bin.wired(&org, 1, 1); | |
nandg.wired(&andg, 0); | |
org.wired(&andg, 1); | |
} | |
void wired(LUnit *to_, int s) override { | |
SingleOutputUnit::wired(to_, s); | |
andg.wired(to_, s); | |
} | |
void set(bool b, int in_serial) override { | |
if (in_serial == 0) ain.set(b, 0); | |
else if (in_serial == 1) bin.set(b, 0); | |
else throw std::runtime_error {"invalid input port"}; | |
} | |
}; | |
// what the fk | |
template<int size> | |
struct MultiwayAndGate : MultiwayAndGate<size-1> { | |
AndGate anothergate; | |
MultiwayAndGate() { | |
MultiwayAndGate<size-1>::wired(&anothergate, 0); | |
} | |
void wired(LUnit *to_, int s) override { | |
SingleOutputUnit::wired(to_, s); | |
anothergate.wired(to_, s); | |
} | |
void set(bool b, int in_serial) override { | |
if (in_serial == size-1) anothergate.set(b, 1); | |
else MultiwayAndGate<size-1>::set(b, in_serial); | |
} | |
}; | |
template<> | |
struct MultiwayAndGate<2> : SingleOutputUnit { | |
AndGate gate; | |
void wired(LUnit *to_, int s) override { | |
SingleOutputUnit::wired(to_, s); | |
gate.wired(to_, s); | |
} | |
void set(bool b, int in_serial) override { | |
gate.set(b, in_serial); | |
} | |
}; | |
// non-member convenient functions | |
void wire(SingleOutputUnit *from, LUnit *to, int to_port) { | |
if (!from) throw std::runtime_error {"cannot wire"}; | |
from->wired(to, to_port); | |
} | |
void wire(MultiOutputUnit *from, LUnit *to, int to_port, int from_output_port) { | |
if (!from) throw std::runtime_error {"cannot wire"}; | |
from->wired(to, to_port, from_output_port); | |
} | |
void wire(LUnit *from, LUnit *to, int to_port) { | |
wire(dynamic_cast<SingleOutputUnit *>(from), to, to_port); | |
} | |
void wire(LUnit *from, LUnit *to, int to_port, int from_output_port) { | |
wire(dynamic_cast<MultiOutputUnit *>(from), to, to_port, from_output_port); | |
} | |
void nameit(LUnit *u, std::string n) { | |
u->name = std::move(n); | |
} | |
// demo | |
int main() { | |
LUnit *i1w = new Wire{}, | |
*i2w = new Wire{}, | |
*nandg = new NandGate{}, | |
*spli = new Splitter<2>{}, | |
*term = new Terminal{}, | |
*notg = new NotGate{}, | |
*term2 = new Terminal{}; | |
wire(i1w, nandg, 0); | |
wire(i2w ,nandg, 1); | |
wire(nandg, spli, 0); | |
wire(spli, term, 0, 0); | |
wire(spli, notg, 0, 1); | |
wire(notg, term2, 0); | |
nameit(term, "nand"); | |
i1w->set(true, 0); | |
i2w->set(true, 0); | |
LUnit *xorg = new XorGate{}, | |
*xterm = new Terminal{}; | |
wire(xorg, xterm, 0); | |
nameit(xterm, "xor"); | |
xorg->set(false, 0); | |
xorg->set(false, 1); | |
LUnit *mandg = new MultiwayAndGate<5>{}, | |
*mnotg = new NotGate{}, | |
*mterm = new Terminal{}; | |
wire(mnotg, mandg, 2); | |
wire(mandg, mterm, 0); | |
mandg->set(true, 0); | |
mandg->set(true, 1); | |
mnotg->set(false, 0); | |
mandg->set(true, 3); | |
mandg->set(true, 4); | |
// suddenly change | |
mandg->set(false, 4); | |
// delete | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
gate: <nand> value: <0> | |
gate: <unnamed.8> value: <1> | |
gate: <xor> value: <0> | |
gate: <unnamed.24> value: <1> | |
gate: <unnamed.24> value: <0> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment