diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 346875e5..f3524363 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -1,7 +1,7 @@ file(GLOB FILENAMES *.cpp) # No debugging and full optimization for benchmark test -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNODEBUG") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -DNDEBUG") set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Enable testing of the benchmark library." FORCE) set(CMAKE_BUILD_TYPE "Release") diff --git a/bench/canonization.cpp b/bench/canonization.cpp new file mode 100644 index 00000000..75f389a7 --- /dev/null +++ b/bench/canonization.cpp @@ -0,0 +1,55 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include + +using namespace kitty; + +void BM_exact_npn_canonization_static( benchmark::State& state ) +{ + while ( state.KeepRunning() ) + { + static_truth_table<6> tt; + create_from_hex_string( tt, "17cad20f55459c3f" ); + exact_npn_canonization( tt ); + } +} + +void BM_exact_npn_canonization_dynamic( benchmark::State& state ) +{ + while ( state.KeepRunning() ) + { + dynamic_truth_table tt( 6 ); + create_from_hex_string( tt, "17cad20f55459c3f" ); + exact_npn_canonization( tt ); + } +} + +BENCHMARK( BM_exact_npn_canonization_static ); +BENCHMARK( BM_exact_npn_canonization_dynamic ); + +BENCHMARK_MAIN() diff --git a/bench/operations.cpp b/bench/operations.cpp index fdfbb96a..d88b4460 100644 --- a/bench/operations.cpp +++ b/bench/operations.cpp @@ -105,6 +105,24 @@ void BM_bitwise_majority_func_dynamic( benchmark::State& state ) } } +void BM_unary_not_ite_dynamic( benchmark::State& state ) +{ + while ( state.KeepRunning() ) + { + dynamic_truth_table tt( state.range( 0 ) ); + const auto tt_new = !get_bit( tt, 0 ) ? unary_not( tt ) : tt; + } +} + +void BM_unary_not_if_dynamic( benchmark::State& state ) +{ + while ( state.KeepRunning() ) + { + dynamic_truth_table tt( state.range( 0 ) ); + const auto tt_new = unary_not_if( tt, get_bit( tt, 0 ) ); + } +} + BENCHMARK_TEMPLATE( BM_bitwise_and_lambda_static, 5 ); BENCHMARK_TEMPLATE( BM_bitwise_and_lambda_static, 7 ); BENCHMARK_TEMPLATE( BM_bitwise_and_lambda_static, 9 ); @@ -123,4 +141,7 @@ BENCHMARK( BM_bitwise_and_std_dynamic )->Arg( 5 )->Arg( 7 )->Arg( 9 ); BENCHMARK( BM_bitwise_and_func_dynamic )->Arg( 5 )->Arg( 7 )->Arg( 9 ); BENCHMARK( BM_bitwise_majority_func_dynamic )->Arg( 5 )->Arg( 7 )->Arg( 9 ); +BENCHMARK( BM_unary_not_ite_dynamic )->Arg( 5 )->Arg( 7 )->Arg( 9 ); +BENCHMARK( BM_unary_not_if_dynamic )->Arg( 5 )->Arg( 7 )->Arg( 9 ); + BENCHMARK_MAIN() diff --git a/docs/changelog.rst b/docs/changelog.rst index a2dc46bc..55922349 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,25 +8,37 @@ v0.1 * Data structures ``static_truth_table`` and ``dynamic_truth_table`` `#1 `_ -* Bit-access functions ``set_bit`` and ``get_bit`` +* Bit functions: ``set_bit``, ``get_bit``, ``clear_bit``, ``clear`` `#1 `_ + `#8 `_ * Constructors ``create_nth_var``, ``create_from_binary_string``, ``create_from_hex_string``, and ``create_majority`` `#1 `_ `#4 `_ `#5 `_ -* Unary and binary operations: ``unary_not``, ``binary_and``, ``binary_or``, and ``binary_xor`` +* Unary and binary operations: ``unary_not``, ``unary_not_if``, ``binary_and``, ``binary_or``, and ``binary_xor`` `#2 `_ + `#8 `_ * Ternary operations: ``ternary_majority`` and ``ternary_ite`` `#3 `_ -* Binary predicates: ``equal`` +* Binary predicates: ``equal``, ``less_than`` `#4 `_ + `#8 `_ + +* Operators: ``~``, ``==``, ``<`` + `#8 `_ * Swap adjacent variables: ``swap_adjacent_inplace``, ``swap_adjacent`` `#6 `_ +* Swap variables: ``swap_inplace``, ``swap`` + `#8 `_ + * Flip variable: ``flip_inplace``, ``flip`` `#7 `_ + +* NPN canonization: ``exact_npn_canonization``, ``create_from_npn_config`` + `#8 `_ diff --git a/docs/reference.rst b/docs/reference.rst index fd0122dd..cdbe119c 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -36,3 +36,13 @@ Operations .. doxygenfile:: operations.hpp +Operators +--------- + +.. doxygenfile:: operators.hpp + +Canonization +------------ + +.. doxygenfile:: canonization.hpp + diff --git a/include/kitty/bit_operations.hpp b/include/kitty/bit_operations.hpp index 6f838fba..0d48f836 100644 --- a/include/kitty/bit_operations.hpp +++ b/include/kitty/bit_operations.hpp @@ -92,4 +92,22 @@ void clear_bit( static_truth_table& tt, uint64_t index ) tt._bits &= ~( uint64_t( 1 ) << index ); } /*! \endcond */ + +/*! Clears all bits. + + \param tt Truth table +*/ +template +void clear( TT& tt ) +{ + std::fill( std::begin( tt._bits ), std::end( tt._bits ), 0 ); +} + +/*! \cond PRIVATE */ +template +void clear( static_truth_table& tt ) +{ + tt._bits = 0; +} +/*! \endcond */ } // namespace kitty diff --git a/include/kitty/canonization.hpp b/include/kitty/canonization.hpp new file mode 100644 index 00000000..06954584 --- /dev/null +++ b/include/kitty/canonization.hpp @@ -0,0 +1,194 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include + +#include "detail/constants.hpp" +#include "operators.hpp" + +namespace kitty +{ + +/*! Exact NPN canonization + + Given a truth table, this function finds the lexicographically + smallest truth table in its NPN class, called NPN representative. + Two functions are in the same NPN class, if one can obtain one from + the other by input negation, input permutation, and output negation. + + The function returns a NPN configuration which contains the + necessary transformations to obtain the representative. It is a + tuple of + + - the NPN representative + - input negations and output negation, output negation is stored as + bit *n*, where *n* is the number of variables in `tt` + - input permutation to apply + + \param tt The truth table + \return NPN configuration +*/ +template +std::tuple> exact_npn_canonization( const TT& tt ) +{ + const auto num_vars = tt.num_vars(); + + /* Special case for n = 0 */ + if ( num_vars == 0 ) + { + const auto bit = get_bit( tt, 0 ); + return std::make_tuple( unary_not_if( tt, bit ), bit, std::vector{} ); + } + + /* Special case for n = 1 */ + if ( num_vars == 1 ) + { + const auto bit1 = get_bit( tt, 1 ); + return std::make_tuple( unary_not_if( tt, bit1 ), bit1 << 1, std::vector{ 0 } ); + } + + assert( num_vars >= 2 && num_vars <= 6u ); + + auto t1 = tt, t2 = ~tt; + auto tmin = std::min( t1, t2 ); + auto invo = tmin == t2; + + const auto& swaps = detail::swaps[num_vars - 2u]; + const auto& flips = detail::flips[num_vars - 2u]; + + int best_swap = -1; + int best_flip = -1; + + for ( int i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + swap_adjacent_inplace( t2, pos ); + if ( t1 < tmin || t2 < tmin ) + { + best_swap = i; + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + } + + for ( int j = 0; j < flips.size(); ++j ) + { + const auto pos = flips[j]; + swap_adjacent_inplace( t1, 0 ); + flip_inplace( t1, pos ); + swap_adjacent_inplace( t2, 0 ); + flip_inplace( t2, pos ); + if ( t1 < tmin || t2 < tmin ) + { + best_swap = -1; + best_flip = j; + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + + for ( int i = 0; i < swaps.size(); ++i ) + { + const auto pos = swaps[i]; + swap_adjacent_inplace( t1, pos ); + swap_adjacent_inplace( t2, pos ); + if ( t1 < tmin || t2 < tmin ) + { + best_swap = i; + best_flip = j; + tmin = std::min( t1, t2 ); + invo = tmin == t2; + } + } + } + + std::vector perm( num_vars ); + std::iota( perm.begin(), perm.end(), 0u ); + + for ( auto i = 0; i <= best_swap; ++i ) + { + const auto pos = swaps[i]; + std::swap( perm[pos], perm[pos + 1] ); + } + + uint32_t phase = uint32_t( invo ) << num_vars; + for ( auto i = 0; i <= best_flip; ++i ) + { + phase ^= 1 << flips[i]; + } + + return std::make_tuple( tmin, phase, perm ); +} + +/*! Obtain truth table from NPN configuration + + Given an NPN configuration, which contains a representative + function, input/output negations, and input permutations this + function computes the original truth table. + + \param config NPN configuration +*/ +template +TT create_from_npn_config( const std::tuple>& config ) +{ + const auto& from = std::get<0>( config ); + const auto& phase = std::get<1>( config ); + auto perm = std::get<2>( config ); + const auto num_vars = from.num_vars(); + + /* is output complemented? */ + auto res = ( ( phase >> num_vars ) & 1 ) ? ~from : from; + + /* input permutations */ + for ( auto i = 0; i < num_vars; ++i ) + { + if ( perm[i] == i ) continue; + + int k = i; + while ( perm[k] != i ) + { + ++k; + } + + swap_inplace( res, i, k ); + std::swap( perm[i], perm[k] ); + } + + /* input complementations */ + for ( auto i = 0; i < num_vars; ++i ) + { + if ( ( phase >> i ) & 1 ) + { + flip_inplace( res, i ); + } + } + + + return res; +} + +} // namespace kitty diff --git a/include/kitty/constructors.hpp b/include/kitty/constructors.hpp index 4a9e4fa7..cd5e332f 100644 --- a/include/kitty/constructors.hpp +++ b/include/kitty/constructors.hpp @@ -102,6 +102,8 @@ void create_from_binary_string( TT& tt, const std::string& binary ) { assert( binary.size() == tt.num_bits() ); + clear( tt ); + size_t i = 0u, j = binary.size(); do { @@ -113,9 +115,38 @@ void create_from_binary_string( TT& tt, const std::string& binary ) } while ( j ); } +/*! Constructs truth table from hexadecimal string + + Note that the first character in the string represents the four most + significant bit in the truth table. For example, the 3-input + majority function is represented by the binary string "E8" or "e8". + The number of characters in `hex` must be one fourth the number of + bits in `tt`. + + \param tt Truth table + \param hex Hexadecimal string +*/ template void create_from_hex_string( TT& tt, const std::string& hex ) { + clear( tt ); + + /* special case for small truth tables */ + if ( tt.num_vars() < 2 ) + { + assert( hex.size() == 1 ); + const auto i = detail::hex_to_int[static_cast( hex[0] )]; + if ( i & 1 ) + { + set_bit( tt, 0 ); + } + if ( tt.num_vars() == 1 && ( i & 2 ) ) + { + set_bit( tt, 1 ); + } + return; + } + assert( ( hex.size() << 2 ) == tt.num_bits() ); auto j = tt.num_bits() - 1; @@ -123,10 +154,14 @@ void create_from_hex_string( TT& tt, const std::string& hex ) for ( unsigned char c : hex ) { const auto i = detail::hex_to_int[c]; - if ( i & 8 ) set_bit( tt, j ); - if ( i & 4 ) set_bit( tt, j - 1 ); - if ( i & 2 ) set_bit( tt, j - 2 ); - if ( i & 1 ) set_bit( tt, j - 3 ); + if ( i & 8 ) + set_bit( tt, j ); + if ( i & 4 ) + set_bit( tt, j - 1 ); + if ( i & 2 ) + set_bit( tt, j - 2 ); + if ( i & 1 ) + set_bit( tt, j - 3 ); j -= 4; } } diff --git a/include/kitty/detail/constants.hpp b/include/kitty/detail/constants.hpp index b8f49739..2912094b 100644 --- a/include/kitty/detail/constants.hpp +++ b/include/kitty/detail/constants.hpp @@ -27,6 +27,7 @@ #pragma once #include +#include namespace kitty { @@ -38,12 +39,44 @@ static constexpr uint64_t projections[] = {0xaaaaaaaaaaaaaaaa, 0xccccccccccccccc static constexpr uint64_t masks[] = {0x0000000000000001, 0x0000000000000003, 0x000000000000000f, 0x00000000000000ff, 0x000000000000ffff, 0x00000000ffffffff, 0xffffffffffffffff}; -static constexpr uint64_t permutation_masks[6][3] = { +static constexpr uint64_t permutation_masks[][3] = { {0x9999999999999999, 0x2222222222222222, 0x4444444444444444}, - {0xC3C3C3C3C3C3C3C3, 0x0C0C0C0C0C0C0C0C, 0x3030303030303030}, - {0xF00FF00FF00FF00F, 0x00F000F000F000F0, 0x0F000F000F000F00}, - {0xFF0000FFFF0000FF, 0x0000FF000000FF00, 0x00FF000000FF0000}, - {0xFFFF00000000FFFF, 0x00000000FFFF0000, 0x0000FFFF00000000}}; + {0xc3c3c3c3c3c3c3c3, 0x0c0c0c0c0c0c0c0c, 0x3030303030303030}, + {0xf00ff00ff00ff00f, 0x00f000f000f000f0, 0x0f000f000f000f00}, + {0xff0000ffff0000ff, 0x0000ff000000ff00, 0x00ff000000ff0000}, + {0xffff00000000ffff, 0x00000000ffff0000, 0x0000ffff00000000}}; + +static constexpr uint64_t ppermutation_masks[][6][3] = { + {{0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x9999999999999999, 0x2222222222222222, 0x4444444444444444}, + {0xa5a5a5a5a5a5a5a5, 0x0a0a0a0a0a0a0a0a, 0x5050505050505050}, + {0xaa55aa55aa55aa55, 0x00aa00aa00aa00aa, 0x5500550055005500}, + {0xaaaa5555aaaa5555, 0x0000aaaa0000aaaa, 0x5555000055550000}, + {0xaaaaaaaa55555555, 0x00000000aaaaaaaa, 0x5555555500000000}}, + {{0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0xc3c3c3c3c3c3c3c3, 0x0c0c0c0c0c0c0c0c, 0x3030303030303030}, + {0xcc33cc33cc33cc33, 0x00cc00cc00cc00cc, 0x3300330033003300}, + {0xcccc3333cccc3333, 0x0000cccc0000cccc, 0x3333000033330000}, + {0xcccccccc33333333, 0x00000000cccccccc, 0x3333333300000000}}, + {{0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0xf00ff00ff00ff00f, 0x00f000f000f000f0, 0x0f000f000f000f00}, + {0xf0f00f0ff0f00f0f, 0x0000f0f00000f0f0, 0x0f0f00000f0f0000}, + {0xf0f0f0f00f0f0f0f, 0x00000000f0f0f0f0, 0x0f0f0f0f00000000}}, + {{0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0xff0000ffff0000ff, 0x0000ff000000ff00, 0x00ff000000ff0000}, + {0xff00ff0000ff00ff, 0x00000000ff00ff00, 0x00ff00ff00000000}}, + {{0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0x0000000000000000, 0x0000000000000000, 0x0000000000000000}, + {0xffff00000000ffff, 0x00000000ffff0000, 0x0000ffff00000000}}}; static constexpr int32_t hex_to_int[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -53,6 +86,21 @@ static constexpr int32_t hex_to_int[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + +/*! adjacent swap sequences (from 2 to 6 variables) */ +static std::vector swaps[] = {{0}, + {1, 0, 1, 0, 1}, + {2, 1, 0, 2, 0, 1, 2, 0, 2, 1, 0, 2, 0, 1, 2, 0, 2, 1, 0, 2, 0, 1, 2}, + {3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3, 0, 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3, 0, 3, 2, 1, 0, 3, 0, 1, 2, 3, 1, 3, 2, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 2, 3, 1, 3, 2, 1, 0, 3, 0, 1, 2, 3}, + {4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 1, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4, 0, 4, 3, 2, 1, 0, 2, 0, 1, 2, 3, 4, 2, 4, 3, 2, 1, 0, 4, 0, 1, 2, 3, 4}}; + +/*! flip sequences (from 2 to 6 variables) */ +static std::vector flips[] = {{0, 1, 0}, + {0, 1, 0, 2, 0, 1, 0}, + {0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}, + {0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}, + {0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}}; + } // namespace detail } // namespace kitty /*! \endcond */ diff --git a/include/kitty/kitty.hpp b/include/kitty/kitty.hpp index 82fb280b..1dd6ca74 100644 --- a/include/kitty/kitty.hpp +++ b/include/kitty/kitty.hpp @@ -29,8 +29,10 @@ #include "dynamic_truth_table.hpp" #include "bit_operations.hpp" +#include "canonization.hpp" #include "constructors.hpp" #include "operations.hpp" +#include "operators.hpp" // /\___/\ // ( o o ) diff --git a/include/kitty/operations.hpp b/include/kitty/operations.hpp index 6642839a..d0c4b32b 100644 --- a/include/kitty/operations.hpp +++ b/include/kitty/operations.hpp @@ -176,6 +176,14 @@ inline TT unary_not( const TT& tt ) return unary_operation( tt, []( auto a ) { return ~a; } ); } +/*! Inverts all bits in a truth table, based on a condition */ +template +inline TT unary_not_if( const TT& tt, bool cond ) +{ + const auto mask = -static_cast( cond ); + return unary_operation( tt, [mask]( auto a ) { return a ^ mask; } ); +} + /*! Performs bitwise AND of two truth tables. */ template inline TT binary_and( const TT& first, const TT& second ) @@ -227,6 +235,28 @@ inline bool equal( const TT& first, const TT& second ) return binary_predicate( first, second, std::equal_to<>() ); } +/*! Checks whether a truth table is lexicographically smaller than another + + Comparison is initiated from most-significant bit. + + \param first First truth table + \param second Second truth table +*/ +template +inline bool less_than( const TT& first, const TT& second ) +{ + return std::lexicographical_compare( std::rbegin( first._bits ), std::rend( first._bits ), + std::rbegin( second._bits ), std::rend( second._bits ) ); +} + +/*! \cond PRIVATE */ +template +inline bool less_than( const static_truth_table& first, const static_truth_table& second ) +{ + return first._bits < second._bits; +} +/*! \endcond PRIVATE */ + /*! Swaps two adjacent variables in a truth table The function swaps variable `var_index` with `var_index + 1`. The @@ -237,7 +267,7 @@ inline bool equal( const TT& first, const TT& second ) \param var_index A variable */ template -void swap_adjacent_inplace( TT& tt, uint64_t var_index ) +void swap_adjacent_inplace( TT& tt, uint8_t var_index ) { assert( var_index < tt.num_vars() - 1 ); @@ -283,7 +313,7 @@ void swap_adjacent_inplace( TT& tt, uint64_t var_index ) /*! \cond PRIVATE */ template -void swap_adjacent_inplace( static_truth_table& tt, uint64_t var_index ) +void swap_adjacent_inplace( static_truth_table& tt, uint8_t var_index ) { assert( var_index < tt.num_vars() ); @@ -304,13 +334,124 @@ void swap_adjacent_inplace( static_truth_table& tt, uint64_t var_ \param var_index A variable */ template -inline TT swap_adjacent( const TT& tt, uint64_t var_index ) +inline TT swap_adjacent( const TT& tt, uint8_t var_index ) { auto copy = tt; swap_adjacent_inplace( copy, var_index ); return copy; } +/*! 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 +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 ), + [var_index1, var_index2, 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; + } + } +} + +/*! \cond PRIVATE */ +template +inline void swap_inplace( static_truth_table& 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 ); + } + + const auto& pmask = detail::ppermutation_masks[var_index1][var_index2]; + const auto shift = ( 1 << var_index2 ) - ( 1 << var_index1 ); + tt._bits = ( tt._bits & pmask[0] ) | ( ( tt._bits & pmask[1] ) << shift ) | ( ( tt._bits & pmask[2] ) >> shift ); +} +/* \endcond */ + +/*! Swaps two adjacent variables in a truth table + + The function swaps variable `var_index1` with `var_index2`. The + function will return a new truth table with the result. + + \param tt Truth table + \param var_index1 First variable + \param var_index2 Second variable +*/ +template +inline TT swap( const TT& tt, uint8_t var_index1, uint8_t var_index2 ) +{ + auto copy = tt; + swap_inplace( copy, var_index1, var_index2 ); + return copy; +} + /*! Flips a variable in a truth table The function flips variable `var_index` in `tt`. The function will @@ -321,7 +462,7 @@ inline TT swap_adjacent( const TT& tt, uint64_t var_index ) \param var_index A variable */ template -void flip_inplace( TT& tt, uint64_t var_index ) +void flip_inplace( TT& tt, uint8_t var_index ) { assert( var_index < tt.num_vars() ); @@ -355,7 +496,7 @@ void flip_inplace( TT& tt, uint64_t var_index ) /*! \cond PRIVATE */ template -inline void flip_inplace( static_truth_table& tt, uint64_t var_index ) +inline void flip_inplace( static_truth_table& tt, uint8_t var_index ) { assert( var_index < tt.num_vars() ); @@ -373,7 +514,7 @@ inline void flip_inplace( static_truth_table& tt, uint64_t var_in \param var_index A variable */ template -inline TT flip( const TT& tt, uint64_t var_index ) +inline TT flip( const TT& tt, uint8_t var_index ) { auto copy = tt; flip_inplace( copy, var_index ); diff --git a/include/kitty/operators.hpp b/include/kitty/operators.hpp new file mode 100644 index 00000000..bcc3c951 --- /dev/null +++ b/include/kitty/operators.hpp @@ -0,0 +1,54 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "operations.hpp" + +namespace kitty +{ + +/*! Operator for `unary_not` */ +template +inline TT operator~( const TT& tt ) +{ + return unary_not( tt ); +} + +/*! Operator for `equal` */ +template +inline bool operator==( const TT& first, const TT& second ) +{ + return equal( first, second ); +} + +/*! Operator for `less_than` */ +template +inline bool operator<( const TT& first, const TT& second ) +{ + return less_than( first, second ); +} + +} // namespace kitty diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fcebfe00..3b86ac7f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,6 +2,8 @@ add_subdirectory(googletest/googletest) include_directories(googletest/googletest/include) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2") + file(GLOB FILENAMES *.cpp) add_executable(run_tests ${FILENAMES}) diff --git a/test/canonization.cpp b/test/canonization.cpp new file mode 100644 index 00000000..6ced7f6a --- /dev/null +++ b/test/canonization.cpp @@ -0,0 +1,76 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include + +#include + +#include "utility.hpp" + +using namespace kitty; + +class CanonizationTest : public kitty::testing::Test +{ +protected: + template + void check_npn( const std::string& hex_func, const std::string& hex_expect ) const + { + const auto tt = from_hex( hex_func ); + const auto res = exact_npn_canonization( tt ); + EXPECT_EQ( std::get<0>( res ), from_hex( hex_expect ) ); + EXPECT_EQ( create_from_npn_config( res ), tt ); + } +}; + +TEST_F( CanonizationTest, random_functions ) +{ + using pair_vec_t = std::vector>; + + for ( const auto& p : pair_vec_t{{"0", "0"}, {"1", "0"}} ) + { + check_npn<0>( p.first, p.second ); + } + + for ( const auto& p : pair_vec_t{{"0", "0"}, {"1", "1"}, {"2", "1"}, {"3", "0"}} ) + { + check_npn<1>( p.first, p.second ); + } + + for ( const auto& p : pair_vec_t{{"0", "0"}, {"1", "1"}, {"2", "1"}, {"3", "3"}, {"4", "1"}, {"5", "3"}, {"6", "6"}, {"7", "1"}, {"8", "1"}, {"9", "6"}, {"a", "3"}, {"b", "1"}, {"c", "3"}, {"d", "1"}, {"e", "1"}, {"f", "0"}} ) + { + check_npn<2>( p.first, p.second ); + } + + check_npn<3>( "5b", "19" ); + check_npn<3>( "dc", "07" ); + check_npn<3>( "2e", "1b" ); + + check_npn<4>( "cafe", "011b" ); + check_npn<6>( "17cad20f55459c3f", "001b674bdf9ca7c3" ); +} diff --git a/test/constructors.cpp b/test/constructors.cpp index 340d1b82..33d3b958 100644 --- a/test/constructors.cpp +++ b/test/constructors.cpp @@ -75,6 +75,38 @@ TEST( ConstructorsTest, create_from_hex_string ) EXPECT_TRUE( equal( tt_d, tt_d_str ) ); } +TEST( ConstructorsTest, create_constants ) +{ + static_truth_table<0> tt_s; + EXPECT_EQ( tt_s.num_vars(), 0 ); + EXPECT_EQ( tt_s.num_bits(), 1 ); + + create_from_hex_string( tt_s, "0" ); + EXPECT_EQ( tt_s._bits, 0 ); + + create_from_hex_string( tt_s, "1" ); + EXPECT_EQ( tt_s._bits, 1 ); +} + +TEST( ConstructorsTest, create_one_variable_functions ) +{ + static_truth_table<1> tt_s; + EXPECT_EQ( tt_s.num_vars(), 1 ); + EXPECT_EQ( tt_s.num_bits(), 2 ); + + create_from_hex_string( tt_s, "0" ); + EXPECT_EQ( tt_s._bits, 0 ); + + create_from_hex_string( tt_s, "1" ); + EXPECT_EQ( tt_s._bits, 1 ); + + create_from_hex_string( tt_s, "2" ); + EXPECT_EQ( tt_s._bits, 2 ); + + create_from_hex_string( tt_s, "3" ); + EXPECT_EQ( tt_s._bits, 3 ); +} + TEST( ConstructorsTest, create_majority5 ) { static_truth_table<5> tt_s; diff --git a/test/operations.cpp b/test/operations.cpp index de7e5db8..b2deb3a6 100644 --- a/test/operations.cpp +++ b/test/operations.cpp @@ -29,226 +29,220 @@ #include +#include "utility.hpp" + using namespace kitty; -TEST( OperationsTest, binary_for_small ) +class OperationsTest : public kitty::testing::Test { - static_truth_table<2> tt1_s, tt2_s; - - create_nth_var( tt1_s, 0 ); - create_nth_var( tt2_s, 1 ); +}; - EXPECT_EQ( binary_and( tt1_s, tt2_s )._bits, 0x8 ); - EXPECT_EQ( binary_or( tt1_s, tt2_s )._bits, 0xe ); - EXPECT_EQ( unary_not( binary_and( tt1_s, tt2_s ) )._bits, 0x7 ); - EXPECT_EQ( unary_not( binary_or( tt1_s, tt2_s ) )._bits, 0x1 ); +TEST_F( OperationsTest, unary_for_small ) +{ + EXPECT_EQ( unary_not( from_hex<2>( "8" ) ), from_hex<2>( "7" ) ); + EXPECT_EQ( unary_not( from_hex<2>( "6" ) ), from_hex<2>( "9" ) ); - dynamic_truth_table tt1_d( 2 ), tt2_d( 2 ); + EXPECT_EQ( unary_not_if( from_hex<2>( "8" ), true ), from_hex<2>( "7" ) ); + EXPECT_EQ( unary_not_if( from_hex<2>( "6" ), true ), from_hex<2>( "9" ) ); - create_nth_var( tt1_d, 0 ); - create_nth_var( tt2_d, 1 ); + EXPECT_EQ( unary_not_if( from_hex<2>( "8" ), false ), from_hex<2>( "8" ) ); + EXPECT_EQ( unary_not_if( from_hex<2>( "6" ), false ), from_hex<2>( "6" ) ); +} - EXPECT_EQ( binary_and( tt1_d, tt2_d )._bits[0], 0x8 ); - EXPECT_EQ( binary_or( tt1_d, tt2_d )._bits[0], 0xe ); - EXPECT_EQ( unary_not( binary_and( tt1_d, tt2_d ) )._bits[0], 0x7 ); - EXPECT_EQ( unary_not( binary_or( tt1_d, tt2_d ) )._bits[0], 0x1 ); +TEST_F( OperationsTest, binary_for_small ) +{ + EXPECT_EQ( binary_and( nth<2>( 0 ), nth<2>( 1 ) ), from_hex<2>( "8" ) ); + EXPECT_EQ( binary_or( nth<2>( 0 ), nth<2>( 1 ) ), from_hex<2>( "e" ) ); + EXPECT_EQ( unary_not( binary_and( nth<2>( 0 ), nth<2>( 1 ) ) ), from_hex<2>( "7" ) ); + EXPECT_EQ( unary_not( binary_or( nth<2>( 0 ), nth<2>( 1 ) ) ), from_hex<2>( "1" ) ); + + EXPECT_EQ( binary_and( nth( 2, 0 ), nth( 2, 1 ) ), from_hex( 2, "8" ) ); + EXPECT_EQ( binary_or( nth( 2, 0 ), nth( 2, 1 ) ), from_hex( 2, "e" ) ); + EXPECT_EQ( unary_not( binary_and( nth( 2, 0 ), nth( 2, 1 ) ) ), from_hex( 2, "7" ) ); + EXPECT_EQ( unary_not( binary_or( nth( 2, 0 ), nth( 2, 1 ) ) ), from_hex( 2, "1" ) ); } -TEST( OperationsTest, ternary_for_small ) +TEST_F( OperationsTest, ternary_for_small ) { { - static_truth_table<3> tt1_s, tt2_s, tt3_s; - - create_nth_var( tt1_s, 0 ); - create_nth_var( tt2_s, 1 ); - create_nth_var( tt3_s, 2 ); + const auto t1 = nth<3>( 0 ), t2 = nth<3>( 1 ), t3 = nth<3>( 2 ); - const auto maj_expr = binary_or( binary_or( binary_and( tt1_s, tt2_s ), binary_and( tt1_s, tt3_s ) ), binary_and( tt2_s, tt3_s ) ); - const auto maj_direct = ternary_majority( tt1_s, tt2_s, tt3_s ); + const auto maj_expr = binary_or( binary_or( binary_and( t1, t2 ), binary_and( t1, t3 ) ), binary_and( t2, t3 ) ); + const auto maj_direct = ternary_majority( t1, t2, t3 ); - EXPECT_EQ( maj_expr._bits, 0xe8 ); - EXPECT_EQ( maj_expr._bits, maj_direct._bits ); + EXPECT_EQ( maj_expr, from_hex<3>( "e8" ) ); + EXPECT_EQ( maj_expr, maj_direct ); - const auto ite_expr = binary_or( binary_and( tt1_s, tt2_s ), binary_and( unary_not( tt1_s ), tt3_s ) ); - const auto ite_direct = ternary_ite( tt1_s, tt2_s, tt3_s ); + const auto ite_expr = binary_or( binary_and( t1, t2 ), binary_and( unary_not( t1 ), t3 ) ); + const auto ite_direct = ternary_ite( t1, t2, t3 ); - EXPECT_EQ( ite_expr._bits, 0xd8 ); - EXPECT_EQ( ite_expr._bits, ite_direct._bits ); + EXPECT_EQ( ite_expr, from_hex<3>( "d8" ) ); + EXPECT_EQ( ite_expr, ite_direct ); } { - dynamic_truth_table tt1_d( 3 ), tt2_d( 3 ), tt3_d( 3 ); + const auto t1 = nth( 3, 0 ), t2 = nth( 3, 1 ), t3 = nth( 3, 2 ); - create_nth_var( tt1_d, 0 ); - create_nth_var( tt2_d, 1 ); - create_nth_var( tt3_d, 2 ); + const auto maj_expr = binary_or( binary_or( binary_and( t1, t2 ), binary_and( t1, t3 ) ), binary_and( t2, t3 ) ); + const auto maj_direct = ternary_majority( t1, t2, t3 ); - const auto maj_expr = binary_or( binary_or( binary_and( tt1_d, tt2_d ), binary_and( tt1_d, tt3_d ) ), binary_and( tt2_d, tt3_d ) ); - const auto maj_direct = ternary_majority( tt1_d, tt2_d, tt3_d ); + EXPECT_EQ( maj_expr, from_hex( 3, "e8" ) ); + EXPECT_EQ( maj_expr, maj_direct ); - EXPECT_EQ( maj_expr._bits[0], 0xe8 ); - EXPECT_EQ( maj_expr._bits[0], maj_direct._bits[0] ); + const auto ite_expr = binary_or( binary_and( t1, t2 ), binary_and( unary_not( t1 ), t3 ) ); + const auto ite_direct = ternary_ite( t1, t2, t3 ); - const auto ite_expr = binary_or( binary_and( tt1_d, tt2_d ), binary_and( unary_not( tt1_d ), tt3_d ) ); - const auto ite_direct = ternary_ite( tt1_d, tt2_d, tt3_d ); - - EXPECT_EQ( ite_expr._bits[0], 0xd8 ); - EXPECT_EQ( ite_expr._bits[0], ite_direct._bits[0] ); + EXPECT_EQ( ite_expr, from_hex( 3, "d8" ) ); + EXPECT_EQ( ite_expr, ite_direct ); } } -TEST( OperationsTest, binary_for_large ) +TEST_F( OperationsTest, unary_for_large ) { - { - static_truth_table<7> tt1_s, tt2_s; - create_nth_var( tt1_s, 0 ); - create_nth_var( tt2_s, 1 ); + EXPECT_EQ( unary_not( from_hex<7>( "80000000000000000000000000000000" ) ), from_hex<7>( "7fffffffffffffffffffffffffffffff" ) ); + EXPECT_EQ( unary_not( from_hex<7>( "66666666666666666666666666666666" ) ), from_hex<7>( "99999999999999999999999999999999" ) ); + + EXPECT_EQ( unary_not_if( from_hex<7>( "80000000000000000000000000000000" ), true ), from_hex<7>( "7fffffffffffffffffffffffffffffff" ) ); + EXPECT_EQ( unary_not_if( from_hex<7>( "66666666666666666666666666666666" ), true ), from_hex<7>( "99999999999999999999999999999999" ) ); - auto tt_s = binary_and( tt1_s, tt2_s ); - for ( auto i = 2; i < 7; ++i ) + EXPECT_EQ( unary_not_if( from_hex<7>( "80000000000000000000000000000000" ), false ), from_hex<7>( "80000000000000000000000000000000" ) ); + EXPECT_EQ( unary_not_if( from_hex<7>( "66666666666666666666666666666666" ), false ), from_hex<7>( "66666666666666666666666666666666" ) ); +} + +TEST_F( OperationsTest, binary_for_large ) +{ + { + auto tt = nth<7>( 0 ); + for ( auto i = 1; i < 7; ++i ) { - static_truth_table<7> ttv_s; - create_nth_var( ttv_s, i ); - tt_s = binary_and( tt_s, ttv_s ); + tt = binary_and( tt, nth<7>( i ) ); } - - EXPECT_EQ( tt_s._bits[0], 0x0 ); - EXPECT_EQ( tt_s._bits[1], uint64_t( 1 ) << 63 ); + EXPECT_EQ( tt, from_hex<7>( "8000000000000000" ) ); } { - dynamic_truth_table tt1_d( 7 ), tt2_d( 7 ); - create_nth_var( tt1_d, 0 ); - create_nth_var( tt2_d, 1 ); - - auto tt_d = binary_and( tt1_d, tt2_d ); - for ( auto i = 2; i < 7; ++i ) + auto tt = nth( 7, 0 ); + for ( auto i = 1; i < 7; ++i ) { - dynamic_truth_table ttv_d( 7 ); - create_nth_var( ttv_d, i ); - tt_d = binary_and( tt_d, ttv_d ); + tt = binary_and( tt, nth( 7, i ) ); } - - EXPECT_EQ( tt_d._bits[0], 0x0 ); - EXPECT_EQ( tt_d._bits[1], uint64_t( 1 ) << 63 ); + EXPECT_EQ( tt, from_hex( 7, "8000000000000000" ) ); } } -TEST( OperationsTest, ternary_for_large ) +TEST_F( OperationsTest, ternary_for_large ) { { - static_truth_table<7> tt1_s, tt2_s, tt3_s; - - create_nth_var( tt1_s, 0 ); - create_nth_var( tt2_s, 3 ); - create_nth_var( tt3_s, 6 ); + const auto t1 = nth<7>( 0 ), t2 = nth<7>( 1 ), t3 = nth<7>( 2 ); - const auto maj_expr = binary_or( binary_or( binary_and( tt1_s, tt2_s ), binary_and( tt1_s, tt3_s ) ), binary_and( tt2_s, tt3_s ) ); - const auto maj_direct = ternary_majority( tt1_s, tt2_s, tt3_s ); + const auto maj_expr = binary_or( binary_or( binary_and( t1, t2 ), binary_and( t1, t3 ) ), binary_and( t2, t3 ) ); + const auto maj_direct = ternary_majority( t1, t2, t3 ); - EXPECT_EQ( maj_expr._bits, maj_direct._bits ); + EXPECT_EQ( maj_expr, maj_direct ); - const auto ite_expr = binary_or( binary_and( tt1_s, tt2_s ), binary_and( unary_not( tt1_s ), tt3_s ) ); - const auto ite_direct = ternary_ite( tt1_s, tt2_s, tt3_s ); + const auto ite_expr = binary_or( binary_and( t1, t2 ), binary_and( unary_not( t1 ), t3 ) ); + const auto ite_direct = ternary_ite( t1, t2, t3 ); - EXPECT_EQ( ite_expr._bits, ite_direct._bits ); + EXPECT_EQ( ite_expr, ite_direct ); } { - dynamic_truth_table tt1_d( 7 ), tt2_d( 7 ), tt3_d( 7 ); - - create_nth_var( tt1_d, 0 ); - create_nth_var( tt2_d, 3 ); - create_nth_var( tt3_d, 6 ); + const auto t1 = nth( 7, 0 ), t2 = nth( 7, 1 ), t3 = nth( 7, 2 ); - const auto maj_expr = binary_or( binary_or( binary_and( tt1_d, tt2_d ), binary_and( tt1_d, tt3_d ) ), binary_and( tt2_d, tt3_d ) ); - const auto maj_direct = ternary_majority( tt1_d, tt2_d, tt3_d ); + const auto maj_expr = binary_or( binary_or( binary_and( t1, t2 ), binary_and( t1, t3 ) ), binary_and( t2, t3 ) ); + const auto maj_direct = ternary_majority( t1, t2, t3 ); - EXPECT_EQ( maj_expr._bits, maj_direct._bits ); + EXPECT_EQ( maj_expr, maj_direct ); - const auto ite_expr = binary_or( binary_and( tt1_d, tt2_d ), binary_and( unary_not( tt1_d ), tt3_d ) ); - const auto ite_direct = ternary_ite( tt1_d, tt2_d, tt3_d ); + const auto ite_expr = binary_or( binary_and( t1, t2 ), binary_and( unary_not( t1 ), t3 ) ); + const auto ite_direct = ternary_ite( t1, t2, t3 ); - EXPECT_EQ( ite_expr._bits, ite_direct._bits ); + EXPECT_EQ( ite_expr, ite_direct ); } } -TEST( OperationsTest, swap_adjacent_inplace_small ) +TEST_F( OperationsTest, comparisons ) +{ + EXPECT_TRUE( equal( from_hex<3>( "e8" ), from_hex<3>( "e8" ) ) ); + EXPECT_TRUE( equal( from_hex( 3, "e8" ), from_hex( 3, "e8" ) ) ); + + EXPECT_TRUE( less_than( from_hex<3>( "e5" ), from_hex<3>( "f6" ) ) ); + EXPECT_TRUE( less_than( from_hex<3>( "e5" ), from_hex<3>( "f5" ) ) ); + EXPECT_TRUE( less_than( from_hex<3>( "e5" ), from_hex<3>( "f4" ) ) ); + EXPECT_TRUE( less_than( from_hex<3>( "e5" ), from_hex<3>( "e6" ) ) ); + EXPECT_FALSE( less_than( from_hex<3>( "e5" ), from_hex<3>( "e5" ) ) ); + EXPECT_FALSE( less_than( from_hex<3>( "e5" ), from_hex<3>( "e4" ) ) ); + EXPECT_FALSE( less_than( from_hex<3>( "e5" ), from_hex<3>( "d6" ) ) ); + EXPECT_FALSE( less_than( from_hex<3>( "e5" ), from_hex<3>( "d5" ) ) ); + EXPECT_FALSE( less_than( from_hex<3>( "e5" ), from_hex<3>( "d4" ) ) ); + + EXPECT_TRUE( less_than( from_hex( 3, "e5" ), from_hex( 3, "f6" ) ) ); + EXPECT_TRUE( less_than( from_hex( 3, "e5" ), from_hex( 3, "f5" ) ) ); + EXPECT_TRUE( less_than( from_hex( 3, "e5" ), from_hex( 3, "f4" ) ) ); + EXPECT_TRUE( less_than( from_hex( 3, "e5" ), from_hex( 3, "e6" ) ) ); + EXPECT_FALSE( less_than( from_hex( 3, "e5" ), from_hex( 3, "e5" ) ) ); + EXPECT_FALSE( less_than( from_hex( 3, "e5" ), from_hex( 3, "e4" ) ) ); + EXPECT_FALSE( less_than( from_hex( 3, "e5" ), from_hex( 3, "d6" ) ) ); + EXPECT_FALSE( less_than( from_hex( 3, "e5" ), from_hex( 3, "d5" ) ) ); + EXPECT_FALSE( less_than( from_hex( 3, "e5" ), from_hex( 3, "d4" ) ) ); + + EXPECT_TRUE( equal( from_hex<7>( "e92c774439c72c8955906ef92ecefec9" ), from_hex<7>( "e92c774439c72c8955906ef92ecefec9" ) ) ); + EXPECT_TRUE( equal( from_hex( 7, "e92c774439c72c8955906ef92ecefec9" ), from_hex( 7, "e92c774439c72c8955906ef92ecefec9" ) ) ); + + EXPECT_TRUE( less_than( from_hex<7>( "e92c774439c72c8955906ef92ecefec9" ), from_hex<7>( "e92c774439c72c8955906ef92edefec9" ) ) ); + EXPECT_FALSE( less_than( from_hex<7>( "e92c774439c72c8955906ef92ecefec9" ), from_hex<7>( "e92c774439c72c8955906ef92ebefec9" ) ) ); + + EXPECT_TRUE( less_than( from_hex( 7, "e92c774439c72c8955906ef92ecefec9" ), from_hex( 7, "e92c774439c72c8955906ef92edefec9" ) ) ); + EXPECT_FALSE( less_than( from_hex( 7, "e92c774439c72c8955906ef92ecefec9" ), from_hex( 7, "e92c774439c72c8955906ef92ebefec9" ) ) ); +} + +TEST_F( OperationsTest, swap_adjacent_inplace_small ) { for ( const auto& p : std::vector>{{0u, "bce8"}, {1u, "e6e8"}, {2u, "dea8"}} ) { - static_truth_table<4> tt_s, tt_s_res; - create_from_hex_string( tt_s, "dae8" ); - + auto tt_s = from_hex<4>( "dae8" ); swap_adjacent_inplace( tt_s, p.first ); - create_from_hex_string( tt_s_res, p.second ); - - EXPECT_TRUE( equal( tt_s, tt_s_res ) ); - - dynamic_truth_table tt_d( 4 ), tt_d_res( 4 ); - create_from_hex_string( tt_d, "dae8" ); + EXPECT_EQ( tt_s, from_hex<4>( p.second ) ); + auto tt_d = from_hex( 4, "dae8" ); swap_adjacent_inplace( tt_d, p.first ); - create_from_hex_string( tt_d_res, p.second ); - - EXPECT_TRUE( equal( tt_d, tt_d_res ) ); + EXPECT_EQ( tt_d, from_hex( 4, p.second ) ); } } -TEST( OperationsTest, swap_adjacent_small ) +TEST_F( OperationsTest, swap_adjacent_small ) { for ( const auto& p : std::vector>{{0u, "bce8"}, {1u, "e6e8"}, {2u, "dea8"}} ) { - static_truth_table<4> tt_s, tt_s_res; - create_from_hex_string( tt_s, "dae8" ); - create_from_hex_string( tt_s_res, p.second ); - - EXPECT_TRUE( equal( swap_adjacent( tt_s, p.first ), tt_s_res ) ); - - dynamic_truth_table tt_d( 4 ), tt_d_res( 4 ); - create_from_hex_string( tt_d, "dae8" ); - create_from_hex_string( tt_d_res, p.second ); - - EXPECT_TRUE( equal( swap_adjacent( tt_d, p.first ), tt_d_res ) ); + EXPECT_EQ( swap_adjacent( from_hex<4>( "dae8" ), p.first ), from_hex<4>( p.second ) ); + EXPECT_EQ( swap_adjacent( from_hex( 4, "dae8" ), p.first ), from_hex( 4, p.second ) ); } } -class OperationsTestSwap : public ::testing::TestWithParam> +class OperationsTestSwap : public OperationsTest, public ::testing::WithParamInterface> { }; TEST_P( OperationsTestSwap, swap_adjacent_inplace_large ) { - static_truth_table<9> tt_s, tt_s_res; - create_from_hex_string( tt_s, "28e3b8d62855c4b787eef391a93b33297856658b6743aa3cc7e11fde4e9cbf7c98b07f5febfff33bc7ad6f551bc4cbc440453e1bbe24f0cb4f268c6771b55eee" ); - + auto tt_s = from_hex<9>( "28e3b8d62855c4b787eef391a93b33297856658b6743aa3cc7e11fde4e9cbf7c98b07f5febfff33bc7ad6f551bc4cbc440453e1bbe24f0cb4f268c6771b55eee" ); swap_adjacent_inplace( tt_s, GetParam().first ); - create_from_hex_string( tt_s_res, GetParam().second ); - - EXPECT_TRUE( equal( tt_s, tt_s_res ) ); - - dynamic_truth_table tt_d( 9 ), tt_d_res( 9 ); - create_from_hex_string( tt_d, "28e3b8d62855c4b787eef391a93b33297856658b6743aa3cc7e11fde4e9cbf7c98b07f5febfff33bc7ad6f551bc4cbc440453e1bbe24f0cb4f268c6771b55eee" ); + EXPECT_EQ( tt_s, from_hex<9>( GetParam().second ) ); + auto tt_d = from_hex( 9, "28e3b8d62855c4b787eef391a93b33297856658b6743aa3cc7e11fde4e9cbf7c98b07f5febfff33bc7ad6f551bc4cbc440453e1bbe24f0cb4f268c6771b55eee" ); swap_adjacent_inplace( tt_d, GetParam().first ); - create_from_hex_string( tt_d_res, GetParam().second ); - - EXPECT_TRUE( equal( tt_d, tt_d_res ) ); + EXPECT_EQ( tt_d, from_hex( 9, GetParam().second ) ); } TEST_P( OperationsTestSwap, swap_adjacent_large ) { - static_truth_table<9> tt_s, tt_s_res; - create_from_hex_string( tt_s, "28e3b8d62855c4b787eef391a93b33297856658b6743aa3cc7e11fde4e9cbf7c98b07f5febfff33bc7ad6f551bc4cbc440453e1bbe24f0cb4f268c6771b55eee" ); - create_from_hex_string( tt_s_res, GetParam().second ); - - EXPECT_TRUE( equal( swap_adjacent( tt_s, GetParam().first ), tt_s_res ) ); + EXPECT_EQ( swap_adjacent( from_hex<9>( "28e3b8d62855c4b787eef391a93b33297856658b6743aa3cc7e11fde4e9cbf7c98b07f5febfff33bc7ad6f551bc4cbc440453e1bbe24f0cb4f268c6771b55eee" ), + GetParam().first ), + from_hex<9>( GetParam().second ) ); - dynamic_truth_table tt_d( 9 ), tt_d_res( 9 ); - create_from_hex_string( tt_d, "28e3b8d62855c4b787eef391a93b33297856658b6743aa3cc7e11fde4e9cbf7c98b07f5febfff33bc7ad6f551bc4cbc440453e1bbe24f0cb4f268c6771b55eee" ); - create_from_hex_string( tt_d_res, GetParam().second ); - - EXPECT_TRUE( equal( swap_adjacent( tt_d, GetParam().first ), tt_d_res ) ); + EXPECT_EQ( swap_adjacent( from_hex( 9, "28e3b8d62855c4b787eef391a93b33297856658b6743aa3cc7e11fde4e9cbf7c98b07f5febfff33bc7ad6f551bc4cbc440453e1bbe24f0cb4f268c6771b55eee" ), + GetParam().first ), + from_hex( 9, GetParam().second ) ); } INSTANTIATE_TEST_CASE_P( OperationsTestSwapInst, @@ -270,82 +264,52 @@ INSTANTIATE_TEST_CASE_P( OperationsTestSwapInst, std::make_pair( 7u, "28e3b8d62855c4b787eef391a93b332998b07f5febfff33bc7ad6f551bc4cbc4" "7856658b6743aa3cc7e11fde4e9cbf7c40453e1bbe24f0cb4f268c6771b55eee" ) ) ); -TEST( OperationsTest, flip_inplace_small ) +TEST_F( OperationsTest, flip_inplace_small ) { for ( const auto& p : std::vector>{{0u, "0b34"}, {1u, "0dc2"}, {2u, "7083"}, {3u, "3807"}} ) { - static_truth_table<4> tt_s, tt_s_res; - create_from_hex_string( tt_s, "0738" ); - + auto tt_s = from_hex<4>( "0738" ); flip_inplace( tt_s, p.first ); - create_from_hex_string( tt_s_res, p.second ); - - EXPECT_TRUE( equal( tt_s, tt_s_res ) ); - - dynamic_truth_table tt_d( 4 ), tt_d_res( 4 ); - create_from_hex_string( tt_d, "0738" ); + EXPECT_EQ( tt_s, from_hex<4>( p.second ) ); + auto tt_d = from_hex( 4, "0738" ); flip_inplace( tt_d, p.first ); - create_from_hex_string( tt_d_res, p.second ); - - EXPECT_TRUE( equal( tt_d, tt_d_res ) ); + EXPECT_EQ( tt_d, from_hex( 4, p.second ) ); } } -TEST( OperationsTest, flip_small ) +TEST_F( OperationsTest, flip_small ) { for ( const auto& p : std::vector>{{0u, "0b34"}, {1u, "0dc2"}, {2u, "7083"}, {3u, "3807"}} ) { - static_truth_table<4> tt_s, tt_s_res; - create_from_hex_string( tt_s, "0738" ); - create_from_hex_string( tt_s_res, p.second ); - - EXPECT_TRUE( equal( flip( tt_s, p.first ), tt_s_res ) ); - - dynamic_truth_table tt_d( 4 ), tt_d_res( 4 ); - create_from_hex_string( tt_d, "0738" ); - create_from_hex_string( tt_d_res, p.second ); - - EXPECT_TRUE( equal( flip( tt_d, p.first ), tt_d_res ) ); + EXPECT_EQ( flip( from_hex<4>( "0738" ), p.first ), from_hex<4>( p.second ) ); + EXPECT_EQ( flip( from_hex( 4, "0738" ), p.first ), from_hex( 4, p.second ) ); } } -class OperationsTestFlip : public ::testing::TestWithParam> +class OperationsTestFlip : public OperationsTest, public ::testing::WithParamInterface> { }; TEST_P( OperationsTestFlip, flip_inplace_large ) { - static_truth_table<9> tt_s, tt_s_res; - create_from_hex_string( tt_s, "8725ca41421c7bba0ca86e26347847526fc346d7f3e79e76566a9493fbef11e24f74a07643afd946195f6a372757e045f3bca58f110ef00ebf2d81e80ba5679f" ); - + auto tt_s = from_hex<9>( "8725ca41421c7bba0ca86e26347847526fc346d7f3e79e76566a9493fbef11e24f74a07643afd946195f6a372757e045f3bca58f110ef00ebf2d81e80ba5679f" ); flip_inplace( tt_s, GetParam().first ); - create_from_hex_string( tt_s_res, GetParam().second ); - - EXPECT_TRUE( equal( tt_s, tt_s_res ) ); - - dynamic_truth_table tt_d( 9 ), tt_d_res( 9 ); - create_from_hex_string( tt_d, "8725ca41421c7bba0ca86e26347847526fc346d7f3e79e76566a9493fbef11e24f74a07643afd946195f6a372757e045f3bca58f110ef00ebf2d81e80ba5679f" ); + EXPECT_EQ( tt_s, from_hex<9>( GetParam().second ) ); + auto tt_d = from_hex( 9, "8725ca41421c7bba0ca86e26347847526fc346d7f3e79e76566a9493fbef11e24f74a07643afd946195f6a372757e045f3bca58f110ef00ebf2d81e80ba5679f" ); flip_inplace( tt_d, GetParam().first ); - create_from_hex_string( tt_d_res, GetParam().second ); - - EXPECT_TRUE( equal( tt_d, tt_d_res ) ); + EXPECT_EQ( tt_d, from_hex( 9, GetParam().second ) ); } TEST_P( OperationsTestFlip, flip_large ) { - static_truth_table<9> tt_s, tt_s_res; - create_from_hex_string( tt_s, "8725ca41421c7bba0ca86e26347847526fc346d7f3e79e76566a9493fbef11e24f74a07643afd946195f6a372757e045f3bca58f110ef00ebf2d81e80ba5679f" ); - create_from_hex_string( tt_s_res, GetParam().second ); - - EXPECT_TRUE( equal( flip( tt_s, GetParam().first ), tt_s_res ) ); - - dynamic_truth_table tt_d( 9 ), tt_d_res( 9 ); - create_from_hex_string( tt_d, "8725ca41421c7bba0ca86e26347847526fc346d7f3e79e76566a9493fbef11e24f74a07643afd946195f6a372757e045f3bca58f110ef00ebf2d81e80ba5679f" ); - create_from_hex_string( tt_d_res, GetParam().second ); - - EXPECT_TRUE( equal( flip( tt_d, GetParam().first ), tt_d_res ) ); + EXPECT_EQ( flip( from_hex<9>( "8725ca41421c7bba0ca86e26347847526fc346d7f3e79e76566a9493fbef11e24f74a07643afd946195f6a372757e045f3bca58f110ef00ebf2d81e80ba5679f" ), + GetParam().first ), + from_hex<9>( GetParam().second ) ); + EXPECT_EQ( flip( from_hex( 9, "8725ca41421c7bba0ca86e26347847526fc346d7f3e79e76566a9493fbef11e24f74a07643afd946195f6a372757e045f3bca58f110ef00ebf2d81e80ba5679f" ), + GetParam().first ), + from_hex( 9, GetParam().second ) ); } INSTANTIATE_TEST_CASE_P( OperationsTestFlipInst, diff --git a/test/utility.hpp b/test/utility.hpp new file mode 100644 index 00000000..f4b91ac1 --- /dev/null +++ b/test/utility.hpp @@ -0,0 +1,70 @@ +/* kitty: C++ truth table library + * Copyright (C) 2017 EPFL + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include + +namespace kitty +{ + +namespace testing +{ + +class Test : public ::testing::Test +{ +protected: + template + inline static_truth_table nth( uint64_t var_index ) const + { + static_truth_table tt; + create_nth_var( tt, var_index ); + return tt; + } + + inline dynamic_truth_table nth( uint64_t num_vars, uint64_t var_index ) const + { + dynamic_truth_table tt( num_vars ); + create_nth_var( tt, var_index ); + return tt; + } + + template + inline static_truth_table from_hex( const std::string& hex ) const + { + static_truth_table tt; + create_from_hex_string( tt, hex ); + return tt; + } + + inline dynamic_truth_table from_hex( uint64_t num_vars, const std::string& hex ) const + { + dynamic_truth_table tt( num_vars ); + create_from_hex_string( tt, hex ); + return tt; + } +}; +} +}