/*
 * Header which provides parallel STL implementations when available.
 */
#ifndef NETWORKIT_AUXILIARY_PARALLEL_HPP_
#define NETWORKIT_AUXILIARY_PARALLEL_HPP_

#include <atomic>
#include <functional>

#if ((defined(__GNUC__) || defined(__GNUG__)) \
     && !(defined(__clang__) || defined(__INTEL_COMPILER))) \
    && defined _OPENMP
#include <parallel/algorithm>
#else
#define NETWORKIT_NO_PARALLEL_STL
#include <algorithm>
#endif

namespace Aux {
namespace Parallel {
#ifdef NETWORKIT_NO_PARALLEL_STL
using std::max_element;
using std::sort;
#else
using __gnu_parallel::max_element;
using __gnu_parallel::sort;
#endif // NETWORKIT_NO_PARALLEL_STL

template <typename ValueType, typename Comp>
void atomic_set(std::atomic<ValueType> &target, const ValueType &input, Comp shallSet) {
    // load current value
    auto curValue = target.load(std::memory_order_relaxed);

    do {
        if (!shallSet(input, curValue))
            break; // skip if shall not be set (anymore)
        // set new value unless current value has been changed in the meantime, if current value has
        // changed load it (so we can compare again)
    } while (!target.compare_exchange_weak(curValue, input, std::memory_order_release,
                                           std::memory_order_relaxed));
}

template <typename ValueType>
void atomic_max(std::atomic<ValueType> &target, const ValueType &input) {
    atomic_set(target, input, std::greater<ValueType>());
}

template <typename ValueType>
void atomic_min(std::atomic<ValueType> &target, const ValueType &input) {
    atomic_set(target, input, std::less<ValueType>());
}

} // namespace Parallel
} // namespace Aux

#endif // NETWORKIT_AUXILIARY_PARALLEL_HPP_
