// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Hash map implementation with open addressing and quadratic probing.
class HashMapImplementation<K extends Hashable, V> implements HashMap<K, V> {

  // The [keys_] list contains the keys inserted in the map.
  // The [keys_] list must be a raw list because it
  // will contain both elements of type K, and the [deletedKey_] of type
  // Object.
  // The alternative of declaring the [keys_] list as of type Object
  // does not work, because the HashSetIterator constructor would fail:
  //  HashSetIterator(HashSet<E> set_)
  //    : _nextValidIndex = -1,
  //      _entries = set_._backingMap._keys {
  //    _advance();
  //  }
  // With K being type int, for example, it would fail because
  // ObjectArray<Object> is not assignable to type List<int> of entries_.
  List _keys;

  // The values_ inserted in the map. For a filled entry index in this
  // list, there is always the corresponding key in the [keys_] list
  // at the same entry index.
  List<V> _values;

  // The load limit is the number of entries we allow until we double
  // the size of the lists.
  int _loadLimit;

  // The current number of entries in the map. Will never be greater
  // than [_loadLimit].
  int _numberOfEntries;

  // The current number of deleted entries in the map.
  int _numberOfDeleted;

  // The sentinel when a key is deleted from the map. We cannot use static
  // const here because we would need to allocate a "const Object()" which
  // would end up canonicalized and then we cannot distinguish the deleted
  // key from the canonicalized Object().
  static Object _deletedKey;

  // The initial capacity of a hash map.
  static final int _INITIAL_CAPACITY = 8;  // must be power of 2

  HashMapImplementation() {
    if (_deletedKey === null) {
      _deletedKey = new Object();
    }
    _numberOfEntries = 0;
    _numberOfDeleted = 0;
    _loadLimit = _computeLoadLimit(_INITIAL_CAPACITY);
    _keys = new List(_INITIAL_CAPACITY);
    _values = new List<V>(_INITIAL_CAPACITY);
  }

  // See bug 5257789.
  factory HashMapImplementation/* <K, V> */.from(Map/* <K, V> */other) {
    Map/* <K, V> */ result = new HashMapImplementation/* <K, V> */();
    other.forEach((/* K */ key, /* V */ value) { result[key] = value; });
    return result;
  }

  static int _computeLoadLimit(int capacity) {
    return (capacity * 3) ~/ 4;
  }

  static int _firstProbe(int hashCode, int length) {
    return hashCode & (length - 1);
  }

  static int _nextProbe(int currentProbe, int numberOfProbes, int length) {
    return (currentProbe + numberOfProbes) & (length - 1);
  }

  int _probeForAdding(K key) {
    int hash = _firstProbe(key.hashCode(), _keys.length);
    int numberOfProbes = 1;
    int initialHash = hash;
    // insertionIndex points to a slot where a key was deleted.
    int insertionIndex = -1;
    while (true) {
      Object existingKey = _keys[hash];
      if (existingKey === null) {
        // We are sure the key is not already in the set.
        // If the current slot is empty and we didn't find any
        // insertion slot before, return this slot.
        if (insertionIndex < 0) return hash;
        // If we did find an insertion slot before, return it.
        return insertionIndex;
      } else if (existingKey == key) {
        // The key is already in the map. Return its slot.
        return hash;
      } else if ((insertionIndex < 0) && (_deletedKey === existingKey)) {
        // The slot contains a deleted element. Because previous calls to this
        // method may not have had this slot deleted, we must continue iterate
        // to find if there is a slot with the given key.
        insertionIndex = hash;
      }

      // We did not find an insertion slot. Look at the next one.
      hash = _nextProbe(hash, numberOfProbes++, _keys.length);
      // _ensureCapacity has guaranteed the following cannot happen.
      // assert(hash != initialHash);
    }
  }

  int _probeForLookup(K key) {
    int hash = _firstProbe(key.hashCode(), _keys.length);
    int numberOfProbes = 1;
    int initialHash = hash;
    while (true) {
      Object existingKey = _keys[hash];
      // If the slot does not contain anything (in particular, it does not
      // contain a deleted key), we know the key is not in the map.
      if (existingKey === null) return -1;
      // The key is in the map, return its index.
      if (existingKey == key) return hash;
      // Go to the next probe.
      hash = _nextProbe(hash, numberOfProbes++, _keys.length);
      // _ensureCapacity has guaranteed the following cannot happen.
      // assert(hash != initialHash);
    }
  }

  void _ensureCapacity() {
    int newNumberOfEntries = _numberOfEntries + 1;
    // Test if adding an element will reach the load limit.
    if (newNumberOfEntries >= _loadLimit) {
      _grow(_keys.length * 2);
      return;
    }

    // Make sure that we don't have poor performance when a map
    // contains lots of deleted entries: we _grow if
    // there are more deleted entried than free entries.
    int capacity = _keys.length;
    int numberOfFreeOrDeleted = capacity - newNumberOfEntries;
    int numberOfFree = numberOfFreeOrDeleted - _numberOfDeleted;
    // assert(numberOfFree > 0);
    if (_numberOfDeleted > numberOfFree) {
      _grow(_keys.length);
    }
  }

  static bool _isPowerOfTwo(int x) {
    return ((x & (x - 1)) == 0);
  }

  void _grow(int newCapacity) {
    assert(_isPowerOfTwo(newCapacity));
    int capacity = _keys.length;
    _loadLimit = _computeLoadLimit(newCapacity);
    List oldKeys = _keys;
    List<V> oldValues = _values;
    _keys = new List(newCapacity);
    _values = new List<V>(newCapacity);
    for (int i = 0; i < capacity; i++) {
      Object key = oldKeys[i];
      // If there is no key, we don't need to deal with the current slot.
      if (key === null || key === _deletedKey) {
        continue;
      }
      V value = oldValues[i];
      // Insert the {key, value} pair in their new slot.
      int newIndex = _probeForAdding(key);
      _keys[newIndex] = key;
      _values[newIndex] = value;
    }
    _numberOfDeleted = 0;
  }

  void clear() {
    _numberOfEntries = 0;
    _numberOfDeleted = 0;
    int length = _keys.length;
    for (int i = 0; i < length; i++) {
      _keys[i] = null;
      _values[i] = null;
    }
  }

  void operator []=(K key, V value) {
    _ensureCapacity();
    int index = _probeForAdding(key);
    if ((_keys[index] === null) || (_keys[index] === _deletedKey)) {
      _numberOfEntries++;
    }
    _keys[index] = key;
    _values[index] = value;
  }

  V operator [](K key) {
    int index = _probeForLookup(key);
    if (index < 0) return null;
    return _values[index];
  }

  V putIfAbsent(K key, V ifAbsent()) {
    int index = _probeForLookup(key);
    if (index >=0) return _values[index];

    V value = ifAbsent();
    this[key] = value;
    return value;
  }

  V remove(K key) {
    int index = _probeForLookup(key);
    if (index >= 0) {
      _numberOfEntries--;
      V value = _values[index];
      _values[index] = null;
      // Set the key to the sentinel to not break the probing chain.
      _keys[index] = _deletedKey;
      _numberOfDeleted++;
      return value;
    }
    return null;
  }

  bool isEmpty() {
    return _numberOfEntries == 0;
  }

  int get length() {
    return _numberOfEntries;
  }

  void forEach(void f(K key, V value)) {
    int length = _keys.length;
    for (int i = 0; i < length; i++) {
      if ((_keys[i] !== null) && (_keys[i] !== _deletedKey)) {
        f(_keys[i], _values[i]);
      }
    }
  }


  Collection<K> getKeys() {
    List<K> list = new List<K>(length);
    int i = 0;
    forEach(void _(K key, V value) {
      list[i++] = key;
    });
    return list;
  }

  Collection<V> getValues() {
    List<V> list = new List<V>(length);
    int i = 0;
    forEach(void _(K key, V value) {
      list[i++] = value;
    });
    return list;
  }

  bool containsKey(K key) {
    return (_probeForLookup(key) != -1);
  }

  bool containsValue(V value) {
    int length = _values.length;
    for (int i = 0; i < length; i++) {
      if ((_keys[i] !== null) && (_keys[i] !== _deletedKey)) {
        if (_values[i] == value) return true;
      }
    }
    return false;
  }
}

class HashSetImplementation<E extends Hashable> implements HashSet<E> {

  HashSetImplementation() {
    _backingMap = new HashMapImplementation<E, E>();
  }

  // See bug 5257789.
  factory HashSetImplementation/* <E> */.from(Iterable/* <E> */ other) {
    Set/* <E> */ set = new HashSetImplementation/* <E> */();
    for (final e in other) {
      set.add(e);
    }
    return set;
  }

  void clear() {
    _backingMap.clear();
  }

  void add(E value) {
    _backingMap[value] = value;
  }

  bool contains(E value) {
    return _backingMap.containsKey(value);
  }

  bool remove(E value) {
    if (!_backingMap.containsKey(value)) return false;
    _backingMap.remove(value);
    return true;
  }

  void addAll(Collection<E> collection) {
    collection.forEach(void _(E value) {
      add(value);
    });
  }

  Set<E> intersection(Collection<E> collection) {
    Set<E> result = new Set<E>();
    collection.forEach(void _(E value) {
      if (contains(value)) result.add(value);
    });
    return result;
  }

  bool isSubsetOf(Collection<E> other) {
    return new Set<E>.from(other).containsAll(this);
  }

  void removeAll(Collection<E> collection) {
    collection.forEach(void _(E value) {
      remove(value);
    });
  }

  bool containsAll(Collection<E> collection) {
    return collection.every(bool _(E value) {
      return contains(value);
    });
  }

  void forEach(void f(E element)) {
    _backingMap.forEach(void _(E key, E value) {
      f(key);
    });
  }

  Set<E> filter(bool f(E element)) {
    Set<E> result = new Set<E>();
    _backingMap.forEach(void _(E key, E value) {
      if (f(key)) result.add(key);
    });
    return result;
  }

  bool every(bool f(E element)) {
    Collection<E> keys = _backingMap.getKeys();
    return keys.every(f);
  }

  bool some(bool f(E element)) {
    Collection<E> keys = _backingMap.getKeys();
    return keys.some(f);
  }

  bool isEmpty() {
    return _backingMap.isEmpty();
  }

  int get length() {
    return _backingMap.length;
  }

  Iterator<E> iterator() {
    return new HashSetIterator<E>(this);
  }

  // The map backing this set. The associations in this map are all
  // of the form element -> element. If a value is not in the map,
  // then it is not in the set.
  HashMapImplementation<E, E> _backingMap;
}

class HashSetIterator<E> implements Iterator<E> {

  // TODO(4504458): Replace set_ with set.
  HashSetIterator(HashSetImplementation<E> set_)
    : _nextValidIndex = -1,
      _entries = set_._backingMap._keys {
    _advance();
  }

  bool hasNext() {
    if (_nextValidIndex >= _entries.length) return false;
    if (_entries[_nextValidIndex] === HashMapImplementation._deletedKey) {
      // This happens in case the set was modified in the meantime.
      // A modification on the set may make this iterator misbehave,
      // but we should never return the sentinel.
      _advance();
    }
    return _nextValidIndex < _entries.length;
  }

  E next() {
    if (!hasNext()) {
      throw const NoMoreElementsException();
    }
    E res = _entries[_nextValidIndex];
    _advance();
    return res;
  }

  void _advance() {
    int length = _entries.length;
    var entry;
    Object deletedKey = HashMapImplementation._deletedKey;
    do {
      if (++_nextValidIndex >= length) break;
      entry = _entries[_nextValidIndex];
    } while ((entry === null) || (entry === deletedKey));
  }

  // The entries in the set. May contain null or the sentinel value.
  List<E> _entries;

  // The next valid index in [_entries] or the length of [entries_].
  // If it is the length of [_entries], calling [hasNext] on the
  // iterator will return false.
  int _nextValidIndex;
}
