18#include <unordered_map>
20#include <unordered_set>
30#ifdef LHF_ENABLE_DEBUG
31#define LHF_DEBUG(x) { x };
42template<
typename K,
typename V>
45template<
typename K,
typename V>
81 std::invalid_argument(message.c_str()) {}
92 const T *value =
nullptr;
105 return value !=
nullptr;
114 "A check is likely missing.");
133static const constexpr std::size_t EMPTY_SET_VALUE = 0;
147template<
typename T,
typename Hash = DefaultHash<T>>
149 return prev ^ (Hash()(next) + 0x9e3779b9 + (prev << 6) + (prev >> 2));
184struct std::hash<
lhf::OperationNode> {
187 std::hash<lhf::IndexValue>()(k.
left) ^
188 (std::hash<lhf::IndexValue>()(k.
right) << 1);
204template<
typename OrderedSetT,
typename ElementT,
typename PropertyLess>
206 bool operator()(
const OrderedSetT *a,
const OrderedSetT *b)
const {
209 auto cursor_1 = a->begin();
210 const auto &cursor_end_1 = a->end();
211 auto cursor_2 = b->begin();
212 const auto &cursor_end_2 = b->end();
214 while (cursor_1 != cursor_end_1 && cursor_2 != cursor_end_2) {
215 if (less(*cursor_1, *cursor_2)) {
219 if (less(*cursor_2, *cursor_1)) {
227 return a->size() < b->size();
242 typename PropertyEqual = DefaultEqual<ElementT>>
246 if (a->size() != b->size()) {
250 if (a->size() == 0) {
254 auto cursor_1 = a->begin();
255 const auto &cursor_end_1 = a->end();
256 auto cursor_2 = b->begin();
258 while (cursor_1 != cursor_end_1) {
259 if (!eq(*cursor_1, *cursor_2)) {
282 typename ElementHash = DefaultHash<ElementT>>
286 size_t hash_value = 0;
287 for (
const auto &value : *k) {
288 hash_value = compose_hash<ElementT, ElementHash>(hash_value, value);
300 std::invalid_argument(message.c_str()) {}
307 Unreachable(
const std::string &message =
"Hit a branch marked as unreachable."):
308 std::runtime_error(message.c_str()) {}
311#define ____LHF__STR(x) #x
312#define __LHF_STR(x) ____LHF__STR(x)
313#define __LHF_EXCEPT(x) AssertError(x " [At: " __FILE__ ":" __LHF_STR(__LINE__) "]")
321template<
typename PropertySetT,
typename PropertyElementT>
322static inline void verify_property_set_integrity(
const PropertySetT &cont) {
323 if (cont.size() == 1) {
329 typename PropertyElementT::Hash> k;
330 const PropertyElementT *prev_val =
nullptr;
332 for (
const PropertyElementT &val : cont) {
333 if (prev_val && !(*prev_val < val)) {
334 throw AssertError(
"Supplied property set is not sorted.");
337 if (k.count(val) > 0) {
338 throw AssertError(
"Found duplicate key in given container.");
353#ifndef LHF_DISABLE_INTEGRITY_CHECKS
354#define LHF_PROPERTY_SET_INTEGRITY_VALID(__set) \
355 verify_property_set_integrity<PropertySet, PropertyElement>(__set);
357#define LHF_PROPERTY_SET_INTEGRITY_VALID(__set)
377#ifdef LHF_ENABLE_DEBUG
381#define LHF_PROPERTY_SET_INDEX_VALID(__index) { \
382 _Pragma("GCC diagnostic push"); \
383 _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
384 if ((__index.value) < 0 || ((__index.value) > property_sets.size() - 1)) { \
385 throw __LHF_EXCEPT("Invalid index supplied"); \
387 _Pragma("GCC diagnostic pop") \
391#define LHF_PROPERTY_SET_PAIR_VALID(__index1, __index2) \
392 LHF_PROPERTY_SET_INDEX_VALID(__index1); \
393 LHF_PROPERTY_SET_INDEX_VALID(__index2);
396#define LHF_PROPERTY_SET_PAIR_UNEQUAL(__index1, __index2) \
397 if ((__index1) == (__index2)) { \
398 throw __LHF_EXCEPT("Equal set condition not handled by caller"); \
403#define LHF_PROPERTY_SET_INDEX_VALID(__index)
404#define LHF_PROPERTY_SET_PAIR_VALID(__index1, __index2)
405#define LHF_PROPERTY_SET_PAIR_UNEQUAL(__index1, __index2)
418#define LHF_PUSH_ONE(__cont, __val) (__cont).push_back((__val))
430#define LHF_PUSH_RANGE(__cont, __start, __end) (__cont).insert((__cont).end(), (__start), (__end))
440#define LHF_REGISTER_SET_INTERNAL(__set, __cold) register_set<LHF_DISABLE_INTERNAL_INTEGRITY_CHECK>((__set), (__cold))
448template<
typename PropertyT>
477 typename PropertyLess,
478 typename PropertyHash,
479 typename PropertyEqual,
480 typename PropertyPrinter>
530 return PropertyPrinter()(
value);
535 return PropertyHash()(p.
value);
567#define LHF_BINARY_NESTED_OPERATION(__op_name) \
568 struct __NestingOperation_ ## __op_name { \
569 template<typename ArgIndex, typename LHF> \
570 void operator()(ArgIndex &ret, LHF &lhf, const ArgIndex &c, const ArgIndex &d) { \
571 ret = lhf . __op_name (c, d); \
586#define LHF_PERFORM_BINARY_NESTED_OPERATION(__op_name, __reflist, __arg1, __arg2) \
587 ((__arg1) . template apply<__NestingOperation_ ## __op_name>((__reflist), (__arg2)))
596template<
typename PropertyT,
typename ...ChildT>
622 typename PropertyLess,
623 typename PropertyHash,
624 typename PropertyEqual,
625 typename PropertyPrinter>
637 const PropertyT &
key,
679 template <
typename Operation, std::size_t... Indices>
684 std::index_sequence<Indices...>)
const {
685 ((Operation()(std::get<Indices>(ret),
686 std::get<Indices>(
lhf),
687 std::get<Indices>(
value),
688 std::get<Indices>(arg_value)
704 template<
typename Operation>
709 apply_internal<Operation>(
713 std::make_index_sequence<num_children>{});
718 return PropertyLess()(
key, b.
key);
722 return PropertyEqual()(
key, b.
key);
727 s << PropertyPrinter()(
key);
730 [&s](
auto&&... args) {
731 ((s << args.value <<
' '), ...);
744 return PropertyHash()(p.
key);
790 s <<
" " <<
"Hits : " <<
hits <<
"\n"
791 <<
" " <<
"Equal Hits : " <<
equal_hits <<
"\n"
793 <<
" " <<
"Empty Hits : " <<
empty_hits <<
"\n"
810#ifdef LHF_ENABLE_PERFORMANCE_METRICS
811#define LHF_PERF_INC(__oper, __category) (perf[__LHF_STR(__oper)] . __category ++)
813#define LHF_PERF_INC(__oper, __category)
837 typename PropertyLess = DefaultLess<PropertyT>,
838 typename PropertyHash = DefaultHash<PropertyT>,
839 typename PropertyEqual = DefaultEqual<PropertyT>,
840 typename PropertyPrinter = DefaultPrinter<PropertyT>,
841 typename Nesting = NestingNone<PropertyT>>
854 return value == EMPTY_SET_VALUE;
874 return std::to_string(
value);
935 typename PropertyElement::Hash>,
939 typename PropertyElement::FullEqual>>;
943 using RefList =
typename Nesting::LHFReferenceList;
948#ifdef LHF_ENABLE_PERFORMANCE_METRICS
1041 return Index(cursor->second);
1073 return Index(cursor->second);
1087 typename PropertyElement::Hash,
1088 typename PropertyElement::FullEqual> deduplicator;
1091 deduplicator.insert(i);
1094 c.assign(deduplicator.begin(), deduplicator.end());
1095 std::sort(c.begin(), c.end());
1108 template <
bool disable_
integrity_check = false>
1112 if (!disable_integrity_check) {
1128 return Index(cursor->second);
1143 template <
bool disable_
integrity_check = false>
1147 if (!disable_integrity_check) {
1165 return Index(cursor->second);
1168 template <
bool disable_
integrity_check = false>
1172 if (!disable_integrity_check) {
1187 return Index(cursor->second);
1190 template <
bool disable_
integrity_check = false>
1194 if (!disable_integrity_check) {
1211 return Index(cursor->second);
1244 if (index == EMPTY_SET_VALUE) {
1265 return a.get_key() < b;
1282 return a.get_key() == b;
1310 std::size_t low = 0;
1311 std::size_t high = s.size() - 1;
1313 while (low <= high) {
1314 std::size_t mid = low + (high - low) / 2;
1347 if (
equal(i, prop)) {
1353 std::size_t low = 0;
1354 std::size_t high = s.size() - 1;
1356 while (low <= high) {
1357 std::size_t mid = low + (high - low) / 2;
1359 if (
equal(s[mid], prop)) {
1361 }
else if (
less(s[mid], prop)) {
1399 const Index &a = std::min(_a, _b);
1400 const Index &b = std::max(_a, _b);
1414 if (cursor ==
unions.end()) {
1422 auto cursor_1 = first.begin();
1423 const auto &cursor_end_1 = first.end();
1424 auto cursor_2 = second.begin();
1425 const auto &cursor_end_2 = second.end();
1427 while (cursor_1 != cursor_end_1) {
1428 if (cursor_2 == cursor_end_2) {
1433 if (
less(*cursor_2, *cursor_1)) {
1437 if (!(
less(*cursor_1, *cursor_2))) {
1438 if constexpr (Nesting::is_nested) {
1464 }
else if (ret == b) {
1480 return Index(cursor->second);
1513 return Index(EMPTY_SET_VALUE);
1518 return Index(EMPTY_SET_VALUE);
1524 auto cursor =
differences.find({a.value, b.value});
1534 auto cursor_1 = first.begin();
1535 const auto &cursor_end_1 = first.end();
1536 auto cursor_2 = second.begin();
1537 const auto &cursor_end_2 = second.end();
1539 while (cursor_1 != cursor_end_1) {
1540 if (cursor_2 == cursor_end_2) {
1545 if (
less(*cursor_1, *cursor_2)) {
1549 if (!(
less(*cursor_2, *cursor_1))) {
1550 if constexpr (Nesting::is_nested) {
1571 std::min(a.value, b.value),
1572 std::max(a.value, b.value)
1573 }, EMPTY_SET_VALUE});
1586 return Index(cursor->second);
1616 auto cursor_1 = first.begin();
1617 const auto &cursor_end_1 = first.end();
1619 for (;cursor_1 != cursor_end_1; cursor_1++) {
1620 if (!PropertyEqual()(cursor_1->get_key(), p)) {
1624 return register_set<true>(std::move(new_set));
1648 return Index(EMPTY_SET_VALUE);
1651 const Index &a = std::min(_a, _b);
1652 const Index &b = std::max(_a, _b);
1674 auto cursor_1 = first.begin();
1675 const auto &cursor_end_1 = first.end();
1676 auto cursor_2 = second.begin();
1677 const auto &cursor_end_2 = second.end();
1679 while (cursor_1 != cursor_end_1 && cursor_2 != cursor_end_2)
1681 if (
less(*cursor_1,*cursor_2)) {
1684 if (!(
less(*cursor_2, *cursor_1))) {
1685 if constexpr (Nesting::is_nested) {
1704 }
else if (ret != b) {
1720 return Index(cursor->second);
1745 std::function<
bool(
const PropertyT &)> filter_func,
1754 auto cursor = cache.find(s);
1756 if (cursor == cache.end()) {
1759 if (filter_func(value)) {
1767 cache.insert({s, new_index.
value});
1775 return Index(new_index);
1779 return Index(cursor->second);
1790 std::stringstream s;
1810 std::stringstream s;
1813 s <<
" " <<
"Unions: " <<
"(Count: " <<
unions.size() <<
")\n";
1815 s <<
" {" << i.first <<
" -> " << i.second <<
"} \n";
1819 s <<
" " <<
"Differences:" <<
"(Count: " <<
differences.size() <<
")\n";
1821 s <<
" {" << i.first <<
" -> " << i.second <<
"} \n";
1825 s <<
" " <<
"Intersections: " <<
"(Count: " <<
intersections.size() <<
")\n";
1827 s <<
" {" << i.first <<
" -> " << i.second <<
"} \n";
1831 s <<
" " <<
"Subsets: " <<
"(Count: " <<
subsets.size() <<
")\n";
1833 s <<
" " << i.first <<
" -> " << (i.second ==
SUBSET ?
"sub" :
"sup") <<
"\n";
1837 s <<
" " <<
"PropertySets: " <<
"(Count: " <<
property_set_map.size() <<
")\n";
1851#ifdef LHF_ENABLE_PERFORMANCE_METRICS
1860 std::stringstream s;
1861 s <<
"Performance Profile: \n";
1862 for (
auto &p :
perf) {
1863 s << p.first <<
"\n"
1889 typename PropertyLess = DefaultLess<PropertyT>,
1890 typename PropertyHash = DefaultHash<PropertyT>,
1891 typename PropertyEqual = DefaultEqual<PropertyT>,
1892 typename PropertyPrinter = DefaultPrinter<PropertyT>>
1904 return value == EMPTY_SET_VALUE;
1963 return Index(cursor->second);
The main LatticeHashForest structure. This class can be used as-is with a type or derived for additio...
typename Nesting::template PropertyElement< PropertyLess, PropertyHash, PropertyEqual, PropertyPrinter > PropertyElement
HashMap< String, OperationPerf > perf
Index register_set(PropertySet &&c)
Index set_insert_single(const Index &a, const PropertyElement &b)
Inserts a single element from a given set (and returns the index of the set). This is a wrapper over ...
HashMap< OperationNode, IndexValue > BinaryOperationMap
Index set_remove_single(const Index &a, const PropertyElement &b)
Removes a single element from a given set (and returns the index of the set). This is a wrapper over ...
void store_subset(const Index &a, const Index &b)
Stores index a as the subset of index b if a < b, else stores index a as the superset of index b
Index register_set(const PropertySet &c)
Inserts a (or gets an existing) set into property set storage.
static bool less(const PropertyElement &a, const PropertyElement &b)
Less than comparator for operations. You MUST use this instead of directly using anything else like "...
BinaryOperationMap unions
BinaryOperationMap intersections
String property_set_to_string(const Index &idx)
BinaryOperationMap differences
Index set_intersection(const Index &_a, const Index &_b)
Calculates, or returns a cached result of the intersection of a and b
PropertySetMap property_set_map
String dump() const
Dumps the current state of the LHF to a string.
Index register_set(const PropertySet &c, bool &cold)
Inserts a (or gets an existing) set into property set storage, and reports whether this set was alrea...
std::size_t size_of(const Index &index) const
Returns the size of the set at index
Index register_set(PropertySet &&c, bool &cold)
std::unordered_map< const PropertySet *, IndexValue, SetHash< PropertySet, PropertyElement, typename PropertyElement::Hash >, SetEqual< PropertySet, PropertyElement, typename PropertyElement::FullEqual > > PropertySetMap
HashMap< OperationNode, SubsetRelation > subsets
SubsetRelation is_subset(const Index &a, const Index &b) const
Returns whether we currently know whether a is a subset or a superset of b.
String dump_perf() const
Dumps a performance information.
Index set_filter(Index s, std::function< bool(const PropertyT &)> filter_func, HashMap< IndexValue, IndexValue > &cache)
Filters a set based on a criterion function. This is supposed to be an abstract filtering mechanism t...
static String property_set_to_string(const PropertySet &set)
Converts the property set to a string.
Index set_union(const Index &_a, const Index &_b)
Calculates, or returns a cached result of the union of a and b
bool is_empty(const Index &i) const
Vector< UniquePointer< PropertySet > > property_sets
Index register_set_single(const PropertyElement &c)
Inserts a (or gets an existing) single-element set into property set storage.
static bool less_key(const PropertyElement &a, const PropertyT &b)
std::vector< PropertyElement > PropertySet
static bool equal(const PropertyElement &a, const PropertyElement &b)
Equality comparator for operations. You MUST use this instead of directly using anything else like "<...
const PropertySet & get_value(const Index &index) const
Gets the actual property set specified by index.
static bool equal_key(const PropertyElement &a, const PropertyT &b)
PerformanceStatistics stat
Optional< PropertyElement > find_key(const Index &index, const PropertyT &p) const
Finds a property element in the set based on the key provided.
Index set_difference(const Index &a, const Index &b)
Calculates, or returns a cached result of the difference of a from b
LatticeHashForest(RefList reflist={})
typename Nesting::LHFReferenceList RefList
Index set_remove_single_key(const Index &a, const PropertyT &p)
Removes a single element from a given set if the "key" element matches.
bool contains(const Index &index, const PropertyElement &prop) const
Determines whether the property set at index contains the element prop or not.
friend std::ostream & operator<<(std::ostream &os, const LatticeHashForest &obj)
HashMap< IndexValue, IndexValue > UnaryOperationMap
void prepare_vector_set(PropertySet &c)
std::size_t property_set_count() const
Returns the total number of property sets currently in the LHF.
Index register_set_single(const PropertyElement &c, bool &cold)
Inserts a (or gets an existing) single-element set into the property set storage, and reports whether...
Describes an optional value of some type T. The value may either be present or absent.
static Optional absent()
Explicitly creates an absent value.
const T & get()
Gets the underlying value. Throws an exception if it is absent.
bool is_present()
Informs if the value is present.
#define LHF_PERFORM_BINARY_NESTED_OPERATION(__op_name, __reflist, __arg1, __arg2)
Actually enables the nesting. this must be placed in the operation body where the nested behaviour is...
#define LHF_PROPERTY_SET_INDEX_VALID(__index)
Check whether the index is a valid index within the property set.
#define LHF_PROPERTY_SET_INTEGRITY_VALID(__set)
Macro and switch for verify_property_set_integrity inside LHF.
#define LHF_PUSH_RANGE(__cont, __start, __end)
Pushes a range of elements to a PropertySet using an iterator. Use this when implementing operations.
#define LHF_PERF_INC(__oper, __category)
Increments the invocation count of the given category and operator.
#define LHF_PROPERTY_SET_PAIR_UNEQUAL(__index1, __index2)
Check whether the pair of indexes are unequal. Used for sanity checking.
#define LHF_PROPERTY_SET_PAIR_VALID(__index1, __index2)
Check whether the pair of indexes is a valid within the property set.
#define LHF_PUSH_ONE(__cont, __val)
Pushes one element to a PropertySet. Use this when implementing operations.
#define LHF_BINARY_NESTED_OPERATION(__op_name)
Declares a struct that enables the recursive/nesting behaviour of an operation.
#define LHF_REGISTER_SET_INTERNAL(__set, __cold)
Registers a set with behaviours defined for internal processing.
Any configurable constants go into this file.
#define LHF_SORTED_VECTOR_BINARY_SEARCH_THRESHOLD
std::ostream & operator<<(std::ostream &os, const OperationNode &op)
std::equal_to< T > DefaultEqual
std::size_t compose_hash(const std::size_t prev, T next)
Composes a preexisting hash with another variable. Useful for Hashing containers. Adapted from boost:...
std::unordered_map< K, V > HashMap
std::unordered_set< T > HashSet
std::map< K, V > OrderedMap
std::unique_ptr< T > UniquePointer
std::hash< T > DefaultHash
SubsetRelation
Used to store a subset relation between two set indices. Because the index pair must be in sorted ord...
std::less< T > DefaultLess
Enables basic code-based profiling metrics in LHF.
#define __lhf_calc_functime(__stat)
Creates a timer object to capture duration of the current function when going out of scope.
Thrown if an Optional is accessed when the value is absent.
AbsentValueAccessError(const std::string &message)
Struct that is thrown on an assertion failure.
AssertError(const std::string &message)
Index returned by an operation. The struct ensures type safety and possible future extensions.
bool operator>(const Index &b) const
bool operator!=(const Index &b) const
bool operator==(const Index &b) const
Index(IndexValue idx=EMPTY_SET_VALUE)
bool operator<(const Index &b) const
An LHF-like structure for scalar values. It does not implement any special operations besides dedupli...
Index register_value(const PropertyT &c)
Inserts a (or gets an existing) element into property storage.
Vector< UniquePointer< PropertyT > > property_list
std::unordered_map< PropertyT *, IndexValue, PropertyHash, PropertyEqual > PropertyMap
std::size_t operator()(const Index &idx) const
Index returned by an operation. The struct ensures type safety and possible future extensions.
bool operator<(const Index &b) const
bool operator!=(const Index &b) const
friend std::ostream & operator<<(std::ostream &os, const Index &obj)
bool operator>(const Index &b) const
Index(IndexValue idx=EMPTY_SET_VALUE)
bool operator==(const Index &b) const
This forces a comparison of both the key and the value instead of only the key. Required in instances...
bool operator()(const PropertyElement &a, const PropertyElement &b) const
std::size_t operator()(const PropertyElement &p) const
Type for the elements for a property set in the nested. The template arguments are for the 'key' type...
void apply_internal(ChildValueList &ret, const LHFReferenceList &lhf, const ChildValueList &arg_value, std::index_sequence< Indices... >) const
Actually performs the template "apply" operation. This sets up template syntax to go over each elemen...
PropertyElement(const PropertyT &key, const ChildValueList &value)
PropertyT InterfaceKeyType
Key type is made available here if required by user.
PropertyT InterfaceValueType
Value type is made available here if required by user.
const PropertyT & get_key() const
Gets the 'key' value.
bool operator<(const PropertyElement &b) const
const ChildValueList & get_value() const
Gets the nested value. It returns a tuple of values containing indices to each of the nested sets of ...
friend std::ostream & operator<<(std::ostream &os, const PropertyElement &obj)
PropertyElement apply(const LHFReferenceList &lhf, const PropertyElement &arg) const
Performs the "apply" operation on the list of nested children specified by the Operation parameter.
bool operator==(const PropertyElement &b) const
Describes the standard nesting structure. Act as "non-leaf" nodes in a tree of nested LHFs.
std::tuple< typename ChildT::Index... > ChildValueList
std::tuple< ChildT &... > LHFReferenceList
Reference list. References to all the nested LHFs are presented here.
static constexpr bool is_nested
Compile-time value that says this is nested.
static constexpr std::size_t num_children
Placeholder value to mark the reference lists and value lists as empty.
This forces a comparison of both the key and the value instead of only the key. Required in instances...
bool operator()(const PropertyElement &a, const PropertyElement &b) const
std::size_t operator()(const PropertyElement &p) const
Base-case type for the elements for a property set. The template arguments are for the 'key' type.
PropertyElement(const PropertyT &value)
PropertyT InterfaceValueType
Value type is made available here if required by user.
bool operator==(const PropertyElement &b) const
const PropertyT & get_key() const
Gets the 'key' value.
bool operator<(const PropertyElement &b) const
PropertyElement apply() const
Performs an "apply" operation. In the base case it is an identity operation and does not do anything.
PropertyT InterfaceKeyType
Key type is made available here if required by user.
const PropertyT & get_value() const
Gets the nested value. In the base case it simply returns the key.
The nesting type for non-nested data structures. Act as "leaf" nodes in a tree of nested LHFs.
static constexpr std::size_t num_children
Compile-time value that says there are no nested children.
static constexpr bool is_nested
Compile-time value that says this is not nested.
This struct contains the information about the operands of an operation (union, intersection,...
std::string to_string() const
bool operator<(const OperationNode &op) const
bool operator==(const OperationNode &op) const
Operation performance Statistics.
size_t equal_hits
Number of equal hits (both arguments consist of the same set)
size_t hits
Number of direct hits (operation pair in map)
Generic Equality comparator for set types.
bool operator()(const SetT *a, const SetT *b) const
std::size_t operator()(const SetT *k) const
Generic Less-than comparator for set types.
bool operator()(const OrderedSetT *a, const OrderedSetT *b) const
Thrown if code region is unreachable.
Unreachable(const std::string &message="Hit a branch marked as unreachable.")
std::size_t operator()(const lhf::OperationNode &k) const