infmachine_gen 0.1.1

The Infinite Machine generator library.
Documentation
#![cfg_attr(docsrs, feature(doc_cfg))]
//! ## InfMachine Gen
//!
//! This library provides utilities to generate Infinite Machines.
//! The Infinite Machine model is described in documentation of `infmachine` crate.
//! This library uses `gategen` library to generate and create circuits.
//! This library uses `intvar` and `dynintvar` object to holds variables.
//! Main structure of that library is `InfParMachineObject` that holds
//! input and outputs of machine's circuit.
//! After filling all circuit outputs it possible to create machine description
//! from that object.
//!
//! Examples of usage that library in crate `infmachine_examples`.
//!
//! More information in `infmachine` crate.

use gate_calc_log_bits::*;
use gategen::boolvar::*;
use gategen::dynintvar::*;
use gategen::generic_array::typenum::*;
use gategen::intvar::*;
use gategen::*;
use infmachine_config::*;

use std::fmt::Debug;
use std::ops::Neg;

pub use gategen;
pub use gategen::gatesim;
pub use gategen::gateutil;
pub use infmachine_config;

/// Constant for data part move. For do nothing.
pub const DPMOVE_NOTHING: u8 = 0;
/// Constant for data part move. For move forward.
pub const DPMOVE_FORWARD: u8 = 1;
/// Constant for data part move. For move backward.
pub const DPMOVE_BACKWARD: u8 = 2;
/// Constant for data part move. For move backward.
pub const DPMOVE_BACKWARD_2: u8 = 3; // this same as DPMOVE_BACKWARD

/// Constant for data kind. For memory address.
pub const DKIND_MEM_ADDRESS: u8 = 0;
/// Constant for data kind. For temp buffer.
pub const DKIND_TEMP_BUFFER: u8 = 1;
/// Constant for data kind. For processor id.
pub const DKIND_PROC_ID: u8 = 2;
/// Constant for data kind. For processor id.
pub const DKIND_PROC_ID_2: u8 = 3; // this same as DKIND_PROC_ID

/// Structure holds circuit input variables.
pub struct InfParInput<T: VarLit> {
    /// Internal state.
    pub state: DynIntVar<T, false>,
    /// Read main memory cell value.
    pub memval: DynIntVar<T, false>,
    /// Read data (internal data) part value.
    pub dpval: DynIntVar<T, false>,
    /// Data movement (if true if move done).
    pub dp_move_done: BoolVar<T>,
}

impl<T: VarLit> InfParInput<T>
where
    T: VarLit + Neg<Output = T> + Debug,
    isize: TryFrom<T>,
    <T as TryInto<usize>>::Error: Debug,
    <T as TryFrom<usize>>::Error: Debug,
    <isize as TryFrom<T>>::Error: Debug,
{
    /// Returns interface configuration.
    pub fn config(&self) -> InfParInterfaceConfig {
        InfParInterfaceConfig {
            cell_len_bits: u32::try_from(calc_log_bits(self.memval.bitnum())).unwrap(),
            data_part_len: u32::try_from(self.dpval.bitnum()).unwrap(),
        }
    }
}

/// Main structure describes circuit using `gategen` variables.
pub struct InfParMachineObject<T: VarLit> {
    config: InfParInterfaceConfig,
    env_config: InfParEnvConfig,
    /// Input internal state.
    pub in_state: Option<DynIntVar<T, false>>,
    /// Input read main memory cell value.
    pub in_memval: DynIntVar<T, false>,
    /// Input read data part value.
    pub in_dpval: DynIntVar<T, false>,
    /// Input data movement (true if move done).
    pub in_dp_move_done: BoolVar<T>,
    /// Output internal state.
    pub out_state: Option<DynIntVar<T, false>>,
    /// Output main memory cell value to write to memory.
    pub out_memval: Option<DynIntVar<T, false>>,
    /// Output data part to write into internal data (memory address or temp buffer).
    pub out_dpval: Option<DynIntVar<T, false>>,
    /// Output memory read bit. If true then machine reads memory.
    pub memr: Option<BoolVar<T>>,
    /// Output memory write bit. If true then machine writes into memory.
    pub memw: Option<BoolVar<T>>,
    /// Output data part read bit. If true then machine reads internal data.
    pub dpr: Option<BoolVar<T>>,
    /// Output data part write bit. If true then machine writes internal data.
    pub dpw: Option<BoolVar<T>>,
    /// Output data part move. Set move of position for internal data.
    pub dpmove: Option<IntVar<T, U2, false>>,
    /// Output data kind. Specifies internal data kind that operates machine.
    pub dkind: Option<IntVar<T, U2, false>>,
    /// Output stop machine indicator. If true then machine stops.
    pub stop: Option<BoolVar<T>>,
}

impl<T> InfParMachineObject<T>
where
    T: VarLit + Neg<Output = T> + Debug,
    isize: TryFrom<T>,
    <T as TryInto<usize>>::Error: Debug,
    <T as TryFrom<usize>>::Error: Debug,
    <isize as TryFrom<T>>::Error: Debug,
{
    /// Sets outputs from given `dynintvar` from argument.
    pub fn from_dynintvar(&mut self, var: DynIntVar<T, false>) {
        let cell_len = 1 << self.config.cell_len_bits;
        let state_len = var.bitnum() - cell_len - (self.config.data_part_len as usize) - 9;
        let vars = var.subvalues(
            0,
            [
                state_len,
                cell_len,
                self.config.data_part_len as usize,
                2,
                2,
                2,
                2,
                1,
            ],
        );
        self.out_state = Some(vars[0].clone());
        self.out_memval = Some(vars[1].clone());
        self.out_dpval = Some(vars[2].clone());
        self.memr = Some(vars[3].bit(0));
        self.memw = Some(vars[3].bit(1));
        self.dpr = Some(vars[4].bit(0));
        self.dpw = Some(vars[4].bit(1));
        self.dpmove = Some(vars[5].clone().try_into().unwrap());
        self.dkind = Some(vars[6].clone().try_into().unwrap());
        self.stop = Some(vars[7].bit(0));
    }

    /// Sets outputs from given output (InfParOutput).
    pub fn from_output(&mut self, output: InfParOutput<T>) {
        assert_eq!(output.memval.bitnum(), 1 << self.config.cell_len_bits);
        assert_eq!(output.dpval.bitnum(), self.config.data_part_len as usize);
        self.out_state = Some(output.state);
        self.out_memval = Some(output.memval);
        self.out_dpval = Some(output.dpval);
        self.memr = Some(output.memr);
        self.memw = Some(output.memw);
        self.dpr = Some(output.dpr);
        self.dpw = Some(output.dpw);
        self.dpmove = Some(output.dpmove);
        self.dkind = Some(output.dkind);
        self.stop = Some(output.stop);
    }

    /// Returns inputs.
    pub fn input(&self) -> InfParInput<T> {
        InfParInput {
            state: self.in_state.clone().unwrap(),
            memval: self.in_memval.clone(),
            dpval: self.in_dpval.clone(),
            dp_move_done: self.in_dp_move_done.clone(),
        }
    }
}

macro_rules! inf_par_machine_object_impl {
    ($tp:ty, $tp2:ty) => {
        impl InfParMachineObject<$tp> {
            /// Creates machine object from `config` configuration and
            /// `env_config` environment configuration.
            /// All outputs are not filled.
            pub fn new(config: InfParInterfaceConfig, env_config: InfParEnvConfig) -> Self {
                config.valid().unwrap();
                env_config.valid().unwrap();
                Self {
                    config,
                    env_config,
                    in_state: None,
                    in_memval: DynIntVar::<$tp, false>::var(1 << config.cell_len_bits),
                    in_dpval: DynIntVar::<$tp, false>::var(config.data_part_len as usize),
                    in_dp_move_done: BoolVar::<$tp>::var(),
                    out_state: None,
                    out_memval: None,
                    out_dpval: None,
                    memr: None,
                    memw: None,
                    dpr: None,
                    dpw: None,
                    dpmove: None,
                    dkind: None,
                    stop: None,
                }
            }

            /// Make machine data (configuration and its circuit).
            pub fn to_machine(self) -> InfParMachineData<$tp2> {
                let in_state_len = u32::try_from(self.in_state.as_ref().unwrap().bitnum()).unwrap();
                let out_state_len =
                    u32::try_from(self.out_state.as_ref().unwrap().bitnum()).unwrap();
                assert_eq!(in_state_len, out_state_len);
                InfParMachineData {
                    config: (self.config, out_state_len).into(),
                    env_config: self.env_config,
                    circuit: self
                        .out_state
                        .unwrap()
                        .concat(self.out_memval.unwrap())
                        .concat(self.out_dpval.unwrap())
                        .concat(DynIntVar::<$tp, false>::from_iter([
                            self.memr.unwrap(),
                            self.memw.unwrap(),
                            self.dpr.unwrap(),
                            self.dpw.unwrap(),
                            self.dpmove.as_ref().unwrap().bit(0),
                            self.dpmove.as_ref().unwrap().bit(1),
                            self.dkind.as_ref().unwrap().bit(0),
                            self.dkind.as_ref().unwrap().bit(1),
                            self.stop.unwrap(),
                        ]))
                        .to_translated_and_filled_circuit(
                            self.in_state
                                .unwrap()
                                .concat(self.in_memval)
                                .concat(self.in_dpval)
                                .concat(DynIntVar::<$tp, false>::filled(1, self.in_dp_move_done))
                                .iter(),
                        ),
                }
            }
        }
    };
}

inf_par_machine_object_impl!(i16, u16);
inf_par_machine_object_impl!(i32, u32);
inf_par_machine_object_impl!(isize, usize);

/// Structure that describes machine circuit outputs.
#[derive(Clone)]
pub struct InfParOutput<T: VarLit> {
    /// Output internal state.
    pub state: DynIntVar<T, false>,
    /// Output main memory cell value to write to memory.
    pub memval: DynIntVar<T, false>,
    /// Output data part to write into internal data (memory address or temp buffer).
    pub dpval: DynIntVar<T, false>,
    /// Output memory read bit. If true then machine reads memory.
    pub memr: BoolVar<T>,
    /// Output memory write bit. If true then machine writes into memory.
    pub memw: BoolVar<T>,
    /// Output data part read bit. If true then machine reads internal data.
    pub dpr: BoolVar<T>,
    /// Output data part write bit. If true then machine writes internal data.
    pub dpw: BoolVar<T>,
    /// Output data part move. Set move of position for internal data.
    pub dpmove: IntVar<T, U2, false>,
    /// Output data kind. Specifies internal data kind that operates machine.
    pub dkind: IntVar<T, U2, false>,
    /// Output stop machine indicator. If true then machine stops.
    pub stop: BoolVar<T>,
}

macro_rules! inf_par_output_impl {
    ($tp:ty) => {
        impl InfParOutput<$tp> {
            /// Creates circuit output object from interface configuration.
            /// All outputs are filled by default values.
            pub fn new(config: InfParInterfaceConfig) -> Self {
                config.valid().unwrap();
                Self {
                    state: DynIntVar::<$tp, false>::from_n(0u32, 1),
                    memval: DynIntVar::<$tp, false>::from_n(0u32, 1 << config.cell_len_bits),
                    dpval: DynIntVar::<$tp, false>::from_n(0u32, config.data_part_len as usize),
                    memr: BoolVar::<$tp>::from(false),
                    memw: BoolVar::<$tp>::from(false),
                    dpr: BoolVar::<$tp>::from(false),
                    dpw: BoolVar::<$tp>::from(false),
                    dpmove: IntVar::<$tp, U2, false>::from(0u32),
                    dkind: IntVar::<$tp, U2, false>::from(0u32),
                    stop: BoolVar::<$tp>::from(false),
                }
            }

            /// Fix internal state length from list of other circuit output objects.
            pub fn fix_state_len(list: &mut [Self]) {
                let max = list
                    .iter()
                    .map(|po| po.state.bitnum())
                    .max()
                    .unwrap_or_default();
                for po in list {
                    if po.state.bitnum() < max {
                        po.state = po.state.clone().concat(DynIntVar::<$tp, false>::filled(
                            max - po.state.bitnum(),
                            BoolVar::from(false),
                        ));
                    }
                }
            }

            /// Creates new from `dynintvar`. `config` is interface configuration.
            /// `var` is variable of all outputs.
            pub fn new_from_dynintvar(
                config: InfParInterfaceConfig,
                var: DynIntVar<$tp, false>,
            ) -> Self {
                let cell_len = 1 << config.cell_len_bits;
                let state_len = var.bitnum() - cell_len - (config.data_part_len as usize) - 9;
                let vars = var.subvalues(
                    0,
                    [
                        state_len,
                        cell_len,
                        config.data_part_len as usize,
                        2,
                        2,
                        2,
                        2,
                        1,
                    ],
                );
                Self {
                    state: vars[0].clone(),
                    memval: vars[1].clone(),
                    dpval: vars[2].clone(),
                    memr: vars[3].bit(0),
                    memw: vars[3].bit(1),
                    dpr: vars[4].bit(0),
                    dpw: vars[4].bit(1),
                    dpmove: vars[5].clone().try_into().unwrap(),
                    dkind: vars[6].clone().try_into().unwrap(),
                    stop: vars[7].bit(0),
                }
            }
        }
    };
}

inf_par_output_impl!(i16);
inf_par_output_impl!(i32);
inf_par_output_impl!(isize);

impl<T> InfParOutput<T>
where
    T: VarLit + Neg<Output = T> + Debug,
    isize: TryFrom<T>,
    <T as TryInto<usize>>::Error: Debug,
    <T as TryFrom<usize>>::Error: Debug,
    <isize as TryFrom<T>>::Error: Debug,
{
    /// Converts to `dynintvar` variable.
    pub fn to_dynintvar(self) -> DynIntVar<T, false> {
        self.state
            .concat(self.memval)
            .concat(self.dpval)
            .concat(DynIntVar::<T, false>::from_iter([
                self.memr,
                self.memw,
                self.dpr,
                self.dpw,
                self.dpmove.bit(0),
                self.dpmove.bit(1),
                self.dkind.bit(0),
                self.dkind.bit(1),
                self.stop,
            ]))
    }
}

/// Circuit input type for circuit with 16-bit type.
pub type InfParInput16 = InfParInput<i16>;
/// Circuit input type for circuit with 32-bit type.
pub type InfParInput32 = InfParInput<i32>;
/// Circuit input type for circuit with system type.
pub type InfParInputSys = InfParInput<isize>;

// Machine object type for circuit with 16-bit type.
pub type InfParMachineObject16 = InfParMachineObject<i16>;
// Machine object type for circuit with 32-bit type.
pub type InfParMachineObject32 = InfParMachineObject<i32>;
// Machine object type for circuit with system type.
pub type InfParMachineObjectSys = InfParMachineObject<isize>;

/// Circuit output type for circuit with 16-bit type.
pub type InfParOutput16 = InfParOutput<i16>;
/// Circuit output type for circuit with 32-bit type.
pub type InfParOutput32 = InfParOutput<i32>;
/// Circuit output type for circuit with system type.
pub type InfParOutputSys = InfParOutput<isize>;