abc/src/map/if/acd/kitty_operations.hpp

337 lines
9.2 KiB
C++

#ifndef _KITTY_OPERATIONS_TT_H_
#define _KITTY_OPERATIONS_TT_H_
#pragma once
#include <algorithm>
#include <cassert>
#include <functional>
#include <iterator>
#include <optional>
#include <iostream>
#include "kitty_algorithm.hpp"
#include "kitty_constants.hpp"
#include "kitty_dynamic_tt.hpp"
#include "kitty_static_tt.hpp"
ABC_NAMESPACE_CXX_HEADER_START
namespace kitty
{
/*! Inverts all bits in a truth table, based on a condition */
template<typename TT>
inline TT unary_not_if( const TT& tt, bool cond )
{
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
const auto mask = -static_cast<uint64_t>( cond );
#ifdef _MSC_VER
#pragma warning( pop )
#endif
return unary_operation( tt, [mask]( auto a )
{ return a ^ mask; } );
}
/*! \brief Inverts all bits in a truth table */
template<typename TT>
inline TT unary_not( const TT& tt )
{
return unary_operation( tt, []( auto a )
{ return ~a; } );
}
/*! \brief Bitwise AND of two truth tables */
template<typename TT>
inline TT binary_and( const TT& first, const TT& second )
{
return binary_operation( first, second, std::bit_and<>() );
}
/*! \brief Bitwise OR of two truth tables */
template<typename TT>
inline TT binary_or( const TT& first, const TT& second )
{
return binary_operation( first, second, std::bit_or<>() );
}
/*! \brief Swaps two variables in a truth table
The function swaps variable `var_index1` with `var_index2`. The
function will change `tt` in-place. If `tt` should not be changed,
one can use `swap` instead.
\param tt Truth table
\param var_index1 First variable
\param var_index2 Second variable
*/
template<typename TT>
void swap_inplace( TT& tt, uint8_t var_index1, uint8_t var_index2 )
{
if ( var_index1 == var_index2 )
{
return;
}
if ( var_index1 > var_index2 )
{
std::swap( var_index1, var_index2 );
}
if ( tt.num_vars() <= 6 )
{
const auto& pmask = detail::ppermutation_masks[var_index1][var_index2];
const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 );
tt._bits[0] = ( tt._bits[0] & pmask[0] ) | ( ( tt._bits[0] & pmask[1] ) << shift ) | ( ( tt._bits[0] & pmask[2] ) >> shift );
}
else if ( var_index2 <= 5 )
{
const auto& pmask = detail::ppermutation_masks[var_index1][var_index2];
const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 );
std::transform( std::begin( tt._bits ), std::end( tt._bits ), std::begin( tt._bits ),
[shift, &pmask]( uint64_t word )
{
return ( word & pmask[0] ) | ( ( word & pmask[1] ) << shift ) | ( ( word & pmask[2] ) >> shift );
} );
}
else if ( var_index1 <= 5 ) /* in this case, var_index2 > 5 */
{
const auto step = 1 << ( var_index2 - 6 );
const auto shift = 1 << var_index1;
auto it = std::begin( tt._bits );
while ( it != std::end( tt._bits ) )
{
for ( auto i = decltype( step ){ 0 }; i < step; ++i )
{
const auto low_to_high = ( *( it + i ) & detail::projections[var_index1] ) >> shift;
const auto high_to_low = ( *( it + i + step ) << shift ) & detail::projections[var_index1];
*( it + i ) = ( *( it + i ) & ~detail::projections[var_index1] ) | high_to_low;
*( it + i + step ) = ( *( it + i + step ) & detail::projections[var_index1] ) | low_to_high;
}
it += 2 * step;
}
}
else
{
const auto step1 = 1 << ( var_index1 - 6 );
const auto step2 = 1 << ( var_index2 - 6 );
auto it = std::begin( tt._bits );
while ( it != std::end( tt._bits ) )
{
for ( auto i = 0; i < step2; i += 2 * step1 )
{
for ( auto j = 0; j < step1; ++j )
{
std::swap( *( it + i + j + step1 ), *( it + i + j + step2 ) );
}
}
it += 2 * step2;
}
}
}
/*! \brief Extends smaller truth table to larger one
The most significant variables will not be in the functional support of the
resulting truth table, but the method is helpful to align a truth table when
being used with another one.
\param tt Larger truth table to create
\param from Smaller truth table to copy from
*/
template<typename TT, typename TTFrom>
void extend_to_inplace( TT& tt, const TTFrom& from )
{
assert( tt.num_vars() >= from.num_vars() );
if ( from.num_vars() < 6 )
{
auto mask = *from.begin();
for ( auto i = from.num_vars(); i < std::min<uint8_t>( 6, tt.num_vars() ); ++i )
{
mask |= ( mask << ( 1 << i ) );
}
std::fill( tt.begin(), tt.end(), mask );
}
else
{
auto it = tt.begin();
while ( it != tt.end() )
{
it = std::copy( from.cbegin(), from.cend(), it );
}
}
}
/*! \brief Extends smaller truth table to larger static one
This is an out-of-place version of `extend_to_inplace` that has the truth
table as a return value. It only works for creating static truth tables. The
template parameter `NumVars` must be equal or larger to the number of
variables in `from`.
\param from Smaller truth table to copy from
*/
template<uint32_t NumVars, typename TTFrom>
inline static_truth_table<NumVars> extend_to( const TTFrom& from )
{
static_truth_table<NumVars> tt;
extend_to_inplace( tt, from );
return tt;
}
/*! \brief Checks whether truth table depends on given variable index
\param tt Truth table
\param var_index Variable index
*/
template<typename TT>
bool has_var( const TT& tt, uint8_t var_index )
{
assert( var_index < tt.num_vars() );
if ( tt.num_vars() <= 6 || var_index < 6 )
{
return std::any_of( std::begin( tt._bits ), std::end( tt._bits ),
[var_index]( uint64_t word )
{ return ( ( word >> ( uint64_t( 1 ) << var_index ) ) & detail::projections_neg[var_index] ) !=
( word & detail::projections_neg[var_index] ); } );
}
const auto step = 1 << ( var_index - 6 );
for ( auto i = 0u; i < static_cast<uint32_t>( tt.num_blocks() ); i += 2 * step )
{
for ( auto j = 0; j < step; ++j )
{
if ( tt._bits[i + j] != tt._bits[i + j + step] )
{
return true;
}
}
}
return false;
}
/*! \brief Checks whether truth table depends on given variable index
\param tt Truth table
\param care Care set
\param var_index Variable index
*/
template<typename TT>
bool has_var( const TT& tt, const TT& care, uint8_t var_index )
{
assert( var_index < tt.num_vars() );
assert( tt.num_vars() == care.num_vars() );
if ( tt.num_vars() <= 6 || var_index < 6 )
{
auto it_tt = std::begin( tt._bits );
auto it_care = std::begin( care._bits );
while ( it_tt != std::end( tt._bits ) )
{
if ( ( ( ( *it_tt >> ( uint64_t( 1 ) << var_index ) ) ^ *it_tt ) & detail::projections_neg[var_index]
& ( *it_care >> ( uint64_t( 1 ) << var_index ) ) & *it_care ) != 0 )
{
return true;
}
++it_tt;
++it_care;
}
return false;
}
const auto step = 1 << ( var_index - 6 );
for ( auto i = 0u; i < static_cast<uint32_t>( tt.num_blocks() ); i += 2 * step )
{
for ( auto j = 0; j < step; ++j )
{
if ( ( ( tt._bits[i + j] ^ tt._bits[i + j + step] ) & care._bits[i + j] & care._bits[i + j + step] ) != 0 )
{
return true;
}
}
}
return false;
}
/*! \brief Shrinks larger truth table to smaller one
The function expects that the most significant bits, which are cut off, are
not in the functional support of the original function. Only then it is
ensured that the resulting function is equivalent.
\param tt Smaller truth table to create
\param from Larger truth table to copy from
*/
template<typename TT, typename TTFrom>
void shrink_to_inplace( TT& tt, const TTFrom& from )
{
assert( tt.num_vars() <= from.num_vars() );
std::copy( from.begin(), from.begin() + tt.num_blocks(), tt.begin() );
if ( tt.num_vars() < 6 )
{
tt.mask_bits();
}
}
/*! \brief Shrinks larger truth table to smaller dynamic one
This is an out-of-place version of `shrink_to` that has the truth table as a
return value. It only works for creating dynamic tables. The parameter
`num_vars` must be equal or smaller to the number of variables in `from`.
\param from Smaller truth table to copy from
*/
template<typename TTFrom>
inline dynamic_truth_table shrink_to( const TTFrom& from, unsigned num_vars )
{
auto tt = create<dynamic_truth_table>( num_vars );
shrink_to_inplace( tt, from );
return tt;
}
/*! \brief Prints truth table in hexadecimal representation
The most-significant bit will be the first character of the string.
\param tt Truth table
\param os Output stream
*/
template<typename TT>
void print_hex( const TT& tt, std::ostream& os = std::cout )
{
auto const chunk_size =
std::min<uint64_t>( tt.num_vars() <= 1 ? 1 : ( tt.num_bits() >> 2 ), 16 );
for_each_block_reversed( tt, [&os, chunk_size]( auto word )
{
std::string chunk( chunk_size, '0' );
auto it = chunk.rbegin();
while (word && it != chunk.rend()) {
auto hex = word & 0xf;
if (hex < 10) {
*it = '0' + static_cast<char>(hex);
} else {
*it = 'a' + static_cast<char>(hex - 10);
}
++it;
word >>= 4;
}
os << chunk; } );
}
} //namespace kitty
ABC_NAMESPACE_CXX_HEADER_END
#endif // _KITTY_OPERATIONS_TT_H_