//===--- iwyu_stl_util.h - STL-like utilities for include-what-you-use ----===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// Utilities that make it easier to work with STL.

#ifndef INCLUDE_WHAT_YOU_USE_IWYU_STL_UTIL_H_
#define INCLUDE_WHAT_YOU_USE_IWYU_STL_UTIL_H_

#include <algorithm>                    // for find
#include <map>                          // for map, multimap
#include <set>                          // for set
#include <vector>                       // for vector

namespace include_what_you_use {

using std::map;
using std::multimap;
using std::set;
using std::vector;

// Returns true if the associative container (e.g. set or map)
// contains the given key.
template <class AssociativeContainer>
bool ContainsKey(const AssociativeContainer& container,
                 const typename AssociativeContainer::key_type& key) {
  return container.find(key) != container.end();
}

// Returns true if the container contains the given value.
template <class Container>
bool ContainsValue(const Container& container,
                   const typename Container::value_type& value) {
  return (std::find(container.begin(), container.end(), value)
          != container.end());
}

// For maps, we also let you check if the key exists with the given value.
template <class Container, typename K, typename V>
bool ContainsKeyValue(const Container& container,
                      const K& key, const V& value) {
  for (typename Container::const_iterator it = container.lower_bound(key),
           end = container.upper_bound(key); it != end; ++it) {
    if (it->second == value)
      return true;
  }
  return false;
}

// Returns true if the associative container contains any key in the
// given set.
template <class AssociativeContainer>
bool ContainsAnyKey(
    const AssociativeContainer& container,
    const set<typename AssociativeContainer::key_type>& keys) {
  for (const auto& key : keys) {
    if (ContainsKey(container, key))
      return true;
  }
  return false;
}

// Returns a_map[key] if key is in a_map; otherwise returns default_value.
template <class Map>
const typename Map::mapped_type& GetOrDefault(
    const Map& a_map, const typename Map::key_type& key,
    const typename Map::mapped_type& default_value) {
  if (ContainsKey(a_map, key))
    return a_map.find(key)->second;
  return default_value;
}

// Returns a pointer to (*a_map)[key] if key is in *a_map; otherwise
// returns nullptr.
template <typename K, typename V>
const V* FindInMap(const map<K, V>* a_map, const K& key) {
  const typename map<K, V>::const_iterator it = a_map->find(key);
  return it == a_map->end() ? nullptr : &it->second;
}
template <typename K, typename V>
V* FindInMap(map<K, V>* a_map, const K& key) {
  const typename map<K, V>::iterator it = a_map->find(key);
  return it == a_map->end() ? nullptr : &it->second;
}

// Returns all values associated with the given key in the multimap.
template <typename K, typename V>
vector<V> FindInMultiMap(const multimap<K, V>& a_multimap, const K& key) {
  vector<V> retval;
  for (typename multimap<K, V>::const_iterator it = a_multimap.lower_bound(key),
           end = a_multimap.upper_bound(key); it != end; ++it) {
    retval.push_back(it->second);
  }
  return retval;
}

// Removes all elements in source from target.
template <class SourceContainer, class TargetContainer>
void RemoveAllFrom(const SourceContainer& source, TargetContainer* target) {
  for (typename SourceContainer::const_iterator it = source.begin();
       it != source.end(); ++it) {
    target->erase(*it);
  }
}

// Inserts all elements from source into target.
template <class SourceContainer, class TargetContainer>
void InsertAllInto(const SourceContainer& source, TargetContainer* target) {
  target->insert(source.begin(), source.end());
}

// Appends all elements from source to the end of target.  The target
// type must support inserting a range at the end, which probably
// means it's a vector.
template <class TargetContainer, class SourceContainer>
void Extend(TargetContainer* target, const SourceContainer& source) {
  target->insert(target->end(), source.begin(), source.end());
}

// Returns the union of the two given sets.
template <typename T>
set<T> Union(const set<T>& lhs, const set<T>& rhs) {
  set<T> retval(lhs);
  InsertAllInto(rhs, &retval);
  return retval;
}

// Returns a vector v with all duplicates removed, but order otherwise
// maintained.
template <typename T>
vector<T> GetUniqueEntries(const vector<T>& v) {
  set<T> seen;
  vector<T> retval;
  for (typename vector<T>::const_iterator it = v.begin(); it != v.end(); ++it) {
    if (!ContainsKey(seen, *it)) {
      retval.push_back(*it);
      seen.insert(*it);
    }
  }
  return retval;
}

}  // namespace include_what_you_use

#endif  // INCLUDE_WHAT_YOU_USE_IWYU_STL_UTIL_H_
