#[must_use]
pub fn possible_truth_values(n: usize) -> Vec<Vec<bool>> {
if n == 0 {
return vec![vec![]];
}
let mut truth_values = Vec::new();
for i in (0..(2 << (n - 1))).rev() {
let mut truth_value = Vec::new();
for j in (0..n).rev() {
truth_value.push((i >> j) % 2 == 1);
}
truth_values.push(truth_value);
}
truth_values
}
#[macro_export]
macro_rules! truth_table {
(|$($atomic:ident),*| => { $($rest:tt)* } ) => {{
truth_table!(@muncher |$($atomic),*|; {$($rest)*} )
}};
(|$($atomic:ident),*| => $proposition:ident -> $body:expr) => {{
let $proposition = | $($atomic: bool),* | -> bool {
$body
};
truth_table!(@muncher |$($atomic),*| $proposition; {} )
}};
(|$($atomic:ident),*| => $proposition:ident) => {{
truth_table!(@muncher |$($atomic),*| $proposition; {} )
}};
(@muncher |$($atomic:ident),*| $($previous:ident),*; { $next:ident -> $body:expr, $($rest:tt)* }) => {{
let $next = | $($atomic: bool),* | -> bool {
$body
};
truth_table!(
@muncher
|$($atomic),*|
$($previous,)* $next;
{
$($rest)*
}
)
}};
(@muncher |$($atomic:ident),*| $($previous:ident),*; { $next:ident, $($rest:tt)* }) => {{
truth_table!(
@muncher
|$($atomic),*|
$($previous,)* $next;
{$($rest)*}
)
}};
(@muncher |$($atomic:ident),*| $($previous:ident),*; { $next:ident -> $body:expr }) => {{
let $next = | $($atomic: bool),* | -> bool {
$body
};
truth_table!(
@muncher
|$($atomic),*|
$($previous,)* $next;
{}
)
}};
(@muncher |$($atomic:ident),*| $($previous:ident),*; { $next:ident }) => {{
truth_table!(
@muncher
|$($atomic),*|
$($previous,)* $next;
{}
)
}};
(@muncher |$($atomic:ident),*| $($compound_proposition:ident),*; {}) => {{
use ::std::convert::identity;
use ::cli_table::{format::Justify, Cell, CellStruct, Style, Table};
let mut number_of_atomic_propositions = 0;
$(
number_of_atomic_propositions += 1;
let _ = identity(stringify!($atomic)); )*
let atomic_rows: Vec<Vec<bool>> = possible_truth_values(number_of_atomic_propositions);
let table = atomic_rows.iter().cloned().map(|atomics| {
let mut i = 0;
$(let $atomic = atomics[i]; i += 1;)*
let _ = i;
let mut row: Vec<CellStruct> = atomics.iter().map(|atomic| atomic.cell().justify(Justify::Center)).collect();
let run_proposition = |proposition: fn($($atomic: bool),*) -> bool |-> bool {proposition($($atomic),*)};
$(
row.push(run_proposition($compound_proposition).cell().justify(Justify::Right));
)*
row
}).collect::<Vec<Vec<_>>>().table().title(vec![
$(
stringify!($atomic).cell().bold(true),
)*
$(
stringify!($compound_proposition).cell().bold(true),
)*
]).bold(true);
table
}};
}
#[macro_export]
macro_rules! print_truth_table {
(|$($atomic:ident),*| => {
$($rest:tt)*
}) => {{
let table = truth_table!(|$($atomic),*| => { $($rest)* } );
assert!(::cli_table::print_stdout(table).is_ok());
}};
(|$($atomic:ident),*| => $prop:ident -> $body:expr) => {{
let table = truth_table!(|$($atomic),*| => $prop -> {$body} );
assert!(::cli_table::print_stdout(table).is_ok());
}};
(|$($atomic:ident),*| => $prop:ident) => {{
let table = truth_table!(|$($atomic),*| => $prop );
assert!(::cli_table::print_stdout(table).is_ok());
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_possible_truth_values_dimensions() {
for i in 0..20 {
let truth_values = possible_truth_values(i);
assert_eq!(truth_values.len(), 2usize.pow(i as u32));
for truth_value in truth_values {
assert_eq!(truth_value.len(), i);
}
}
}
#[test]
fn test_possible_truth_values() {
let expected = vec![
vec![true, true, true, true],
vec![true, true, true, false],
vec![true, true, false, true],
vec![true, true, false, false],
vec![true, false, true, true],
vec![true, false, true, false],
vec![true, false, false, true],
vec![true, false, false, false],
vec![false, true, true, true],
vec![false, true, true, false],
vec![false, true, false, true],
vec![false, true, false, false],
vec![false, false, true, true],
vec![false, false, true, false],
vec![false, false, false, true],
vec![false, false, false, false],
];
assert_eq!(possible_truth_values(4), expected);
let expected = vec![
vec![true, true, true],
vec![true, true, false],
vec![true, false, true],
vec![true, false, false],
vec![false, true, true],
vec![false, true, false],
vec![false, false, true],
vec![false, false, false],
];
assert_eq!(possible_truth_values(3), expected);
let expected = vec![
vec![true, true],
vec![true, false],
vec![false, true],
vec![false, false],
];
assert_eq!(possible_truth_values(2), expected);
let expected = vec![vec![true], vec![false]];
assert_eq!(possible_truth_values(1), expected);
let expected = vec![vec![]];
assert_eq!(possible_truth_values(0), expected);
}
}