libreda-logic 0.0.3

Logic library for LibrEDA.
Documentation
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Implement the [`BooleanSystem`] trait for native Rust functions.

use super::traits::*;

impl<F> NumInputs for F
where
    F: Fn(bool, bool) -> bool,
{
    fn num_inputs(&self) -> usize {
        2
    }
}
impl<F> NumOutputs for F
where
    F: Fn(bool, bool) -> bool,
{
    fn num_outputs(&self) -> usize {
        1
    }
}
/// Implement the boolean system for two-input boolean functions.
impl<F> PartialBooleanSystem for F
where
    F: Fn(bool, bool) -> bool,
{
    type LiteralId = usize;

    type TermId = ();

    fn evaluate_term_partial(&self, _term: &Self::TermId, input_values: &[bool]) -> Option<bool> {
        Some((self)(input_values[0], input_values[1]))
    }
}

/// Implement the boolean system for two-input boolean functions.
impl<F> BooleanSystem for F
where
    F: Fn(bool, bool) -> bool + PartialBooleanSystem<LiteralId = usize>,
{
    fn evaluate_term(&self, _term: &Self::TermId, input_values: &[bool]) -> bool {
        (self)(input_values[0], input_values[1])
    }
}

// Would be nice but somehow does not work:
//impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> PartialBooleanSystem for F where
//    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS]
//{
//}

//impl<const NUM_INPUTS: usize> PartialBooleanSystem for dyn Fn([bool; NUM_INPUTS]) -> bool {
//    type LiteralId = usize;
//
//    type TermId = ();
//
//    fn evaluate_partial(
//        &self,
//        term: &Self::TermId,
//        input_values: &dyn Fn(&Self::LiteralId) -> bool,
//    ) -> Option<bool> {
//        let mut inputs = [false; NUM_INPUTS];
//        // Fetch input values.
//        (0..NUM_INPUTS).for_each(|i| inputs[i] = input_values(&i));
//        Some((self)(inputs))
//    }
//}

/// A boolean system which is implemented by a Rust function.
pub struct NativeBooleanFunction<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
    f: F,
}

impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> From<F>
    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
    fn from(f: F) -> Self {
        Self::new(f)
    }
}

impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> StaticNumInputs<NUM_INPUTS>
    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
}

impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> StaticNumOutputs<NUM_OUTPUTS>
    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
}

impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize>
    NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
    /// Create a new boolean system from a native boolean function.
    /// # Example
    /// ```
    /// use libreda_logic::traits::*;
    /// use libreda_logic::native_boolean_functions::*;
    ///
    /// let f = NativeBooleanFunction::new(|[a, b, c]| [a ^ b ^ c]);
    ///
    /// assert_eq!(true, f.evaluate_term(&0, &[true, true, true]));
    /// assert_eq!(false, f.evaluate_term(&0, &[true, true, false]));
    /// ```
    pub fn new(f: F) -> Self {
        Self { f }
    }
}

impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> NumInputs
    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
    fn num_inputs(&self) -> usize {
        NUM_INPUTS
    }
}

impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> NumOutputs
    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
    fn num_outputs(&self) -> usize {
        NUM_OUTPUTS
    }
}

impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> PartialBooleanSystem
    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
    type LiteralId = usize;

    type TermId = usize;

    fn evaluate_term_partial(&self, term: &Self::TermId, input_values: &[bool]) -> Option<bool> {
        Some(self.evaluate_term(term, input_values))
    }
}

impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> BooleanSystem
    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
where
    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
{
    fn evaluate_term(&self, term: &Self::TermId, input_values: &[bool]) -> bool {
        let mut inputs = [false; NUM_INPUTS];
        // Fetch input values.
        (0..NUM_INPUTS).for_each(|i| inputs[i] = input_values[i]);
        (self.f)(inputs)[*term]
    }
}

#[test]
fn test_2x2_function() {
    let f = |a: bool, b: bool| -> bool { a ^ b };

    let result = f.evaluate_term(&(), &[true, true]);

    assert_eq!(result, false);
}

#[test]
fn test_evaluate_nary_function() {
    let f = NativeBooleanFunction::new(|[a, b, c]| [a ^ b ^ c]);
    let result = f.evaluate_term(&0, &[true, true, true]);
    assert_eq!(result, true);
}