logic_circus/lib.rs
1#![warn(clippy::cargo_common_metadata)]
2#![allow(unused_unsafe)]
3
4//! Logic Circus is a logic circut simulator written in Rust
5//!
6//! It is nowhere near finished and I'd like to one day even add a GUI
7//! You can build circuits by composing other circuits together (with
8//! [`Component`]) or by fully implementing their logic (with the [`GateLike`]
9//! trait)
10
11use std::convert::Infallible;
12
13use gate::GateKind;
14use sequals::SequalsExtension;
15use util::ContainerSizeGames;
16
17mod constructors;
18mod gate;
19mod implemented;
20mod sequals;
21#[cfg(test)]
22mod todo;
23mod util;
24
25/// A semantic alias for a boolean
26pub type Bit = bool;
27
28/// A circuit
29///
30/// use [`builder()`][Self::builder] to construct
31///
32/// use compute to run the logic
33///
34/// stick inside a [`Gate::component`] to use as part of a bigger component and
35/// compose with other gates
36///
37/// put a gate implemented in rust (or an enum of them) in `<Rust>` to use it
38/// (or them)
39#[derive(Clone, Debug)]
40pub struct Component<Rust = Infallible> {
41 gates: Vec<Gate<Rust>>,
42 sequals: Vec<Vec<Sequal>>,
43 outputs: usize,
44}
45
46/// The constructor for [`Component`]
47///
48/// you create one with [`Component::builder()`]
49///
50/// use [`gate`][ComponentBuilder::gate] to add another inner gate
51///
52/// use [`inputs`][ComponentBuilder::inputs] to set the input locations and
53/// finalize construction (get your final [`Component`])
54///
55/// put a gate implemented in rust (or an enum of them) in `<Rust>` to use it
56/// (or them)
57pub struct ComponentBuilder<Rust = Infallible> {
58 gates: Vec<Gate<Rust>>,
59 sequals: Vec<Vec<Sequal>>,
60 outputs: usize,
61}
62
63/// A wire
64///
65/// connects the outputs of the current [`Gate`] (or the inputs of the
66/// [`Component`]) to either the inputs of another gate or the outputs of the
67/// containing component.
68///
69/// if you want to send an output to several places it must be done
70/// indirectly, you should take a look at [`Gate::dup`]
71#[derive(Clone, Copy, Debug)]
72pub enum Sequal {
73 Gate { index: usize, entry: usize },
74 End { output: usize },
75}
76
77/// A gate, or rather a single node of a [`Component`]
78///
79/// put a gate implemented in rust (or an enum of them) in `<Rust>` to use it
80/// (or them)
81#[derive(Clone, Debug)]
82pub struct Gate<Rust = Infallible> {
83 kind: GateKind<Rust>,
84 inputs: Vec<Bit>,
85 inputs_filled: usize,
86}
87
88/// Implement a gate in rust
89///
90/// if you want to implement a custom [`Gate`] or a node of a [`Component`] in
91/// rust code instead of as a component with logic (whether the goal is
92/// efficiancy or you're just lazy, I won't judge) all you have to do is
93/// implement this trait on your type and stick it in the generic `<Rust>`
94/// parameter of your [`Component`], then construct the full gate with
95/// [`Gate::rust`]
96///
97/// you yourself are not supposed to ever need to use the functions in this
98/// trait
99///
100/// # Example
101/// ```
102/// use logic_circus::{Bit, Component, Gate, GateLike};
103/// struct And;
104///
105/// impl GateLike for And {
106/// fn compute(&mut self, input: Vec<Bit>) -> Vec<Bit> {
107/// vec![input[0] && input[1]]
108/// }
109/// fn num_of_inputs(&self) -> usize {
110/// 2
111/// }
112/// }
113///
114/// let gate = Gate::rust(And);
115/// let mut component = Component::single_gate(gate, 1);
116/// assert_eq!(component.compute(vec![true, true]), vec![true]);
117/// // note that I must `reset` the component in between each call to compute even
118/// // though `And` doesn't implement the `reset` function, this is due to internal
119/// // logic of the `compute` function
120/// component.reset(false);
121/// assert_eq!(component.compute(vec![true, false]), vec![false]);
122/// ```
123pub trait GateLike {
124 /// This function computes the outputs of a gate for it's given inputs,
125 /// these are in `Vec`s as a gate can have multiple inputs and outputs,
126 /// for example an `And` gate has two inputs and one output
127 ///
128 /// you yourself are not supposed to ever need to use this function
129 ///
130 /// # Safety
131 /// `input`s length must be equal to `num_of_inputs()`
132 unsafe fn compute(&mut self, input: Vec<Bit>) -> Vec<Bit>;
133 /// This function counts the amount of inputs a gate has
134 ///
135 /// you yourself are not supposed to ever need to use this function
136 fn num_of_inputs(&self) -> usize;
137 /// This function resets the gate, if your gate needs to be reset between cpu tiks (I have no example) or between runs (for example memory)
138 fn reset(&mut self, full: bool) {
139 let _ = full;
140 // the default implementation does nothing as most gates don't need a reset mechanism (other than the one that is implemented for all of them)
141 }
142}
143
144impl<Rust> Component<Rust>
145where
146 Rust: GateLike,
147{
148 /// Runs the component
149 ///
150 /// This is an alias for [`compute`][GateLike::compute] since that function
151 /// is inside a trait impl and thus way harder to find, also the name is
152 /// nice
153 ///
154 /// # Safety
155 /// this function is only safe if:
156 /// - `input` is the same length as the last sequals vector
157 /// - all `Sequal::End`s point to outputs that exist - guaranteed if
158 /// constructed with [`ComponentBuilder`] and used outputs from 0 up to
159 /// the number of outputs
160 /// - all `Sequal::Gate`s must point to actual inputs of actual gates
161 /// - component is clean (right after construction or after a call to
162 /// `Component::reset()`)
163 pub unsafe fn run(&mut self, input: Vec<Bit>) -> Vec<Bit> {
164 self.compute(input)
165 }
166
167 /// Resets the whole component
168 ///
169 /// this must be done in between cpu ticks (calls to compute) in order for
170 /// the gates to work (otherwise the code is panicks in debug and is
171 /// unsound in release)
172 ///
173 /// a full reset is done in between runs, this is to reset things like
174 /// memory that are kept between ticks
175 pub fn reset(&mut self, full_reset: bool) {
176 for gate in &mut self.gates {
177 gate.reset(full_reset)
178 }
179 }
180}
181
182impl<Rust> GateLike for Component<Rust>
183where
184 Rust: GateLike,
185{
186 /// # Safety
187 /// this function is only safe if:
188 /// - `input` is the same length as the last sequals vector
189 /// - all `Sequal::End`s point to outputs that exist - guaranteed if
190 /// constructed with [`ComponentBuilder`] and used outputs from 0 up to
191 /// the number of outputs
192 /// - all `Sequal::Gate`s must point to actual inputs of actual gates
193 /// - component is clean (right after construction or after a call to
194 /// `Component::reset()`)
195 unsafe fn compute(&mut self, input: Vec<Bit>) -> Vec<Bit> {
196 let mut outputs = Vec::zeroed(self.outputs);
197
198 // SAFETY: `sequals.len() - 1` must be in bounds for `sequals`
199 self.sequals.run(
200 self.sequals.len() - 1,
201 &input,
202 &mut self.gates,
203 &mut outputs,
204 );
205
206 outputs
207 }
208
209 fn num_of_inputs(&self) -> usize {
210 unsafe { self.sequals.get_unchecked(self.sequals.len() - 1) }.len()
211 }
212}
213
214impl GateLike for Infallible {
215 unsafe fn compute(&mut self, _input: Vec<Bit>) -> Vec<Bit> {
216 unreachable!("can't compute result of infallible")
217 }
218
219 fn num_of_inputs(&self) -> usize {
220 unreachable!("infallible has <???> inputs")
221 }
222}