(function (root) {
  var exports = undefined,
      module = undefined,
      require = undefined;
  var define = undefined;
  var self = root,
      window = root,
      global = root,
      globalThis = root;
  (function () {
    /**
     * HashMap - HashMap Class for JavaScript
     * @author Ariel Flesler <aflesler@gmail.com>
     * @version 2.4.0
     * Homepage: https://github.com/flesler/hashmap
     */
    (function (factory) {
      if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
      } else if (typeof module === 'object') {
        // Node js environment
        var HashMap = module.exports = factory(); // Keep it backwards compatible

        HashMap.HashMap = HashMap;
      } else {
        // Browser globals (this is window)
        this.HashMap = factory();
      }
    })(function () {
      function HashMap(other) {
        this.clear();

        switch (arguments.length) {
          case 0:
            break;

          case 1:
            {
              if ('length' in other) {
                // Flatten 2D array to alternating key-value array
                multi(this, Array.prototype.concat.apply([], other));
              } else {
                // Assumed to be a HashMap instance
                this.copy(other);
              }

              break;
            }

          default:
            multi(this, arguments);
            break;
        }
      }

      var proto = HashMap.prototype = {
        constructor: HashMap,
        get: function (key) {
          var data = this._data[this.hash(key)];

          return data && data[1];
        },
        set: function (key, value) {
          // Store original key as well (for iteration)
          var hash = this.hash(key);

          if (!(hash in this._data)) {
            this.size++;
          }

          this._data[hash] = [key, value];
        },
        multi: function () {
          multi(this, arguments);
        },
        copy: function (other) {
          for (var hash in other._data) {
            if (!(hash in this._data)) {
              this.size++;
            }

            this._data[hash] = other._data[hash];
          }
        },
        has: function (key) {
          return this.hash(key) in this._data;
        },
        search: function (value) {
          for (var key in this._data) {
            if (this._data[key][1] === value) {
              return this._data[key][0];
            }
          }

          return null;
        },
        delete: function (key) {
          var hash = this.hash(key);

          if (hash in this._data) {
            this.size--;
            delete this._data[hash];
          }
        },
        type: function (key) {
          var str = Object.prototype.toString.call(key);
          var type = str.slice(8, -1).toLowerCase(); // Some browsers yield DOMWindow or Window for null and undefined, works fine on Node

          if (!key && (type === 'domwindow' || type === 'window')) {
            return key + '';
          }

          return type;
        },
        keys: function () {
          var keys = [];
          this.forEach(function (_, key) {
            keys.push(key);
          });
          return keys;
        },
        values: function () {
          var values = [];
          this.forEach(function (value) {
            values.push(value);
          });
          return values;
        },
        entries: function () {
          var entries = [];
          this.forEach(function (value, key) {
            entries.push([key, value]);
          });
          return entries;
        },
        // TODO: This is deprecated and will be deleted in a future version
        count: function () {
          return this.size;
        },
        clear: function () {
          // TODO: Would Object.create(null) make any difference
          this._data = {};
          this.size = 0;
        },
        clone: function () {
          return new HashMap(this);
        },
        hash: function (key) {
          switch (this.type(key)) {
            case 'undefined':
            case 'null':
            case 'boolean':
            case 'number':
            case 'regexp':
              return key + '';

            case 'date':
              return '♣' + key.getTime();

            case 'string':
              return '♠' + key;

            case 'array':
              var hashes = [];

              for (var i = 0; i < key.length; i++) {
                hashes[i] = this.hash(key[i]);
              }

              return '♥' + hashes.join('⁞');

            default:
              // TODO: Don't use expandos when Object.defineProperty is not available?
              if (!key.hasOwnProperty('_hmuid_')) {
                key._hmuid_ = ++HashMap.uid;
                hide(key, '_hmuid_');
              }

              return '♦' + key._hmuid_;
          }
        },
        forEach: function (func, ctx) {
          for (var key in this._data) {
            var data = this._data[key];
            func.call(ctx || this, data[1], data[0]);
          }
        }
      };
      HashMap.uid = 0; // Iterator protocol for ES6

      if (typeof Symbol !== 'undefined' && typeof Symbol.iterator !== 'undefined') {
        proto[Symbol.iterator] = function () {
          var entries = this.entries();
          var i = 0;
          return {
            next: function () {
              if (i === entries.length) {
                return {
                  done: true
                };
              }

              var currentEntry = entries[i++];
              return {
                value: {
                  key: currentEntry[0],
                  value: currentEntry[1]
                },
                done: false
              };
            }
          };
        };
      } //- Add chaining to all methods that don't return something


      ['set', 'multi', 'copy', 'delete', 'clear', 'forEach'].forEach(function (method) {
        var fn = proto[method];

        proto[method] = function () {
          fn.apply(this, arguments);
          return this;
        };
      }); //- Backwards compatibility
      // TODO: remove() is deprecated and will be deleted in a future version

      HashMap.prototype.remove = HashMap.prototype.delete; //- Utils

      function multi(map, args) {
        for (var i = 0; i < args.length; i += 2) {
          map.set(args[i], args[i + 1]);
        }
      }

      function hide(obj, prop) {
        // Make non iterable if supported
        if (Object.defineProperty) {
          Object.defineProperty(obj, prop, {
            enumerable: false
          });
        }
      }

      return HashMap;
    });
  }).call(root);
})( // The environment-specific global.
function () {
  if (typeof globalThis !== 'undefined') return globalThis;
  if (typeof self !== 'undefined') return self;
  if (typeof window !== 'undefined') return window;
  if (typeof global !== 'undefined') return global;
  if (typeof this !== 'undefined') return this;
  return {};
}.call(this));