| 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 FunctionalUtil.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 <algorithm>
 | 
| 23 | #include <cassert>
 | 
| 24 | #include <functional>
 | 
| 25 | #include <map>
 | 
| 26 | #include <set>
 | 
| 27 | #include <type_traits>
 | 
| 28 | #include <utility>
 | 
| 29 | #include <vector>
 | 
| 30 | 
 | 
| 31 | namespace souffle {
 | 
| 32 | 
 | 
| 33 | // -------------------------------------------------------------------------------
 | 
| 34 | //                              Functional Utils
 | 
| 35 | // -------------------------------------------------------------------------------
 | 
| 36 | 
 | 
| 37 | /**
 | 
| 38 |  * A functor comparing the dereferenced value of a pointer type utilizing a
 | 
| 39 |  * given comparator. Its main use case are sets of non-null pointers which should
 | 
| 40 |  * be ordered according to the value addressed by the pointer.
 | 
| 41 |  */
 | 
| 42 | template <typename T, typename C = std::less<T>>
 | 
| 43 | struct deref_less {
 | 
| 44 |     bool operator()(const T* a, const T* b) const {
 | 
| 45 |         return C()(*a, *b);
 | 
| 46 |     }
 | 
| 47 | };
 | 
| 48 | 
 | 
| 49 | // -------------------------------------------------------------------------------
 | 
| 50 | //                               Lambda Utils
 | 
| 51 | // -------------------------------------------------------------------------------
 | 
| 52 | 
 | 
| 53 | /**
 | 
| 54 |  * A type trait enabling the deduction of type properties of lambdas.
 | 
| 55 |  *
 | 
| 56 |  * source:
 | 
| 57 |  * https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda
 | 
| 58 |  */
 | 
| 59 | template <typename A, typename = void>
 | 
| 60 | struct lambda_traits;
 | 
| 61 | 
 | 
| 62 | template <typename A>
 | 
| 63 | struct lambda_traits<A, std::enable_if_t<std::is_class_v<std::decay_t<A>>>>
 | 
| 64 |         : lambda_traits<decltype(&std::decay_t<A>::operator())> {};
 | 
| 65 | 
 | 
| 66 | #define LAMBDA_TYPE_INFO_REM_CTOR(...) __VA_ARGS__
 | 
| 67 | #define LAMBDA_TYPE_INFO_SPEC(kind, cv, var, is_var)                                 \
 | 
| 68 |     struct lambda_traits<R(kind)(Args... LAMBDA_TYPE_INFO_REM_CTOR var) cv, void> {  \
 | 
| 69 |         using arity = std::integral_constant<std::size_t, sizeof...(Args)>;          \
 | 
| 70 |         using is_variadic = std::integral_constant<bool, is_var>;                    \
 | 
| 71 |         using is_const = std::is_const<int cv>;                                      \
 | 
| 72 |                                                                                      \
 | 
| 73 |         using result_type = R;                                                       \
 | 
| 74 |                                                                                      \
 | 
| 75 |         template <std::size_t i>                                                     \
 | 
| 76 |         using arg = typename std::tuple_element<i, std::tuple<Args..., void>>::type; \
 | 
| 77 |     };
 | 
| 78 | 
 | 
| 79 | #define LAMBDA_TYPE_INFO_MEMBER(cv, var, is_var)        \
 | 
| 80 |     template <typename C, typename R, typename... Args> \
 | 
| 81 |     LAMBDA_TYPE_INFO_SPEC(C::*, cv, var, is_var)
 | 
| 82 | 
 | 
| 83 | #define LAMBDA_TYPE_INFO_FUNC(var, is_var)  \
 | 
| 84 |     template <typename R, typename... Args> \
 | 
| 85 |     LAMBDA_TYPE_INFO_SPEC(*, , var, is_var) \
 | 
| 86 |     template <typename R, typename... Args> \
 | 
| 87 |     LAMBDA_TYPE_INFO_SPEC(&, , var, is_var)
 | 
| 88 | 
 | 
| 89 | LAMBDA_TYPE_INFO_MEMBER(const, (, ...), 1)
 | 
| 90 | LAMBDA_TYPE_INFO_MEMBER(const, (), 0)
 | 
| 91 | LAMBDA_TYPE_INFO_MEMBER(, (, ...), 1)
 | 
| 92 | LAMBDA_TYPE_INFO_MEMBER(, (), 0)
 | 
| 93 | LAMBDA_TYPE_INFO_FUNC((, ...), 1)
 | 
| 94 | LAMBDA_TYPE_INFO_FUNC((), 0)
 | 
| 95 | #undef LAMBDA_TYPE_INFO_REM_CTOR
 | 
| 96 | #undef LAMBDA_TYPE_INFO_SPEC
 | 
| 97 | #undef LAMBDA_TYPE_INFO_MEMBER
 | 
| 98 | #undef LAMBDA_TYPE_INFO_FUNC
 | 
| 99 | 
 | 
| 100 | namespace detail {
 | 
| 101 | 
 | 
| 102 | template <typename F>
 | 
| 103 | struct LambdaFix {
 | 
| 104 |     F f;
 | 
| 105 |     template <typename... Args>
 | 
| 106 |     auto operator()(Args&&... args) -> decltype(f(*this, std::forward<Args>(args)...)) {
 | 
| 107 |         return f(*this, std::forward<Args>(args)...);
 | 
| 108 |     }
 | 
| 109 | };
 | 
| 110 | 
 | 
| 111 | }  // namespace detail
 | 
| 112 | 
 | 
| 113 | template <typename F /* f -> ... */>
 | 
| 114 | detail::LambdaFix<F> fix(F f) {
 | 
| 115 |     return {std::move(f)};
 | 
| 116 | }
 | 
| 117 | 
 | 
| 118 | // -------------------------------------------------------------------------------
 | 
| 119 | //                              General Algorithms
 | 
| 120 | // -------------------------------------------------------------------------------
 | 
| 121 | 
 | 
| 122 | namespace detail {
 | 
| 123 | constexpr auto coerceToBool = [](auto&& x) { return (bool)x; };
 | 
| 124 | 
 | 
| 125 | template <typename C, typename F /* : A -> B */>
 | 
| 126 | auto mapVector(C& xs, F&& f) {
 | 
| 127 |     std::vector<decltype(f(xs[0]))> ys;
 | 
| 128 |     ys.reserve(xs.size());
 | 
| 129 |     for (auto&& x : xs) {
 | 
| 130 |         ys.push_back(f(x));
 | 
| 131 |     }
 | 
| 132 |     return ys;
 | 
| 133 | }
 | 
| 134 | }  // namespace detail
 | 
| 135 | 
 | 
| 136 | /**
 | 
| 137 |  * A generic test checking whether all elements within a container satisfy a
 | 
| 138 |  * certain predicate.
 | 
| 139 |  *
 | 
| 140 |  * @param c the container
 | 
| 141 |  * @param p the predicate
 | 
| 142 |  * @return true if for all elements x in c the predicate p(x) is true, false
 | 
| 143 |  *          otherwise; for empty containers the result is always true
 | 
| 144 |  */
 | 
| 145 | template <typename Container, typename UnaryPredicate>
 | 
| 146 | bool all_of(const Container& c, UnaryPredicate p) {
 | 
| 147 |     return std::all_of(c.begin(), c.end(), p);
 | 
| 148 | }
 | 
| 149 | 
 | 
| 150 | /**
 | 
| 151 |  * A generic test checking whether any elements within a container satisfy a
 | 
| 152 |  * certain predicate.
 | 
| 153 |  *
 | 
| 154 |  * @param c the container
 | 
| 155 |  * @param p the predicate
 | 
| 156 |  * @return true if there is an element x in c such that predicate p(x) is true, false
 | 
| 157 |  *          otherwise; for empty containers the result is always false
 | 
| 158 |  */
 | 
| 159 | template <typename Container, typename UnaryPredicate>
 | 
| 160 | bool any_of(const Container& c, UnaryPredicate p) {
 | 
| 161 |     return std::any_of(c.begin(), c.end(), p);
 | 
| 162 | }
 | 
| 163 | 
 | 
| 164 | /**
 | 
| 165 |  * A generic test checking whether all elements within a container satisfy a
 | 
| 166 |  * certain predicate.
 | 
| 167 |  *
 | 
| 168 |  * @param c the container
 | 
| 169 |  * @param p the predicate
 | 
| 170 |  * @return true if for all elements x in c the predicate p(x) is true, false
 | 
| 171 |  *          otherwise; for empty containers the result is always true
 | 
| 172 |  */
 | 
| 173 | template <typename Container, typename UnaryPredicate>
 | 
| 174 | bool none_of(const Container& c, UnaryPredicate p) {
 | 
| 175 |     return std::none_of(c.begin(), c.end(), p);
 | 
| 176 | }
 | 
| 177 | 
 | 
| 178 | /**
 | 
| 179 |  * Filter a vector to exclude certain elements.
 | 
| 180 |  */
 | 
| 181 | template <typename A, typename F>
 | 
| 182 | std::vector<A> filterNot(std::vector<A> xs, F&& f) {
 | 
| 183 |     xs.erase(std::remove_if(xs.begin(), xs.end(), std::forward<F>(f)), xs.end());
 | 
| 184 |     return xs;
 | 
| 185 | }
 | 
| 186 | 
 | 
| 187 | /**
 | 
| 188 |  * Filter a vector to include certain elements.
 | 
| 189 |  */
 | 
| 190 | template <typename A, typename F>
 | 
| 191 | std::vector<A> filter(std::vector<A> xs, F&& f) {
 | 
| 192 |     return filterNot(std::move(xs), [&](auto&& x) { return !f(x); });
 | 
| 193 | }
 | 
| 194 | 
 | 
| 195 | template <typename B, typename CrossCast = void, typename C>
 | 
| 196 | auto filterAs(C&& xs) {
 | 
| 197 |     return filterMap(std::forward<C>(xs), [](auto&& x) { return as<B, CrossCast>(x); });
 | 
| 198 | }
 | 
| 199 | 
 | 
| 200 | /**
 | 
| 201 |  * Fold left a sequence
 | 
| 202 |  */
 | 
| 203 | template <typename A, typename B, typename F /* : B -> A -> B */>
 | 
| 204 | B foldl(std::vector<A> xs, B zero, F&& f) {
 | 
| 205 |     B accum = std::move(zero);
 | 
| 206 |     for (auto&& x : xs)
 | 
| 207 |         accum = f(std::move(accum), std::move(x));
 | 
| 208 |     return accum;
 | 
| 209 | }
 | 
| 210 | 
 | 
| 211 | /**
 | 
| 212 |  * Fold left a non-empty sequence
 | 
| 213 |  */
 | 
| 214 | template <typename A, typename F /* : A -> A -> A */>
 | 
| 215 | auto foldl(std::vector<A> xs, F&& f) {
 | 
| 216 |     assert(!xs.empty() && "cannot foldl an empty sequence");
 | 
| 217 |     auto it = xs.begin();
 | 
| 218 |     A y = std::move(*it++);
 | 
| 219 |     for (; it != xs.end(); it++) {
 | 
| 220 |         y = f(std::move(y), std::move(*it));
 | 
| 221 |     }
 | 
| 222 |     return y;
 | 
| 223 | }
 | 
| 224 | 
 | 
| 225 | template <typename A, typename B, typename F /* : A -> B -> B */>
 | 
| 226 | B foldr(std::vector<A> xs, B zero, F&& f) {
 | 
| 227 |     B accum = std::move(zero);
 | 
| 228 |     for (auto&& x : reverse(xs))
 | 
| 229 |         accum = f(std::move(x), std::move(accum));
 | 
| 230 |     return accum;
 | 
| 231 | }
 | 
| 232 | 
 | 
| 233 | template <typename A, typename F /* : A -> A -> A */>
 | 
| 234 | auto foldr(std::vector<A> xs, F&& f) {
 | 
| 235 |     assert(!xs.empty() && "cannot foldr an empty sequence");
 | 
| 236 |     auto it = xs.rbegin();
 | 
| 237 |     A y = std::move(*it++);
 | 
| 238 |     for (; it != xs.rend(); it++) {
 | 
| 239 |         y = f(std::move(*it), std::move(y));
 | 
| 240 |     }
 | 
| 241 |     return y;
 | 
| 242 | }
 | 
| 243 | 
 | 
| 244 | /**
 | 
| 245 |  * Applies a function to each element of a vector and returns the results.
 | 
| 246 |  *
 | 
| 247 |  * Unlike `makeTransformRange`, this creates a transformed collection instead of a transformed view.
 | 
| 248 |  */
 | 
| 249 | template <typename A, typename F /* : A -> B */>
 | 
| 250 | auto map(std::vector<A>& xs, F&& f) {
 | 
| 251 |     return detail::mapVector(xs, std::forward<F>(f));
 | 
| 252 | }
 | 
| 253 | 
 | 
| 254 | template <typename A, typename F /* : A -> B */>
 | 
| 255 | auto map(const std::vector<A>& xs, F&& f) {
 | 
| 256 |     return detail::mapVector(xs, std::forward<F>(f));
 | 
| 257 | }
 | 
| 258 | 
 | 
| 259 | template <typename A, typename F /* : A -> B */>
 | 
| 260 | auto map(std::vector<A>&& xs, F&& f) {
 | 
| 261 |     return detail::mapVector(xs, std::forward<F>(f));
 | 
| 262 | }
 | 
| 263 | 
 | 
| 264 | template <typename A, typename F /* : A -> pointer_like<B> */>
 | 
| 265 | auto filterMap(const std::vector<A>& xs, F&& f) {
 | 
| 266 |     using R = decltype(f(xs[0]));
 | 
| 267 |     // not a pointer -> assume it's `std::optional`
 | 
| 268 |     using B = std::conditional_t<std::is_pointer_v<R>, R, std::decay_t<decltype(*std::declval<R>())>>;
 | 
| 269 |     std::vector<B> ys;
 | 
| 270 |     ys.reserve(xs.size());
 | 
| 271 |     for (auto&& x : xs) {
 | 
| 272 |         auto y = f(std::move(x));
 | 
| 273 |         if (y) {
 | 
| 274 |             if constexpr (std::is_pointer_v<R>)
 | 
| 275 |                 ys.push_back(std::move(y));
 | 
| 276 |             else  // assume it's `std::optional`
 | 
| 277 |                 ys.push_back(std::move(*y));
 | 
| 278 |         }
 | 
| 279 |     }
 | 
| 280 |     return ys;
 | 
| 281 | }
 | 
| 282 | 
 | 
| 283 | namespace detail {
 | 
| 284 | 
 | 
| 285 | template <typename It>
 | 
| 286 | constexpr bool IsLegacyIteratorOutput_v = std::is_reference_v<decltype(*std::declval<It>())>&&
 | 
| 287 |         std::is_move_assignable_v<std::remove_reference_t<decltype(*std::declval<It>())>>;
 | 
| 288 | 
 | 
| 289 | // HACK: Workaround r-ref collapsing w/ template parameters.
 | 
| 290 | template <typename C>
 | 
| 291 | struct filter {
 | 
| 292 |     static_assert(!std::is_reference_v<C>);
 | 
| 293 |     static constexpr bool has_output_iter = IsLegacyIteratorOutput_v<typename C::iterator>;
 | 
| 294 | 
 | 
| 295 |     template <typename F>
 | 
| 296 |     C operator()(C&& xs, F&& f) {
 | 
| 297 |         // TODO: replace w/ C++20 `std::erase_if`
 | 
| 298 |         if constexpr (has_output_iter) {
 | 
| 299 |             xs.erase(std::remove_if(xs.begin(), xs.end(), [&](auto&& x) { return !f(x); }), xs.end());
 | 
| 300 |         } else {
 | 
| 301 |             auto end = xs.end();
 | 
| 302 |             for (auto it = xs.begin(); it != end;)
 | 
| 303 |                 it = f(*it) ? ++it : xs.erase(it);
 | 
| 304 |         }
 | 
| 305 |         return std::move(xs);
 | 
| 306 |     }
 | 
| 307 | 
 | 
| 308 |     template <typename F, bool enable = std::is_copy_constructible_v<typename C::value_type>>
 | 
| 309 |     std::enable_if_t<enable, C> operator()(const C& xs, F&& f) {
 | 
| 310 |         C ys;
 | 
| 311 |         for (auto&& x : xs)
 | 
| 312 |             if (f(x)) {
 | 
| 313 |                 if constexpr (has_output_iter)
 | 
| 314 |                     ys.insert(ys.end(), x);
 | 
| 315 |                 else
 | 
| 316 |                     ys.insert(x);
 | 
| 317 |             }
 | 
| 318 |         return ys;
 | 
| 319 |     }
 | 
| 320 | };
 | 
| 321 | }  // namespace detail
 | 
| 322 | 
 | 
| 323 | template <typename C, typename F /* : C::element_type -> bool */>
 | 
| 324 | auto filter(C&& xs, F&& f) {
 | 
| 325 |     return detail::filter<std::decay_t<C>>{}(std::forward<C>(xs), std::forward<F>(f));
 | 
| 326 | }
 | 
| 327 | 
 | 
| 328 | template <typename C, typename F /* : C::element_type -> bool */>
 | 
| 329 | auto filterNot(C&& xs, F&& f) {
 | 
| 330 |     return filter(std::forward<C>(xs), [&](auto&& x) { return !f(x); });
 | 
| 331 | }
 | 
| 332 | 
 | 
| 333 | template <typename A, typename F /* : A -> B */>
 | 
| 334 | auto groupBy(std::vector<A> xs, F&& key) {
 | 
| 335 |     std::map<decltype(key(xs.front())), std::vector<A>> m;
 | 
| 336 |     for (auto&& x : xs)
 | 
| 337 |         m[key(x)].push_back(std::move(x));
 | 
| 338 |     return m;
 | 
| 339 | }
 | 
| 340 | 
 | 
| 341 | template <typename A, typename B, typename F /* : const A& -> const B& -> () */>
 | 
| 342 | void zipForEach(const std::vector<A>& xs, const std::vector<B>& ys, F&& f) {
 | 
| 343 |     for (size_t i = 0; i < std::min(xs.size(), ys.size()); i++)
 | 
| 344 |         f(xs[i], ys[i]);
 | 
| 345 | }
 | 
| 346 | 
 | 
| 347 | template <typename A, typename B, typename F /* : const A& -> const B& -> () */>
 | 
| 348 | auto zipMap(const std::vector<A>& xs, const std::vector<B>& ys, F&& f) {
 | 
| 349 |     size_t n = std::min(xs.size(), ys.size());
 | 
| 350 |     std::vector<decltype(f(xs.front(), ys.front()))> zs;
 | 
| 351 |     zs.reserve(n);
 | 
| 352 |     for (size_t i = 0; i < n; i++)
 | 
| 353 |         zs.push_back(f(xs[i], ys[i]));
 | 
| 354 |     return zs;
 | 
| 355 | }
 | 
| 356 | 
 | 
| 357 | template <typename A, typename B>
 | 
| 358 | std::vector<A> concat(std::vector<A> xs, std::vector<B> ys) {
 | 
| 359 |     for (auto&& y : ys)
 | 
| 360 |         xs.push_back(std::move(y));
 | 
| 361 | 
 | 
| 362 |     return xs;
 | 
| 363 | }
 | 
| 364 | 
 | 
| 365 | template <typename A, typename B>
 | 
| 366 | std::vector<A> concat(std::vector<A> xs, const range<B>& ys) {
 | 
| 367 |     for (A y : ys)
 | 
| 368 |         xs.push_back(std::move(y));
 | 
| 369 | 
 | 
| 370 |     return xs;
 | 
| 371 | }
 | 
| 372 | 
 | 
| 373 | template <typename A, typename B>
 | 
| 374 | std::vector<A> concat(std::vector<A> xs, B x) {
 | 
| 375 |     xs.push_back(std::move(x));
 | 
| 376 |     return xs;
 | 
| 377 | }
 | 
| 378 | 
 | 
| 379 | template <typename A, typename B>
 | 
| 380 | void append(std::vector<A>& xs, B&& y) {
 | 
| 381 |     xs = concat(std::move(xs), std::forward<B>(y));
 | 
| 382 | }
 | 
| 383 | 
 | 
| 384 | // -------------------------------------------------------------------------------
 | 
| 385 | //                               Set Utilities
 | 
| 386 | // -------------------------------------------------------------------------------
 | 
| 387 | 
 | 
| 388 | template <typename A>
 | 
| 389 | std::set<A> operator&(const std::set<A, std::less<A>>& lhs, const std::set<A, std::less<A>>& rhs) {
 | 
| 390 |     std::set<A> result;
 | 
| 391 |     std::set_intersection(
 | 
| 392 |             lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::inserter(result, result.begin()));
 | 
| 393 |     return result;
 | 
| 394 | }
 | 
| 395 | 
 | 
| 396 | template <typename A>
 | 
| 397 | std::set<A> operator|(const std::set<A, std::less<A>>& lhs, const std::set<A, std::less<A>>& rhs) {
 | 
| 398 |     std::set<A> result;
 | 
| 399 |     std::set_union(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::inserter(result, result.begin()));
 | 
| 400 |     return result;
 | 
| 401 | }
 | 
| 402 | 
 | 
| 403 | template <typename A>
 | 
| 404 | std::set<A> operator-(const std::set<A, std::less<A>>& lhs, const std::set<A, std::less<A>>& rhs) {
 | 
| 405 |     std::set<A> result;
 | 
| 406 |     std::set_difference(
 | 
| 407 |             lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::inserter(result, result.begin()));
 | 
| 408 |     return result;
 | 
| 409 | }
 | 
| 410 | 
 | 
| 411 | }  // namespace souffle
 |