libreda_logic/
native_boolean_functions.rs

1// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5//! Implement the [`BooleanSystem`] trait for native Rust functions.
6
7use super::traits::*;
8
9impl<F> NumInputs for F
10where
11    F: Fn(bool, bool) -> bool,
12{
13    fn num_inputs(&self) -> usize {
14        2
15    }
16}
17impl<F> NumOutputs for F
18where
19    F: Fn(bool, bool) -> bool,
20{
21    fn num_outputs(&self) -> usize {
22        1
23    }
24}
25/// Implement the boolean system for two-input boolean functions.
26impl<F> PartialBooleanSystem for F
27where
28    F: Fn(bool, bool) -> bool,
29{
30    type LiteralId = usize;
31
32    type TermId = ();
33
34    fn evaluate_term_partial(&self, _term: &Self::TermId, input_values: &[bool]) -> Option<bool> {
35        Some((self)(input_values[0], input_values[1]))
36    }
37}
38
39/// Implement the boolean system for two-input boolean functions.
40impl<F> BooleanSystem for F
41where
42    F: Fn(bool, bool) -> bool + PartialBooleanSystem<LiteralId = usize>,
43{
44    fn evaluate_term(&self, _term: &Self::TermId, input_values: &[bool]) -> bool {
45        (self)(input_values[0], input_values[1])
46    }
47}
48
49// Would be nice but somehow does not work:
50//impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> PartialBooleanSystem for F where
51//    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS]
52//{
53//}
54
55//impl<const NUM_INPUTS: usize> PartialBooleanSystem for dyn Fn([bool; NUM_INPUTS]) -> bool {
56//    type LiteralId = usize;
57//
58//    type TermId = ();
59//
60//    fn evaluate_partial(
61//        &self,
62//        term: &Self::TermId,
63//        input_values: &dyn Fn(&Self::LiteralId) -> bool,
64//    ) -> Option<bool> {
65//        let mut inputs = [false; NUM_INPUTS];
66//        // Fetch input values.
67//        (0..NUM_INPUTS).for_each(|i| inputs[i] = input_values(&i));
68//        Some((self)(inputs))
69//    }
70//}
71
72/// A boolean system which is implemented by a Rust function.
73pub struct NativeBooleanFunction<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize>
74where
75    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
76{
77    f: F,
78}
79
80impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> From<F>
81    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
82where
83    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
84{
85    fn from(f: F) -> Self {
86        Self::new(f)
87    }
88}
89
90impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> StaticNumInputs<NUM_INPUTS>
91    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
92where
93    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
94{
95}
96
97impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> StaticNumOutputs<NUM_OUTPUTS>
98    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
99where
100    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
101{
102}
103
104impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize>
105    NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
106where
107    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
108{
109    /// Create a new boolean system from a native boolean function.
110    /// # Example
111    /// ```
112    /// use libreda_logic::traits::*;
113    /// use libreda_logic::native_boolean_functions::*;
114    ///
115    /// let f = NativeBooleanFunction::new(|[a, b, c]| [a ^ b ^ c]);
116    ///
117    /// assert_eq!(true, f.evaluate_term(&0, &[true, true, true]));
118    /// assert_eq!(false, f.evaluate_term(&0, &[true, true, false]));
119    /// ```
120    pub fn new(f: F) -> Self {
121        Self { f }
122    }
123}
124
125impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> NumInputs
126    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
127where
128    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
129{
130    fn num_inputs(&self) -> usize {
131        NUM_INPUTS
132    }
133}
134
135impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> NumOutputs
136    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
137where
138    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
139{
140    fn num_outputs(&self) -> usize {
141        NUM_OUTPUTS
142    }
143}
144
145impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> PartialBooleanSystem
146    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
147where
148    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
149{
150    type LiteralId = usize;
151
152    type TermId = usize;
153
154    fn evaluate_term_partial(&self, term: &Self::TermId, input_values: &[bool]) -> Option<bool> {
155        Some(self.evaluate_term(term, input_values))
156    }
157}
158
159impl<F, const NUM_INPUTS: usize, const NUM_OUTPUTS: usize> BooleanSystem
160    for NativeBooleanFunction<F, NUM_INPUTS, NUM_OUTPUTS>
161where
162    F: Fn([bool; NUM_INPUTS]) -> [bool; NUM_OUTPUTS],
163{
164    fn evaluate_term(&self, term: &Self::TermId, input_values: &[bool]) -> bool {
165        let mut inputs = [false; NUM_INPUTS];
166        // Fetch input values.
167        (0..NUM_INPUTS).for_each(|i| inputs[i] = input_values[i]);
168        (self.f)(inputs)[*term]
169    }
170}
171
172#[test]
173fn test_2x2_function() {
174    let f = |a: bool, b: bool| -> bool { a ^ b };
175
176    let result = f.evaluate_term(&(), &[true, true]);
177
178    assert_eq!(result, false);
179}
180
181#[test]
182fn test_evaluate_nary_function() {
183    let f = NativeBooleanFunction::new(|[a, b, c]| [a ^ b ^ c]);
184    let result = f.evaluate_term(&0, &[true, true, true]);
185    assert_eq!(result, true);
186}