use core::fmt;
use std::cmp::Ordering;
use std::ops::BitAnd;
use std::ops::BitAndAssign;
use std::ops::BitOr;
use std::ops::BitOrAssign;
use std::ops::BitXor;
use std::ops::BitXorAssign;
use std::ops::Not;
use crate::bdd::*;
use crate::canonization::*;
use crate::decomposition::*;
use crate::operations::*;
use crate::Lut;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct StaticLut<const N: usize, const T: usize> {
table: [u64; T],
}
impl<const N: usize, const T: usize> Default for StaticLut<N, T> {
fn default() -> Self {
Self { table: [0u64; T] }
}
}
impl<const N: usize, const T: usize> StaticLut<N, T> {
pub fn num_vars(&self) -> usize {
N
}
pub fn num_bits(&self) -> usize {
1 << N
}
pub fn num_blocks(&self) -> usize {
table_size(N)
}
fn check_var(&self, ind: usize) {
assert!(ind < self.num_vars());
}
fn check_lut(&self, rhs: &Self) {
assert_eq!(self.num_vars(), rhs.num_vars());
}
fn check_bit(&self, ind: usize) {
assert!(ind < self.num_bits());
}
pub fn one() -> Self {
let mut ret = Self::default();
fill_one(N, ret.table.as_mut());
ret
}
pub fn zero() -> Self {
Self::default()
}
pub fn nth_var(var: usize) -> Self {
assert!(var < N);
let mut ret = Self::default();
fill_nth_var(N, ret.table.as_mut(), var);
ret
}
pub fn parity() -> Self {
let mut ret = Self::default();
fill_parity(N, ret.table.as_mut());
ret
}
pub fn majority() -> Self {
let mut ret = Self::default();
fill_majority(N, ret.table.as_mut());
ret
}
pub fn threshold(k: usize) -> Self {
let mut ret = Self::default();
fill_threshold(N, ret.table.as_mut(), k);
ret
}
pub fn equals(k: usize) -> Self {
let mut ret = Self::default();
fill_equals(N, ret.table.as_mut(), k);
ret
}
pub fn symmetric(count_values: usize) -> Self {
let mut ret = Self::default();
fill_symmetric(N, ret.table.as_mut(), count_values);
ret
}
#[cfg(feature = "rand")]
pub fn random() -> Self {
let mut ret = Self::default();
fill_random(N, ret.table.as_mut());
ret
}
pub fn value(&self, mask: usize) -> bool {
self.get_bit(mask)
}
pub fn get_bit(&self, mask: usize) -> bool {
self.check_bit(mask);
get_bit(N, self.table.as_ref(), mask)
}
pub fn set_value(&mut self, mask: usize, value: bool) {
if value {
self.set_bit(mask)
} else {
self.unset_bit(mask)
}
}
pub fn set_bit(&mut self, mask: usize) {
self.check_bit(mask);
set_bit(N, self.table.as_mut(), mask);
}
pub fn unset_bit(&mut self, mask: usize) {
self.check_bit(mask);
unset_bit(N, self.table.as_mut(), mask);
}
pub fn not_inplace(&mut self) {
not_inplace(N, self.table.as_mut());
}
pub fn and_inplace(&mut self, rhs: &Self) {
self.check_lut(rhs);
and_inplace(self.table.as_mut(), rhs.table.as_ref());
}
pub fn or_inplace(&mut self, rhs: &Self) {
self.check_lut(rhs);
or_inplace(self.table.as_mut(), rhs.table.as_ref());
}
pub fn xor_inplace(&mut self, rhs: &Self) {
self.check_lut(rhs);
xor_inplace(self.table.as_mut(), rhs.table.as_ref());
}
pub fn flip_inplace(&mut self, ind: usize) {
self.check_var(ind);
flip_inplace(N, self.table.as_mut(), ind);
}
pub fn swap_inplace(&mut self, ind1: usize, ind2: usize) {
self.check_var(ind1);
self.check_var(ind2);
swap_inplace(N, self.table.as_mut(), ind1, ind2);
}
pub fn swap_adjacent_inplace(&mut self, ind: usize) {
self.check_var(ind);
self.check_var(ind + 1);
swap_adjacent_inplace(N, self.table.as_mut(), ind);
}
pub fn not(&self) -> StaticLut<N, T> {
let mut l = *self;
l.not_inplace();
l
}
pub fn and(&self, rhs: &Self) -> Self {
let mut l = *self;
l.and_inplace(rhs);
l
}
pub fn or(&self, rhs: &Self) -> Self {
let mut l = *self;
l.or_inplace(rhs);
l
}
pub fn xor(&self, rhs: &Self) -> Self {
let mut l = *self;
l.xor_inplace(rhs);
l
}
pub fn flip(&self, ind: usize) -> Self {
let mut l = *self;
l.flip_inplace(ind);
l
}
pub fn swap(&self, ind1: usize, ind2: usize) -> Self {
let mut l = *self;
l.swap_inplace(ind1, ind2);
l
}
pub fn swap_adjacent(&mut self, ind: usize) -> Self {
let mut l = *self;
l.swap_adjacent_inplace(ind);
l
}
pub fn cofactors(&self, ind: usize) -> (Self, Self) {
let mut c = (*self, *self);
cofactor0_inplace(self.num_vars(), c.0.table.as_mut(), ind);
cofactor1_inplace(self.num_vars(), c.1.table.as_mut(), ind);
c
}
pub fn from_cofactors(c0: &Self, c1: &Self, ind: usize) -> Self {
let mut ret = Self::zero();
from_cofactors_inplace(
N,
ret.table.as_mut(),
c0.table.as_ref(),
c1.table.as_ref(),
ind,
);
ret
}
pub fn blocks(&self) -> &[u64] {
self.table.as_ref()
}
pub fn from_blocks(blocks: &[u64]) -> Self {
let mut ret = Self::default();
ret.table.clone_from_slice(blocks);
ret
}
pub fn p_canonization(&self) -> (Self, [u8; N]) {
let mut work = *self;
let mut ret = *self;
let mut perm = [0; N];
p_canonization(N, work.table.as_mut(), ret.table.as_mut(), perm.as_mut());
(ret, perm)
}
pub fn n_canonization(&self) -> (Self, u32) {
let mut work = *self;
let mut ret = *self;
let flip = n_canonization(N, work.table.as_mut(), ret.table.as_mut());
(ret, flip)
}
pub fn npn_canonization(&self) -> (Self, [u8; N], u32) {
let mut work = *self;
let mut ret = *self;
let mut perm = [0; N];
let flip = npn_canonization(N, work.table.as_mut(), ret.table.as_mut(), perm.as_mut());
(ret, perm, flip)
}
pub fn top_decomposition(&self, ind: usize) -> DecompositionType {
top_decomposition(N, self.table.as_ref(), ind)
}
pub fn is_pos_unate(&self, ind: usize) -> bool {
input_pos_unate(N, self.table.as_ref(), ind)
}
pub fn is_neg_unate(&self, ind: usize) -> bool {
input_neg_unate(N, self.table.as_ref(), ind)
}
pub fn all_functions() -> StaticLutIterator<N, T> {
StaticLutIterator {
lut: StaticLut::zero(),
ok: true,
}
}
pub fn bdd_complexity(luts: &[Self]) -> usize {
let mut table = Vec::<u64>::new();
for l in luts {
table.extend(l.blocks().iter());
}
table_complexity(N, table.as_slice())
}
pub fn to_hex_string(&self) -> String {
to_hex(self.num_vars(), self.table.as_ref())
}
pub fn to_bin_string(&self) -> String {
to_bin(self.num_vars(), self.table.as_ref())
}
pub fn from_hex_string(s: &str) -> Result<Self, ()> {
let mut ret = Self::zero();
fill_hex(ret.num_vars(), ret.table.as_mut(), s)?;
Ok(ret)
}
}
#[doc(hidden)]
pub struct StaticLutIterator<const N: usize, const T: usize> {
lut: StaticLut<N, T>,
ok: bool,
}
impl<const N: usize, const T: usize> Iterator for StaticLutIterator<N, T> {
type Item = StaticLut<N, T>;
fn next(&mut self) -> Option<Self::Item> {
if !self.ok {
None
} else {
let ret = self.lut;
self.ok = next_inplace(N, self.lut.table.as_mut());
Some(ret)
}
}
}
impl<const N: usize, const T: usize> Ord for StaticLut<N, T> {
fn cmp(&self, other: &Self) -> Ordering {
return cmp(self.table.as_ref(), other.table.as_ref());
}
}
impl<const N: usize, const T: usize> PartialOrd for StaticLut<N, T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<const N: usize, const T: usize> Not for StaticLut<N, T> {
type Output = Self;
fn not(self) -> Self::Output {
let mut l = self;
l.not_inplace();
l
}
}
impl<const N: usize, const T: usize> Not for &StaticLut<N, T> {
type Output = StaticLut<N, T>;
fn not(self) -> Self::Output {
let mut l = *self;
l.not_inplace();
l
}
}
impl<const N: usize, const T: usize> BitAndAssign<StaticLut<N, T>> for StaticLut<N, T> {
fn bitand_assign(&mut self, rhs: Self) {
and_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitAndAssign<&StaticLut<N, T>> for StaticLut<N, T> {
fn bitand_assign(&mut self, rhs: &Self) {
and_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitAnd<StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
let mut l = self;
l &= rhs;
l
}
}
impl<const N: usize, const T: usize> BitAnd<&StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitand(self, rhs: &StaticLut<N, T>) -> Self::Output {
let mut l = self;
l &= rhs;
l
}
}
impl<const N: usize, const T: usize> BitAnd<StaticLut<N, T>> for &StaticLut<N, T> {
type Output = StaticLut<N, T>;
fn bitand(self, rhs: StaticLut<N, T>) -> Self::Output {
let mut l = *self;
l &= rhs;
l
}
}
impl<const N: usize, const T: usize> BitAnd<&StaticLut<N, T>> for &StaticLut<N, T> {
type Output = StaticLut<N, T>;
fn bitand(self, rhs: &StaticLut<N, T>) -> Self::Output {
let mut l = *self;
l &= rhs;
l
}
}
impl<const N: usize, const T: usize> BitOrAssign<StaticLut<N, T>> for StaticLut<N, T> {
fn bitor_assign(&mut self, rhs: Self) {
or_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitOrAssign<&StaticLut<N, T>> for StaticLut<N, T> {
fn bitor_assign(&mut self, rhs: &Self) {
or_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitOr<StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
let mut l = self;
l |= rhs;
l
}
}
impl<const N: usize, const T: usize> BitOr<&StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitor(self, rhs: &StaticLut<N, T>) -> Self::Output {
let mut l = self;
l |= rhs;
l
}
}
impl<const N: usize, const T: usize> BitOr<StaticLut<N, T>> for &StaticLut<N, T> {
type Output = StaticLut<N, T>;
fn bitor(self, rhs: StaticLut<N, T>) -> Self::Output {
let mut l = *self;
l |= rhs;
l
}
}
impl<const N: usize, const T: usize> BitOr<&StaticLut<N, T>> for &StaticLut<N, T> {
type Output = StaticLut<N, T>;
fn bitor(self, rhs: &StaticLut<N, T>) -> Self::Output {
let mut l = *self;
l |= rhs;
l
}
}
impl<const N: usize, const T: usize> BitXorAssign<StaticLut<N, T>> for StaticLut<N, T> {
fn bitxor_assign(&mut self, rhs: Self) {
xor_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitXorAssign<&StaticLut<N, T>> for StaticLut<N, T> {
fn bitxor_assign(&mut self, rhs: &Self) {
xor_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitXor<StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self::Output {
let mut l = self;
l ^= rhs;
l
}
}
impl<const N: usize, const T: usize> BitXor<&StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitxor(self, rhs: &StaticLut<N, T>) -> Self::Output {
let mut l = self;
l ^= rhs;
l
}
}
impl<const N: usize, const T: usize> BitXor<StaticLut<N, T>> for &StaticLut<N, T> {
type Output = StaticLut<N, T>;
fn bitxor(self, rhs: StaticLut<N, T>) -> Self::Output {
let mut l = *self;
l ^= rhs;
l
}
}
impl<const N: usize, const T: usize> BitXor<&StaticLut<N, T>> for &StaticLut<N, T> {
type Output = StaticLut<N, T>;
fn bitxor(self, rhs: &StaticLut<N, T>) -> Self::Output {
let mut l = *self;
l ^= rhs;
l
}
}
impl<const N: usize, const T: usize> fmt::Display for StaticLut<N, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_hex(N, self.table.as_ref(), f)
}
}
impl<const N: usize, const T: usize> fmt::LowerHex for StaticLut<N, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_hex(N, self.table.as_ref(), f)
}
}
impl<const N: usize, const T: usize> fmt::Binary for StaticLut<N, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_bin(N, self.table.as_ref(), f)
}
}
impl<const N: usize, const T: usize> TryFrom<Lut> for StaticLut<N, T> {
type Error = ();
fn try_from(lut: Lut) -> Result<Self, Self::Error> {
if lut.num_vars() != N {
return Err(());
}
Ok(StaticLut::<N, T>::from_blocks(lut.blocks()))
}
}
impl<const N: usize, const T: usize> From<StaticLut<N, T>> for Lut {
fn from(lut: StaticLut<N, T>) -> Lut {
Lut::from_blocks(lut.num_vars(), lut.blocks())
}
}
pub type Lut0 = StaticLut<0, 1>;
pub type Lut1 = StaticLut<1, 1>;
pub type Lut2 = StaticLut<2, 1>;
pub type Lut3 = StaticLut<3, 1>;
pub type Lut4 = StaticLut<4, 1>;
pub type Lut5 = StaticLut<5, 1>;
pub type Lut6 = StaticLut<6, 1>;
pub type Lut7 = StaticLut<7, 2>;
pub type Lut8 = StaticLut<8, 4>;
pub type Lut9 = StaticLut<9, 8>;
pub type Lut10 = StaticLut<10, 16>;
pub type Lut11 = StaticLut<11, 32>;
pub type Lut12 = StaticLut<12, 64>;
impl From<u8> for Lut3 {
fn from(table: u8) -> Lut3 {
Lut3::from_blocks(&[table as u64])
}
}
impl From<u16> for Lut4 {
fn from(table: u16) -> Lut4 {
Lut4::from_blocks(&[table as u64])
}
}
impl From<u32> for Lut5 {
fn from(table: u32) -> Lut5 {
Lut5::from_blocks(&[table as u64])
}
}
impl From<u64> for Lut6 {
fn from(table: u64) -> Lut6 {
Lut6::from_blocks(&[table])
}
}
impl From<Lut3> for u8 {
fn from(lut: Lut3) -> u8 {
(lut.table[0] & !VAR_MASK[3]) as u8
}
}
impl From<Lut4> for u16 {
fn from(lut: Lut4) -> u16 {
(lut.table[0] & !VAR_MASK[4]) as u16
}
}
impl From<Lut5> for u32 {
fn from(lut: Lut5) -> u32 {
(lut.table[0] & !VAR_MASK[5]) as u32
}
}
impl From<Lut6> for u64 {
fn from(lut: Lut6) -> u64 {
lut.table[0]
}
}
#[cfg(test)]
mod tests {
use crate::{Lut0, Lut1, Lut2, Lut3, Lut4, Lut5, Lut6, Lut7};
#[test]
fn test_symmetric() {
assert_eq!(Lut3::majority().to_string(), "Lut3(e8)");
assert_eq!(Lut4::majority().to_string(), "Lut4(fee8)");
assert_eq!(Lut3::equals(0).to_string(), "Lut3(01)");
assert_eq!(Lut4::equals(0).to_string(), "Lut4(0001)");
assert_eq!(Lut3::equals(1).to_string(), "Lut3(16)");
assert_eq!(Lut4::equals(1).to_string(), "Lut4(0116)");
assert_eq!(Lut3::parity().to_string(), "Lut3(96)");
assert_eq!(Lut4::parity().to_string(), "Lut4(6996)");
}
#[test]
fn test_display() {
assert_eq!(format!("{:}", Lut0::zero()), "Lut0(0)");
assert_eq!(format!("{:}", Lut0::one()), "Lut0(1)");
assert_eq!(format!("{:}", Lut1::zero()), "Lut1(0)");
assert_eq!(format!("{:}", Lut1::one()), "Lut1(3)");
assert_eq!(format!("{:}", Lut2::zero()), "Lut2(0)");
assert_eq!(format!("{:}", Lut2::one()), "Lut2(f)");
assert_eq!(format!("{:}", Lut3::zero()), "Lut3(00)");
assert_eq!(format!("{:}", Lut3::one()), "Lut3(ff)");
assert_eq!(format!("{:}", Lut4::zero()), "Lut4(0000)");
assert_eq!(format!("{:}", Lut4::one()), "Lut4(ffff)");
assert_eq!(format!("{:}", Lut5::zero()), "Lut5(00000000)");
assert_eq!(format!("{:}", Lut5::one()), "Lut5(ffffffff)");
assert_eq!(format!("{:}", Lut6::zero()), "Lut6(0000000000000000)");
assert_eq!(format!("{:}", Lut6::one()), "Lut6(ffffffffffffffff)");
assert_eq!(
format!("{:}", Lut7::zero()),
"Lut7(00000000000000000000000000000000)"
);
assert_eq!(
format!("{:}", Lut7::one()),
"Lut7(ffffffffffffffffffffffffffffffff)"
);
}
#[test]
#[allow(
unused_must_use,
clippy::no_effect,
clippy::unnecessary_operation,
clippy::op_ref
)]
fn test_ops() {
let mut a = Lut6::nth_var(0);
let b = Lut6::nth_var(1);
!a;
!&a;
&a & &b;
&a & b;
a & &b;
a & b;
&a | &b;
&a | b;
a | &b;
a | b;
&a ^ &b;
&a ^ b;
a ^ &b;
a ^ b;
a &= b;
a &= &b;
a |= b;
a |= &b;
a ^= b;
a ^= &b;
}
#[test]
#[cfg(feature = "rand")]
fn test_random() {
Lut0::random();
Lut1::random();
Lut2::random();
Lut3::random();
Lut4::random();
Lut5::random();
Lut6::random();
Lut7::random();
}
#[test]
#[cfg(feature = "rand")]
fn test_lut_conversion() {
use crate::{Lut, Lut7};
for _ in 0..10 {
let lut = Lut3::random();
assert_eq!(lut, Lut3::try_from(Lut::from(lut)).unwrap());
}
for _ in 0..10 {
let lut = Lut7::random();
assert_eq!(lut, Lut7::try_from(Lut::from(lut)).unwrap());
}
}
#[test]
#[cfg(feature = "rand")]
fn test_int_conversion() {
for _ in 0..10 {
let lut = Lut3::random();
assert_eq!(lut, Lut3::from(u8::from(lut)));
}
for _ in 0..10 {
let lut = Lut4::random();
assert_eq!(lut, Lut4::from(u16::from(lut)));
}
for _ in 0..10 {
let lut = Lut5::random();
assert_eq!(lut, Lut5::from(u32::from(lut)));
}
for _ in 0..10 {
let lut = Lut6::random();
assert_eq!(lut, Lut6::from(u64::from(lut)));
}
}
#[test]
#[cfg(feature = "rand")]
fn test_string_conversion() {
use crate::Lut8;
for _ in 0..10 {
let lut = Lut0::random();
assert_eq!(lut, Lut0::from_hex_string(&lut.to_hex_string()).unwrap());
}
for _ in 0..10 {
let lut = Lut1::random();
assert_eq!(lut, Lut1::from_hex_string(&lut.to_hex_string()).unwrap());
}
for _ in 0..10 {
let lut = Lut2::random();
assert_eq!(lut, Lut2::from_hex_string(&lut.to_hex_string()).unwrap());
}
for _ in 0..10 {
let lut = Lut3::random();
assert_eq!(lut, Lut3::from_hex_string(&lut.to_hex_string()).unwrap());
}
for _ in 0..10 {
let lut = Lut8::random();
assert_eq!(lut, Lut8::from_hex_string(&lut.to_hex_string()).unwrap());
}
}
}