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}