logic-circus 0.3.0

Open source logic circuit simualtor written in Rust.
Documentation
#![warn(clippy::cargo_common_metadata)]
#![allow(unused_unsafe)]

//! Logic Circus is a logic circut simulator written in Rust
//!
//! It is nowhere near finished and I'd like to one day even add a GUI
//! You can build circuits by composing other circuits together (with
//! [`Component`]) or by fully implementing their logic (with the [`GateLike`]
//! trait)

use std::convert::Infallible;

use gate::GateKind;
use sequals::SequalsExtension;
use util::ContainerSizeGames;

mod constructors;
mod gate;
mod implemented;
mod sequals;
#[cfg(test)]
mod todo;
mod util;

/// A semantic alias for a boolean
pub type Bit = bool;

/// A circuit
///
/// use [`builder()`][Self::builder] to construct
///
/// use compute to run the logic
///
/// stick inside a [`Gate::component`] to use as part of a bigger component and
/// compose with other gates
///
/// put a gate implemented in rust (or an enum of them) in `<Rust>` to use it
/// (or them)
#[derive(Clone, Debug)]
pub struct Component<Rust = Infallible> {
    gates: Vec<Gate<Rust>>,
    sequals: Vec<Vec<Sequal>>,
    outputs: usize,
}

/// The constructor for [`Component`]
///
/// you create one with [`Component::builder()`]
///
/// use [`gate`][ComponentBuilder::gate] to add another inner gate
///
/// use [`inputs`][ComponentBuilder::inputs] to set the input locations and
/// finalize construction (get your final [`Component`])
///
/// put a gate implemented in rust (or an enum of them) in `<Rust>` to use it
/// (or them)
pub struct ComponentBuilder<Rust = Infallible> {
    gates: Vec<Gate<Rust>>,
    sequals: Vec<Vec<Sequal>>,
    outputs: usize,
}

/// A wire
///
/// connects the outputs of the current [`Gate`] (or the inputs of the
/// [`Component`]) to either the inputs of another gate or the outputs of the
/// containing component.
///
/// if you want to send an output to several places it must be done
/// indirectly, you should take a look at [`Gate::dup`]
#[derive(Clone, Copy, Debug)]
pub enum Sequal {
    Gate { index: usize, entry: usize },
    End { output: usize },
}

/// A gate, or rather a single node of a [`Component`]
///
/// put a gate implemented in rust (or an enum of them) in `<Rust>` to use it
/// (or them)
#[derive(Clone, Debug)]
pub struct Gate<Rust = Infallible> {
    kind: GateKind<Rust>,
    inputs: Vec<Bit>,
    inputs_filled: usize,
}

/// Implement a gate in rust
///
/// if you want to implement a custom [`Gate`] or a node of a [`Component`] in
/// rust code instead of as a component with logic (whether the goal is
/// efficiancy or you're just lazy, I won't judge) all you have to do is
/// implement this trait on your type and stick it in the generic `<Rust>`
/// parameter of your [`Component`], then construct the full gate with
/// [`Gate::rust`]
///
/// you yourself are not supposed to ever need to use the functions in this
/// trait
///
/// # Example
/// ```
/// use logic_circus::{Bit, Component, Gate, GateLike};
/// struct And;
///
/// impl GateLike for And {
///     fn compute(&mut self, input: Vec<Bit>) -> Vec<Bit> {
///         vec![input[0] && input[1]]
///     }
///     fn num_of_inputs(&self) -> usize {
///         2
///     }
/// }
///
/// let gate = Gate::rust(And);
/// let mut component = Component::single_gate(gate, 1);
/// assert_eq!(component.compute(vec![true, true]), vec![true]);
/// // note that I must `reset` the component in between each call to compute even
/// // though `And` doesn't implement the `reset` function, this is due to internal
/// // logic of the `compute` function
/// component.reset(false);
/// assert_eq!(component.compute(vec![true, false]), vec![false]);
/// ```
pub trait GateLike {
    /// This function computes the outputs of a gate for it's given inputs,
    /// these are in `Vec`s as a gate can have multiple inputs and outputs,
    /// for example an `And` gate has two inputs and one output
    ///
    /// you yourself are not supposed to ever need to use this function
    ///
    /// # Safety
    /// `input`s length must be equal to `num_of_inputs()`
    unsafe fn compute(&mut self, input: Vec<Bit>) -> Vec<Bit>;
    /// This function counts the amount of inputs a gate has
    ///
    /// you yourself are not supposed to ever need to use this function
    fn num_of_inputs(&self) -> usize;
    /// 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)
    fn reset(&mut self, full: bool) {
        let _ = full;
        // 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)
    }
}

impl<Rust> Component<Rust>
where
    Rust: GateLike,
{
    /// Runs the component
    ///
    /// This is an alias for [`compute`][GateLike::compute] since that function
    /// is inside a trait impl and thus way harder to find, also the name is
    /// nice
    ///
    /// # Safety
    /// this function is only safe if:
    /// - `input` is the same length as the last sequals vector
    /// - all `Sequal::End`s point to outputs that exist - guaranteed if
    ///   constructed with [`ComponentBuilder`] and used outputs from 0 up to
    ///   the number of outputs
    /// - all `Sequal::Gate`s must point to actual inputs of actual gates
    /// - component is clean (right after construction or after a call to
    ///   `Component::reset()`)
    pub unsafe fn run(&mut self, input: Vec<Bit>) -> Vec<Bit> {
        self.compute(input)
    }

    /// Resets the whole component
    ///
    /// this must be done in between cpu ticks (calls to compute) in order for
    /// the gates to work (otherwise the code is panicks in debug and is
    /// unsound in release)
    ///
    /// a full reset is done in between runs, this is to reset things like
    /// memory that are kept between ticks
    pub fn reset(&mut self, full_reset: bool) {
        for gate in &mut self.gates {
            gate.reset(full_reset)
        }
    }
}

impl<Rust> GateLike for Component<Rust>
where
    Rust: GateLike,
{
    /// # Safety
    /// this function is only safe if:
    /// - `input` is the same length as the last sequals vector
    /// - all `Sequal::End`s point to outputs that exist - guaranteed if
    ///   constructed with [`ComponentBuilder`] and used outputs from 0 up to
    ///   the number of outputs
    /// - all `Sequal::Gate`s must point to actual inputs of actual gates
    /// - component is clean (right after construction or after a call to
    ///   `Component::reset()`)
    unsafe fn compute(&mut self, input: Vec<Bit>) -> Vec<Bit> {
        let mut outputs = Vec::zeroed(self.outputs);

        // SAFETY: `sequals.len() - 1` must be in bounds for `sequals`
        self.sequals.run(
            self.sequals.len() - 1,
            &input,
            &mut self.gates,
            &mut outputs,
        );

        outputs
    }

    fn num_of_inputs(&self) -> usize {
        unsafe { self.sequals.get_unchecked(self.sequals.len() - 1) }.len()
    }
}

impl GateLike for Infallible {
    unsafe fn compute(&mut self, _input: Vec<Bit>) -> Vec<Bit> {
        unreachable!("can't compute result of infallible")
    }

    fn num_of_inputs(&self) -> usize {
        unreachable!("infallible has <???> inputs")
    }
}