| 1 | /*
 | 
| 2 |  * Souffle - A Datalog Compiler
 | 
| 3 |  * Copyright (c) 2021, The Souffle Developers. All rights reserved
 | 
| 4 |  * Licensed under the Universal Permissive License v 1.0 as shown at:
 | 
| 5 |  * - https://opensource.org/licenses/UPL
 | 
| 6 |  * - <souffle root>/licenses/SOUFFLE-UPL.txt
 | 
| 7 |  */
 | 
| 8 | 
 | 
| 9 | /************************************************************************
 | 
| 10 |  *
 | 
| 11 |  * @file ContainerUtil.h
 | 
| 12 |  *
 | 
| 13 |  * @brief Datalog project utilities
 | 
| 14 |  *
 | 
| 15 |  ***********************************************************************/
 | 
| 16 | 
 | 
| 17 | #pragma once
 | 
| 18 | 
 | 
| 19 | #include "souffle/utility/DynamicCasting.h"
 | 
| 20 | #include "souffle/utility/Iteration.h"
 | 
| 21 | #include "souffle/utility/MiscUtil.h"
 | 
| 22 | #include "souffle/utility/Types.h"
 | 
| 23 | 
 | 
| 24 | #include <algorithm>
 | 
| 25 | #include <functional>
 | 
| 26 | #include <iterator>
 | 
| 27 | #include <map>
 | 
| 28 | #include <set>
 | 
| 29 | #include <type_traits>
 | 
| 30 | #include <utility>
 | 
| 31 | #include <vector>
 | 
| 32 | 
 | 
| 33 | namespace souffle {
 | 
| 34 | 
 | 
| 35 | // -------------------------------------------------------------------------------
 | 
| 36 | //                           General Container Utilities
 | 
| 37 | // -------------------------------------------------------------------------------
 | 
| 38 | 
 | 
| 39 | /**
 | 
| 40 |  * Use to range-for iterate in reverse.
 | 
| 41 |  * Assumes `std::rbegin` and `std::rend` are defined for type `A`.
 | 
| 42 |  */
 | 
| 43 | template <typename A>
 | 
| 44 | struct reverse {
 | 
| 45 |     reverse(A& iterable) : iterable(iterable) {}
 | 
| 46 |     A& iterable;
 | 
| 47 | 
 | 
| 48 |     auto begin() {
 | 
| 49 |         return std::rbegin(iterable);
 | 
| 50 |     }
 | 
| 51 | 
 | 
| 52 |     auto end() {
 | 
| 53 |         return std::rend(iterable);
 | 
| 54 |     }
 | 
| 55 | };
 | 
| 56 | 
 | 
| 57 | /**
 | 
| 58 |  * A utility to check generically whether a given element is contained in a given
 | 
| 59 |  * container.
 | 
| 60 |  */
 | 
| 61 | template <typename C, typename = std::enable_if_t<!is_associative<C>>>
 | 
| 62 | bool contains(const C& container, const typename C::value_type& element) {
 | 
| 63 |     return std::find(container.begin(), container.end(), element) != container.end();
 | 
| 64 | }
 | 
| 65 | 
 | 
| 66 | /**
 | 
| 67 |  * A utility to check generically whether a given key exists within a given
 | 
| 68 |  * associative container.
 | 
| 69 |  */
 | 
| 70 | template <typename C, typename A, typename = std::enable_if_t<is_associative<C>>>
 | 
| 71 | bool contains(const C& container, A&& element) {
 | 
| 72 |     return container.find(element) != container.end();
 | 
| 73 | }
 | 
| 74 | 
 | 
| 75 | /**
 | 
| 76 |  * Returns the first element in a container that satisfies a given predicate,
 | 
| 77 |  * nullptr otherwise.
 | 
| 78 |  */
 | 
| 79 | template <typename C, typename F>
 | 
| 80 | auto getIf(C&& container, F&& pred) {
 | 
| 81 |     auto it = std::find_if(container.begin(), container.end(), std::forward<F>(pred));
 | 
| 82 |     return it == container.end() ? nullptr : *it;
 | 
| 83 | }
 | 
| 84 | 
 | 
| 85 | /**
 | 
| 86 |  * Get value for a given key; if not found, return default value.
 | 
| 87 |  */
 | 
| 88 | template <typename C, typename A, typename = std::enable_if_t<is_associative<C>>>
 | 
| 89 | typename C::mapped_type const& getOr(
 | 
| 90 |         const C& container, A&& key, const typename C::mapped_type& defaultValue) {
 | 
| 91 |     auto it = container.find(key);
 | 
| 92 | 
 | 
| 93 |     if (it != container.end()) {
 | 
| 94 |         return it->second;
 | 
| 95 |     } else {
 | 
| 96 |         return defaultValue;
 | 
| 97 |     }
 | 
| 98 | }
 | 
| 99 | 
 | 
| 100 | /**
 | 
| 101 |  * Append elements to a container
 | 
| 102 |  */
 | 
| 103 | template <class C, typename R>
 | 
| 104 | void append(C& container, R&& range) {
 | 
| 105 |     container.insert(container.end(), std::begin(range), std::end(range));
 | 
| 106 | }
 | 
| 107 | 
 | 
| 108 | /**
 | 
| 109 |  * A utility function enabling the creation of a vector with a fixed set of
 | 
| 110 |  * elements within a single expression. This is the base case covering empty
 | 
| 111 |  * vectors.
 | 
| 112 |  */
 | 
| 113 | template <typename T>
 | 
| 114 | std::vector<T> toVector() {
 | 
| 115 |     return std::vector<T>();
 | 
| 116 | }
 | 
| 117 | 
 | 
| 118 | /**
 | 
| 119 |  * A utility function enabling the creation of a vector with a fixed set of
 | 
| 120 |  * elements within a single expression. This is the step case covering vectors
 | 
| 121 |  * of arbitrary length.
 | 
| 122 |  */
 | 
| 123 | template <typename T, typename... R>
 | 
| 124 | std::vector<T> toVector(T first, R... rest) {
 | 
| 125 |     // Init-lists are effectively const-arrays. You can't `move` out of them.
 | 
| 126 |     // Combine with `vector`s not having variadic constructors, can't do:
 | 
| 127 |     //   `vector{Own<A>{}, Own<A>{}}`
 | 
| 128 |     // This is inexcusably awful and defeats the purpose of having init-lists.
 | 
| 129 |     std::vector<T> xs;
 | 
| 130 |     T ary[] = {std::move(first), std::move(rest)...};
 | 
| 131 |     for (auto& x : ary) {
 | 
| 132 |         xs.push_back(std::move(x));
 | 
| 133 |     }
 | 
| 134 |     return xs;
 | 
| 135 | }
 | 
| 136 | 
 | 
| 137 | /**
 | 
| 138 |  * A utility function enabling the creation of a vector of pointers.
 | 
| 139 |  */
 | 
| 140 | template <typename A = void, typename T, typename U = std::conditional_t<std::is_same_v<A, void>, T, A>>
 | 
| 141 | std::vector<U*> toPtrVector(const VecOwn<T>& v) {
 | 
| 142 |     std::vector<U*> res;
 | 
| 143 |     for (auto& e : v) {
 | 
| 144 |         res.push_back(e.get());
 | 
| 145 |     }
 | 
| 146 |     return res;
 | 
| 147 | }
 | 
| 148 | 
 | 
| 149 | // -------------------------------------------------------------------------------
 | 
| 150 | //                             Equality Utilities
 | 
| 151 | // -------------------------------------------------------------------------------
 | 
| 152 | 
 | 
| 153 | /**
 | 
| 154 |  * Cast the values, from baseType to toType and compare using ==. (if casting fails -> return false.)
 | 
| 155 |  *
 | 
| 156 |  * @tparam baseType, initial Type of values
 | 
| 157 |  * @tparam toType, type where equality comparison takes place.
 | 
| 158 |  */
 | 
| 159 | template <typename toType, typename baseType>
 | 
| 160 | bool castEq(const baseType* left, const baseType* right) {
 | 
| 161 |     if (auto castedLeft = as<toType>(left)) {
 | 
| 162 |         if (auto castedRight = as<toType>(right)) {
 | 
| 163 |             return castedLeft == castedRight;
 | 
| 164 |         }
 | 
| 165 |     }
 | 
| 166 |     return false;
 | 
| 167 | }
 | 
| 168 | 
 | 
| 169 | /**
 | 
| 170 |  * A functor class supporting the values pointers are pointing to.
 | 
| 171 |  */
 | 
| 172 | template <typename T>
 | 
| 173 | struct comp_deref {
 | 
| 174 |     bool operator()(const T& a, const T& b) const {
 | 
| 175 |         if (a == nullptr) {
 | 
| 176 |             return false;
 | 
| 177 |         }
 | 
| 178 |         if (b == nullptr) {
 | 
| 179 |             return false;
 | 
| 180 |         }
 | 
| 181 |         return *a == *b;
 | 
| 182 |     }
 | 
| 183 | };
 | 
| 184 | 
 | 
| 185 | /**
 | 
| 186 |  * A function testing whether two containers are equal with the given Comparator.
 | 
| 187 |  */
 | 
| 188 | template <typename Container, typename Comparator>
 | 
| 189 | bool equal_targets(const Container& a, const Container& b, const Comparator& comp) {
 | 
| 190 |     // check reference
 | 
| 191 |     if (&a == &b) {
 | 
| 192 |         return true;
 | 
| 193 |     }
 | 
| 194 | 
 | 
| 195 |     // check size
 | 
| 196 |     if (a.size() != b.size()) {
 | 
| 197 |         return false;
 | 
| 198 |     }
 | 
| 199 | 
 | 
| 200 |     // check content
 | 
| 201 |     return std::equal(a.begin(), a.end(), b.begin(), comp);
 | 
| 202 | }
 | 
| 203 | 
 | 
| 204 | /**
 | 
| 205 |  * A function testing whether two containers of pointers are referencing equivalent
 | 
| 206 |  * targets.
 | 
| 207 |  */
 | 
| 208 | template <typename T, template <typename...> class Container>
 | 
| 209 | bool equal_targets(const Container<T*>& a, const Container<T*>& b) {
 | 
| 210 |     return equal_targets(a, b, comp_deref<T*>());
 | 
| 211 | }
 | 
| 212 | 
 | 
| 213 | /**
 | 
| 214 |  * A function testing whether two containers of unique pointers are referencing equivalent
 | 
| 215 |  * targets.
 | 
| 216 |  */
 | 
| 217 | template <typename T, template <typename...> class Container>
 | 
| 218 | bool equal_targets(const Container<Own<T>>& a, const Container<Own<T>>& b) {
 | 
| 219 |     return equal_targets(a, b, comp_deref<Own<T>>());
 | 
| 220 | }
 | 
| 221 | 
 | 
| 222 | #ifdef _MSC_VER
 | 
| 223 | // issue:
 | 
| 224 | // https://developercommunity.visualstudio.com/t/c-template-template-not-recognized-as-class-templa/558979
 | 
| 225 | template <typename T>
 | 
| 226 | bool equal_targets(const std::vector<Own<T>>& a, const std::vector<Own<T>>& b) {
 | 
| 227 |     return equal_targets(a, b, comp_deref<Own<T>>());
 | 
| 228 | }
 | 
| 229 | 
 | 
| 230 | template <typename T>
 | 
| 231 | bool equal_targets(const std::vector<T>& a, const std::vector<T>& b) {
 | 
| 232 |     return equal_targets(a, b, comp_deref<T>());
 | 
| 233 | }
 | 
| 234 | #endif
 | 
| 235 | 
 | 
| 236 | /**
 | 
| 237 |  * A function testing whether two maps of unique pointers are referencing to equivalent
 | 
| 238 |  * targets.
 | 
| 239 |  */
 | 
| 240 | template <typename Key, typename Value, typename Cmp>
 | 
| 241 | bool equal_targets(const std::map<Key, Own<Value>, Cmp>& a, const std::map<Key, Own<Value>, Cmp>& b) {
 | 
| 242 |     auto comp = comp_deref<Own<Value>>();
 | 
| 243 |     return equal_targets(
 | 
| 244 |             a, b, [&comp](auto& a, auto& b) { return a.first == b.first && comp(a.second, b.second); });
 | 
| 245 | }
 | 
| 246 | 
 | 
| 247 | /**
 | 
| 248 |  * A function testing whether two maps are equivalent using projected values.
 | 
| 249 |  */
 | 
| 250 | template <typename Key, typename Value, typename Cmp, typename F>
 | 
| 251 | bool equal_targets_map(const std::map<Key, Value, Cmp>& a, const std::map<Key, Value, Cmp>& b, F&& comp) {
 | 
| 252 |     return equal_targets(
 | 
| 253 |             a, b, [&](auto& a, auto& b) { return a.first == b.first && comp(a.second, b.second); });
 | 
| 254 | }
 | 
| 255 | 
 | 
| 256 | // -------------------------------------------------------------------------------
 | 
| 257 | //                             Checking Utilities
 | 
| 258 | // -------------------------------------------------------------------------------
 | 
| 259 | template <typename R>
 | 
| 260 | bool allValidPtrs(R const& range) {
 | 
| 261 |     return std::all_of(range.begin(), range.end(), [](auto&& p) { return (bool)p; });
 | 
| 262 | }
 | 
| 263 | 
 | 
| 264 | }  // namespace souffle
 | 
| 265 | 
 | 
| 266 | namespace std {
 | 
| 267 | template <typename Iter, typename F>
 | 
| 268 | struct iterator_traits<souffle::TransformIterator<Iter, F>> {
 | 
| 269 |     using iter_t = std::iterator_traits<Iter>;
 | 
| 270 |     using iter_tag = typename iter_t::iterator_category;
 | 
| 271 |     using difference_type = typename iter_t::difference_type;
 | 
| 272 |     using reference = decltype(std::declval<F&>()(*std::declval<Iter>()));
 | 
| 273 |     using value_type = std::remove_cv_t<std::remove_reference_t<reference>>;
 | 
| 274 |     using iterator_category = std::conditional_t<std::is_base_of_v<std::random_access_iterator_tag, iter_tag>,
 | 
| 275 |             std::random_access_iterator_tag, iter_tag>;
 | 
| 276 | };
 | 
| 277 | }  // namespace std
 |