use crate::gatesim::*;
use crate::xor_table::*;
use gategen2::boolvar::*;
use gategen2::dynintvar::*;
use static_init::dynamic;
use std::collections::HashMap;
use std::hash::Hash;
use std::str::FromStr;
const PERFECT_4INPUT_CIRCUITS: &'static str = include_str!("all_4bit_funcs.txt");
#[dynamic]
static PERFECT_4INPUT_CIRCUITS_LINES: Vec<&'static str> =
PERFECT_4INPUT_CIRCUITS.split('\n').collect::<Vec<_>>();
fn parse_perfect_circuit_line(line: &str) -> (u32, Vec<usize>, Circuit<usize>) {
let num_end = line.bytes().position(|x| !x.is_ascii_digit()).unwrap();
let value = line[0..num_end].parse().unwrap();
let mut line = line[num_end..].trim_start();
line = if line.starts_with(':') {
&line[1..]
} else {
panic!("Unknown data");
};
line = line.trim_start();
let mut inputs = vec![];
if line.starts_with('[') {
line = &line[1..];
loop {
line = line.trim_start();
if line.starts_with(']') {
line = &line[1..];
break;
}
let num_end = line.bytes().position(|x| !x.is_ascii_digit()).unwrap();
inputs.push(line[0..num_end].parse().unwrap());
line = line[num_end..].trim_start();
if line.starts_with(']') {
line = &line[1..];
break;
} else if line.starts_with(',') {
line = &line[1..];
}
}
} else {
panic!("Unknown data");
}
line = line.trim_start();
let circuit = Circuit::from_str(line).unwrap();
(value, inputs, circuit)
}
pub(crate) type CircuitCache = HashMap<u16, (u32, Vec<usize>, Circuit<usize>)>;
fn gen_perfect_expr(cache: &mut CircuitCache, value: u16, inputvar: &UDynVarSys) -> BoolVarSys {
if value == 0 {
false.into()
} else if value == 0xffff {
true.into()
} else {
let (_, inputs, circuit) = if let Some(t) = cache.get(&value) {
t.clone()
} else {
let t = parse_perfect_circuit_line(PERFECT_4INPUT_CIRCUITS_LINES[(value as usize) - 1]);
cache.insert(value, t.clone());
t
};
BoolVarSys::from_circuit(circuit, inputs.into_iter().map(|i| inputvar.bit(i)))[0].clone()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum TableCircuit {
Value(bool),
Circuit((Circuit<usize>, Vec<Option<usize>>)),
}
fn gen_perfect_circuit(cache: &mut CircuitCache, value: u16) -> TableCircuit {
if value == 0 {
TableCircuit::Value(false)
} else if value == 0xffff {
TableCircuit::Value(false)
} else {
let (_, inputs, circuit) = if let Some(t) = cache.get(&value) {
t.clone()
} else {
let t = parse_perfect_circuit_line(PERFECT_4INPUT_CIRCUITS_LINES[(value as usize) - 1]);
cache.insert(value, t.clone());
t
};
let mut out_inputs = vec![None; 4];
for (newi, i) in inputs.into_iter().enumerate() {
out_inputs[i] = Some(newi);
}
TableCircuit::Circuit((circuit, out_inputs))
}
}
fn gen_booltable_circuit_by_xor_table(cache: &mut CircuitCache, table: &[bool]) -> TableCircuit {
let table_len = table.len();
let table_len_bits = (usize::BITS - table_len.leading_zeros() - 1) as usize;
assert_eq!(table_len.count_ones(), 1);
if table.iter().all(|x| !*x) {
TableCircuit::Value(false)
} else if table.iter().all(|x| *x) {
TableCircuit::Value(true)
} else if table_len > 16 {
callsys(|| {
let mut perfect_expr_map = HashMap::<u16, BoolVarSys>::new();
let mut valtable = vec![0u16; table.len() >> 4];
for i in 0..table_len >> 4 {
let cur_val_table = &table[(i << 4)..(i << 4) + 16];
let value = cur_val_table
.into_iter()
.enumerate()
.fold(0u16, |a, (b, v)| a | (u16::from(*v) << b));
valtable[i] = value;
}
let mut xor_elem_outputs: Vec<u16> = vec![];
let mut temp_elem_outputs: Vec<Vec<u16>> = vec![];
type_extend_prep_xor_table(
&mut xor_elem_outputs,
&mut temp_elem_outputs,
valtable.iter().copied(),
);
let int_inputs = UDynVarSys::var(4); let ec = get_expr_creator_sys();
let expr_table = (0..table_len >> 4)
.map(|i| {
let value = xor_elem_outputs[i];
let expr = if let Some(expr) = perfect_expr_map.get(&value) {
expr.clone()
} else {
let expr = gen_perfect_expr(cache, value, &int_inputs);
perfect_expr_map.insert(value, expr.clone());
expr
};
expr.into()
})
.collect::<Vec<_>>();
TableCircuit::Circuit(gen_table_circuit_bool_prep(
ec,
int_inputs.iter().map(|v| v.into()).collect::<Vec<_>>(),
expr_table,
))
})
} else {
let mask = table_len - 1;
let value = (0..16).fold(0u16, |a, b| a | (u16::from(table[b & mask]) << b));
let mut out = gen_perfect_circuit(cache, value);
if let TableCircuit::Circuit((_, input)) = &mut out {
input.resize(table_len_bits, None);
}
out
}
}
#[repr(u8)]
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum PLACell {
#[default]
Zero,
Unknown,
One,
}
pub(crate) fn pla_entry_from_tokens(
var_num: usize,
line_no: usize,
tokens: &[String],
) -> Option<(Vec<PLACell>, bool, usize)> {
if tokens.len() < 2 {
if var_num == 0 {
let set_value = match tokens[0].chars().next() {
Some('0') => false,
Some('1') => true,
_ => {
return None;
}
};
return Some((vec![], set_value, line_no));
} else {
return None;
}
}
if let Some(entry) = tokens[0]
.chars()
.map(|c| match c {
'0' => Some(PLACell::Zero),
'-' => Some(PLACell::Unknown),
'1' => Some(PLACell::One),
_ => None,
})
.collect::<Option<Vec<_>>>()
{
if entry.len() != var_num {
return None;
};
let set_value = match tokens[1].chars().next() {
Some('0') => false,
Some('1') => true,
_ => {
return None;
}
};
Some((entry, set_value, line_no))
} else {
None
}
}
fn pla_to_truth_table(
var_num: usize,
set_value: bool,
pla: &[(Vec<PLACell>, bool, usize)],
) -> Vec<bool> {
assert!(var_num < (usize::BITS - 1) as usize);
let mut out_table = vec![!set_value; 1 << var_num];
for (entry, _, _) in pla {
assert_eq!(var_num, entry.len());
if entry.iter().any(|c| *c == PLACell::Unknown) {
let unknowns = entry
.iter()
.enumerate()
.filter_map(|(b, c)| {
if *c == PLACell::Unknown {
Some(b)
} else {
None
}
})
.collect::<Vec<_>>();
let mut index = entry.iter().enumerate().fold(0usize, |a, (b, c)| {
a | (usize::from(*c == PLACell::One) << b)
});
for _ in 0..1 << unknowns.len() {
out_table[index] = set_value;
for b in &unknowns {
index ^= 1 << b;
if ((index >> b) & 1) != 0 {
break;
}
}
}
} else {
let index = entry.iter().enumerate().fold(0usize, |a, (b, c)| {
a | (usize::from(*c == PLACell::One) << b)
});
out_table[index] = set_value;
}
}
out_table
}
fn gen_pla_table_circuit(
var_num: usize,
set_value: bool,
pla: &[(Vec<PLACell>, bool, usize)],
) -> TableCircuit {
callsys(|| {
let vars = UDynVarSys::var(var_num);
let mut whole_expr = BoolVar::from(!set_value);
for (entry, _, _) in pla {
assert_eq!(var_num, entry.len());
let entry_expr =
entry
.iter()
.enumerate()
.fold(BoolVarSys::from(set_value), |a, (b, c)| {
if *c != PLACell::Unknown {
let cv = *c == PLACell::One;
if set_value {
a & (vars.bit(b) ^ !cv)
} else {
a | (vars.bit(b) ^ cv)
}
} else {
a
}
});
if set_value {
whole_expr |= entry_expr;
} else {
whole_expr &= entry_expr;
}
}
if let Some(v) = whole_expr.value() {
TableCircuit::Value(v)
} else {
let (circuit, map) = whole_expr.to_translated_circuit_with_map(vars.iter());
TableCircuit::Circuit((circuit, map))
}
})
}
pub(crate) fn gen_pla_circuit_with_two_methods(
cache: &mut CircuitCache,
var_num: usize,
set_value: bool,
pla: &[(Vec<PLACell>, bool, usize)],
) -> TableCircuit {
if var_num <= 4 {
let table = pla_to_truth_table(var_num, set_value, pla);
gen_booltable_circuit_by_xor_table(cache, &table)
} else if var_num >= usize::BITS as usize - 1 {
gen_pla_table_circuit(var_num, set_value, pla)
} else {
let pla_total_gate_num = pla
.iter()
.map(|(e, _, _)| e.iter().filter(|c| **c != PLACell::Unknown).count())
.sum::<usize>();
if pla_total_gate_num >= (1 << var_num) / 7 {
let table = pla_to_truth_table(var_num, set_value, pla);
let table_circuit = gen_booltable_circuit_by_xor_table(cache, &table);
match &table_circuit {
TableCircuit::Circuit((circuit, _)) => {
if circuit.len() < pla_total_gate_num {
table_circuit
} else {
gen_pla_table_circuit(var_num, set_value, pla)
}
}
TableCircuit::Value(_) => table_circuit,
}
} else {
gen_pla_table_circuit(var_num, set_value, pla)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_perfect_circuit_line() {
assert_eq!(
(
31,
vec![0, 1, 2, 3],
Circuit::new(
4,
[
Gate::new_nor(0, 1),
Gate::new_nimpl(2, 4),
Gate::new_nor(3, 5),
],
[(6, false)]
)
.unwrap()
),
parse_perfect_circuit_line(PERFECT_4INPUT_CIRCUITS_LINES[30])
);
assert_eq!(
(
0xdada,
vec![0, 1, 2],
Circuit::new(
3,
[
Gate::new_and(0, 1),
Gate::new_xor(0, 2),
Gate::new_nor(3, 4),
],
[(5, true)]
)
.unwrap()
),
parse_perfect_circuit_line(PERFECT_4INPUT_CIRCUITS_LINES[0xdada - 1])
);
}
fn str_to_vecbool(s: &str) -> Vec<bool> {
s.chars()
.filter(|x| *x == '0' || *x == '1')
.map(|x| x == '1')
.collect()
}
fn check_circuit(exp_table: &[bool], table_circuit: TableCircuit) {
match table_circuit {
TableCircuit::Circuit((circuit, inputs)) => {
for (i, expv) in exp_table.into_iter().enumerate() {
let out = circuit.eval(
inputs
.iter()
.enumerate()
.filter_map(|(i, x)| (*x).map(|_| i))
.map(|b| ((i >> b) & 1) != 0),
);
assert_eq!(*expv, out[0], "{}", i);
}
}
TableCircuit::Value(val) => {
assert!(exp_table.iter().all(|x| *x == val));
}
}
}
fn gen_xor_booltable_circuit_and_check(s: &str) {
let mut cache = CircuitCache::new();
let table = str_to_vecbool(s);
let table_circuit = gen_booltable_circuit_by_xor_table(&mut cache, &table);
check_circuit(&table, table_circuit);
}
fn gen_xor_booltable_circuit_and_check_2(data: &[u64]) {
let mut cache = CircuitCache::new();
let table = data
.iter()
.map(|x| (0..64).map(|i| ((*x >> i) & 1) != 0))
.flatten()
.collect::<Vec<bool>>();
let table_circuit = gen_booltable_circuit_by_xor_table(&mut cache, &table);
if let TableCircuit::Circuit((circuit, _)) = &table_circuit {
println!("Circuit len: {}", circuit.len());
}
check_circuit(&table, table_circuit);
}
#[test]
fn test_gen_booltable_circuit_by_xor_table() {
gen_xor_booltable_circuit_and_check("0");
gen_xor_booltable_circuit_and_check("1");
gen_xor_booltable_circuit_and_check("00");
gen_xor_booltable_circuit_and_check("11");
gen_xor_booltable_circuit_and_check("0000");
gen_xor_booltable_circuit_and_check("1111");
gen_xor_booltable_circuit_and_check("00000000");
gen_xor_booltable_circuit_and_check("11111111");
gen_xor_booltable_circuit_and_check("00000000_00000000");
gen_xor_booltable_circuit_and_check("11111111_11111111");
gen_xor_booltable_circuit_and_check("00000000_00000000_00000000_00000000");
gen_xor_booltable_circuit_and_check("11111111_11111111_11111111_11111111");
gen_xor_booltable_circuit_and_check(&std::iter::repeat('0').take(1024).collect::<String>());
gen_xor_booltable_circuit_and_check(&std::iter::repeat('1').take(1024).collect::<String>());
gen_xor_booltable_circuit_and_check("01");
gen_xor_booltable_circuit_and_check("10");
gen_xor_booltable_circuit_and_check("0001");
gen_xor_booltable_circuit_and_check("0010");
gen_xor_booltable_circuit_and_check("0100");
gen_xor_booltable_circuit_and_check("1000");
gen_xor_booltable_circuit_and_check("0110");
gen_xor_booltable_circuit_and_check("1001");
gen_xor_booltable_circuit_and_check("0101");
gen_xor_booltable_circuit_and_check("1100");
gen_xor_booltable_circuit_and_check("01100100_01001101");
gen_xor_booltable_circuit_and_check("01100110_01000100");
gen_xor_booltable_circuit_and_check("01011111_10100101");
gen_xor_booltable_circuit_and_check("10111011_10111011");
gen_xor_booltable_circuit_and_check("00011010_01000100_01111001_01011010");
gen_xor_booltable_circuit_and_check("11001100_00110011_10101010_01010101");
gen_xor_booltable_circuit_and_check(concat!(
"00010101_00010101_11100110_11011110",
"11001011_10001000_11110111_11010111",
"00111001_11011001_00001000_00010111",
"11101101_01100010_01100010_11101111",
));
gen_xor_booltable_circuit_and_check(concat!(
"00000000_00000000_11111111_11111111",
"11111111_11111111_00000000_00000000",
"00000000_00000000_00000000_00000000",
"11111111_11111111_11111111_11111111",
));
let mut data = vec![0u64; 128];
data[0] = 0xfac0d0a0341acdab;
data[1] = 0x60da03491bc8da91;
data[2] = 0x57dd9840a894bc0d;
for i in 3..data.len() {
data[i] = data[i - 3].overflowing_mul(data[i - 2]).0 ^ data[i - 1];
}
gen_xor_booltable_circuit_and_check_2(&data);
}
fn pla_helper(strings: &[&str]) -> Vec<(Vec<PLACell>, bool, usize)> {
strings
.into_iter()
.map(|s| {
(
s.chars()
.map(|c| match c {
'0' => PLACell::Zero,
'-' => PLACell::Unknown,
'1' => PLACell::One,
_ => {
panic!("Unexpected");
}
})
.collect::<Vec<_>>(),
false,
0,
)
})
.collect::<Vec<_>>()
}
#[test]
fn test_pla_to_truth_table() {
assert_eq!(vec![false], pla_to_truth_table(0, true, &vec![]));
assert_eq!(
vec![true],
pla_to_truth_table(0, true, &vec![(vec![], false, 0)])
);
assert_eq!(vec![true], pla_to_truth_table(0, false, &vec![]));
assert_eq!(
vec![false],
pla_to_truth_table(0, false, &vec![(vec![], false, 0)])
);
assert_eq!(vec![false; 16], pla_to_truth_table(4, true, &vec![]));
assert_eq!(vec![true; 16], pla_to_truth_table(4, false, &vec![]));
assert_eq!(
str_to_vecbool("00000000_00000100"),
pla_to_truth_table(4, true, &pla_helper(&["1011"]))
);
assert_eq!(
str_to_vecbool("11111111_11111011"),
pla_to_truth_table(4, false, &pla_helper(&["1011"]))
);
assert_eq!(
str_to_vecbool("00001111_00000000"),
pla_to_truth_table(4, true, &pla_helper(&["--10"]))
);
assert_eq!(
str_to_vecbool("11110000_11111111"),
pla_to_truth_table(4, false, &pla_helper(&["--10"]))
);
assert_eq!(
str_to_vecbool("00000011_00000011"),
pla_to_truth_table(4, true, &pla_helper(&["-11-"]))
);
assert_eq!(
str_to_vecbool(concat!(
"00000000_00000000_00000000_00000000",
"00001010_00000000_00001010_00000000"
)),
pla_to_truth_table(6, true, &pla_helper(&["0-10-1"]))
);
assert_eq!(
str_to_vecbool(concat!(
"00000000_00000000_00000000_00000000",
"00000000_11110000_00000000_11110000"
)),
pla_to_truth_table(6, true, &pla_helper(&["--01-1"]))
);
assert_eq!(
str_to_vecbool(concat!(
"00000000_00000000_00000000_00000000",
"00000000_11110000_00000000_11110000"
)),
pla_to_truth_table(6, true, &pla_helper(&["--01-1"]))
);
assert_eq!(
str_to_vecbool(concat!(
"00000000_00000000_11001100_11001100",
"00000000_00000000_00000000_00000000",
"00000000_00000000_11001100_11001100",
"00000000_00000000_00000000_00000000",
"00000000_00000000_11001100_11001100",
"00000000_00000000_00000000_00000000",
"00000000_00000000_11001100_11001100",
"00000000_00000000_00000000_00000000",
)),
pla_to_truth_table(8, true, &pla_helper(&["-0--10--"]))
);
assert_eq!(
str_to_vecbool(concat!(
"00000000_01001111_01000000_01000000",
"00000000_01000000_00000000_01000000"
)),
pla_to_truth_table(6, true, &pla_helper(&["--1100", "1001--", "100-10"]))
);
}
fn gen_pla_table_circuit_and_check(var_num: usize, set_value: bool, pla_strings: &[&str]) {
let pla_table = pla_helper(pla_strings);
let table = pla_to_truth_table(var_num, set_value, &pla_table);
let table_circuit = gen_pla_table_circuit(var_num, set_value, &pla_table);
check_circuit(&table, table_circuit);
}
#[test]
fn test_gen_pla_table_circuit() {
gen_pla_table_circuit_and_check(0, true, &[]);
gen_pla_table_circuit_and_check(0, false, &[]);
gen_pla_table_circuit_and_check(4, true, &[]);
gen_pla_table_circuit_and_check(4, false, &[]);
gen_pla_table_circuit_and_check(0, true, &[""]);
gen_pla_table_circuit_and_check(0, false, &[""]);
gen_pla_table_circuit_and_check(1, true, &["0"]);
gen_pla_table_circuit_and_check(1, false, &["0"]);
gen_pla_table_circuit_and_check(1, true, &["1"]);
gen_pla_table_circuit_and_check(1, false, &["1"]);
gen_pla_table_circuit_and_check(1, true, &["-"]);
gen_pla_table_circuit_and_check(1, false, &["-"]);
gen_pla_table_circuit_and_check(4, true, &["1011"]);
gen_pla_table_circuit_and_check(4, false, &["1011"]);
gen_pla_table_circuit_and_check(4, true, &["--01"]);
gen_pla_table_circuit_and_check(4, false, &["--01"]);
gen_pla_table_circuit_and_check(6, true, &["0-10-1"]);
gen_pla_table_circuit_and_check(6, false, &["0-10-1"]);
gen_pla_table_circuit_and_check(8, true, &["-0--10--"]);
gen_pla_table_circuit_and_check(8, false, &["-0--10--"]);
gen_pla_table_circuit_and_check(6, true, &["--1100", "1001--", "100-10"]);
gen_pla_table_circuit_and_check(6, false, &["--1100", "1001--", "100-10"]);
gen_pla_table_circuit_and_check(
9,
true,
&["-1--101-1", "---11----", "00--11-00", "--1001-10"],
);
gen_pla_table_circuit_and_check(
9,
false,
&["-1--101-1", "---11----", "00--11-00", "--1001-10"],
);
gen_pla_table_circuit_and_check(
8,
true,
&[
"0011-111", "1010-10-", "01-101-0", "-0101-10", "100-11-1", "10--1100", "-01010-1",
],
);
gen_pla_table_circuit_and_check(
8,
false,
&[
"0011-111", "1010-10-", "01-101-0", "-0101-10", "100-11-1", "10--1100", "-01010-1",
],
);
gen_pla_table_circuit_and_check(
8,
true,
&[
"0011-111", "1010-10-", "01-101-0", "-0101-10", "100-11-1", "10--1100", "-01010-1",
"01011110", "01---101",
],
);
}
fn gen_pla_circuit_2m_and_check(var_num: usize, set_value: bool, pla_strings: &[&str]) {
let mut cache = CircuitCache::new();
let pla_table = pla_helper(pla_strings);
let table = pla_to_truth_table(var_num, set_value, &pla_table);
let table_circuit =
gen_pla_circuit_with_two_methods(&mut cache, var_num, set_value, &pla_table);
check_circuit(&table, table_circuit);
}
#[test]
fn test_gen_pla_circuit_with_two_methods() {
gen_pla_circuit_2m_and_check(0, true, &[]);
gen_pla_circuit_2m_and_check(0, false, &[]);
gen_pla_circuit_2m_and_check(4, true, &[]);
gen_pla_circuit_2m_and_check(4, false, &[]);
gen_pla_circuit_2m_and_check(0, true, &[""]);
gen_pla_circuit_2m_and_check(0, false, &[""]);
gen_pla_circuit_2m_and_check(1, true, &["0"]);
gen_pla_circuit_2m_and_check(1, false, &["0"]);
gen_pla_circuit_2m_and_check(1, true, &["1"]);
gen_pla_circuit_2m_and_check(1, false, &["1"]);
gen_pla_circuit_2m_and_check(1, true, &["-"]);
gen_pla_circuit_2m_and_check(1, false, &["-"]);
gen_pla_circuit_2m_and_check(4, true, &["1011"]);
gen_pla_circuit_2m_and_check(4, false, &["1011"]);
gen_pla_circuit_2m_and_check(4, true, &["--01"]);
gen_pla_circuit_2m_and_check(4, false, &["--01"]);
gen_pla_circuit_2m_and_check(6, true, &["0-10-1"]);
gen_pla_circuit_2m_and_check(6, false, &["0-10-1"]);
gen_pla_circuit_2m_and_check(8, true, &["-0--10--"]);
gen_pla_circuit_2m_and_check(8, false, &["-0--10--"]);
gen_pla_circuit_2m_and_check(6, true, &["--1100", "1001--", "100-10"]);
gen_pla_circuit_2m_and_check(
9,
true,
&["-1--101-1", "---11----", "00--11-00", "--1001-10"],
);
gen_pla_circuit_2m_and_check(
8,
true,
&[
"0011-111", "1010-10-", "01-101-0", "-0101-10", "100-11-1", "10--1100", "-01010-1",
],
);
gen_pla_circuit_2m_and_check(
8,
true,
&[
"0011-111", "1010-10-", "01-101-0", "-0101-10", "100-11-1", "10--1100", "-01010-1",
"01011110", "01---101",
],
);
gen_pla_circuit_2m_and_check(4, true, &["1-00", "-111", "10-0", "100-"]);
}
}