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::canonization::n_canonization;
use crate::canonization::npn_canonization;
use crate::canonization::p_canonization;
use crate::operations::*;
#[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
}
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
}
pub fn get_bit(&self, mask: usize) -> bool {
self.check_bit(mask);
get_bit(N, self.table.as_ref(), 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 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 decomposition(&self, ind: usize) -> DecompositionType {
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,
}
}
}
#[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 for StaticLut<N, T> {
fn bitand_assign(&mut self, rhs: Self) {
and_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<'a, const N: usize, const T: usize> BitAndAssign<&'a StaticLut<N, T>> for StaticLut<N, T> {
fn bitand_assign(&mut self, rhs: &'a Self) {
and_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitAnd for StaticLut<N, T> {
type Output = Self;
fn bitand(self, rhs: Self) -> Self::Output {
let mut l = self;
l &= rhs;
l
}
}
impl<'a, const N: usize, const T: usize> BitAnd<&'a StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitand(self, rhs: &'a StaticLut<N, T>) -> Self::Output {
let mut l = self;
l &= rhs;
l
}
}
impl<const N: usize, const T: usize> BitOrAssign for StaticLut<N, T> {
fn bitor_assign(&mut self, rhs: Self) {
or_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<'a, const N: usize, const T: usize> BitOrAssign<&'a StaticLut<N, T>> for StaticLut<N, T> {
fn bitor_assign(&mut self, rhs: &'a Self) {
or_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitOr for StaticLut<N, T> {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
let mut l = self;
l |= rhs;
l
}
}
impl<'a, const N: usize, const T: usize> BitOr<&'a StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitor(self, rhs: &'a StaticLut<N, T>) -> Self::Output {
let mut l = self;
l |= rhs;
l
}
}
impl<const N: usize, const T: usize> BitXorAssign for StaticLut<N, T> {
fn bitxor_assign(&mut self, rhs: Self) {
xor_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<'a, const N: usize, const T: usize> BitXorAssign<&'a StaticLut<N, T>> for StaticLut<N, T> {
fn bitxor_assign(&mut self, rhs: &'a Self) {
xor_inplace(self.table.as_mut(), rhs.table.as_ref());
}
}
impl<const N: usize, const T: usize> BitXor for StaticLut<N, T> {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self::Output {
let mut l = self;
l ^= rhs;
l
}
}
impl<'a, const N: usize, const T: usize> BitXor<&'a StaticLut<N, T>> for StaticLut<N, T> {
type Output = Self;
fn bitxor(self, rhs: &'a 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)
}
}
#[doc(hidden)]
pub type Lut0 = StaticLut<0, 1>;
#[doc(hidden)]
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>;
#[cfg(test)]
mod tests {
use crate::{Lut0, Lut1, Lut2, Lut3, Lut4};
#[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)");
}
}