text: var global$1 = (typeof global !== "undefined" ? global :
    typeof self !== "undefined" ? self :
    typeof window !== "undefined" ? window : {});
  
  var lookup = [];
  var revLookup = [];
  var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
  var inited = false;
  function init () {
    inited = true;
    var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    for (var i = 0, len = code.length; i < len; ++i) {
      lookup[i] = code[i];
      revLookup[code.charCodeAt(i)] = i;
    }
  
    revLookup['-'.charCodeAt(0)] = 62;
    revLookup['_'.charCodeAt(0)] = 63;
  }
  
  function toByteArray (b64) {
    if (!inited) {
      init();
    }
    var i, j, l, tmp, placeHolders, arr;
    var len = b64.length;
  
    if (len % 4 > 0) {
      throw new Error('Invalid string. Length must be a multiple of 4')
    }
  
    // the number of equal signs (place holders)
    // if there are two placeholders, than the two characters before it
    // represent one byte
    // if there is only one, then the three characters before it represent 2 bytes
    // this is just a cheap hack to not do indexOf twice
    placeHolders = b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0;
  
    // base64 is 4/3 + up to two characters of the original data
    arr = new Arr(len * 3 / 4 - placeHolders);
  
    // if there are placeholders, only get up to the last complete 4 chars
    l = placeHolders > 0 ? len - 4 : len;
  
    var L = 0;
  
    for (i = 0, j = 0; i < l; i += 4, j += 3) {
      tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)];
      arr[L++] = (tmp >> 16) & 0xFF;
      arr[L++] = (tmp >> 8) & 0xFF;
      arr[L++] = tmp & 0xFF;
    }
  
    if (placeHolders === 2) {
      tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4);
      arr[L++] = tmp & 0xFF;
    } else if (placeHolders === 1) {
      tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2);
      arr[L++] = (tmp >> 8) & 0xFF;
      arr[L++] = tmp & 0xFF;
    }
  
    return arr
  }
  
  function tripletToBase64 (num) {
    return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
  }
  
  function encodeChunk (uint8, start, end) {
    var tmp;
    var output = [];
    for (var i = start; i < end; i += 3) {
      tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]);
      output.push(tripletToBase64(tmp));
    }
    return output.join('')
  }
  
  function fromByteArray (uint8) {
    if (!inited) {
      init();
    }
    var tmp;
    var len = uint8.length;
    var extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
    var output = '';
    var parts = [];
    var maxChunkLength = 16383; // must be multiple of 3
  
    // go through the array every three bytes, we'll deal with trailing stuff later
    for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
      parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)));
    }
  
    // pad the end with zeros, but make sure to not forget the extra bytes
    if (extraBytes === 1) {
      tmp = uint8[len - 1];
      output += lookup[tmp >> 2];
      output += lookup[(tmp << 4) & 0x3F];
      output += '==';
    } else if (extraBytes === 2) {
      tmp = (uint8[len - 2] << 8) + (uint8[len - 1]);
      output += lookup[tmp >> 10];
      output += lookup[(tmp >> 4) & 0x3F];
      output += lookup[(tmp << 2) & 0x3F];
      output += '=';
    }
  
    parts.push(output);
  
    return parts.join('')
  }
  
  function read (buffer, offset, isLE, mLen, nBytes) {
    var e, m;
    var eLen = nBytes * 8 - mLen - 1;
    var eMax = (1 << eLen) - 1;
    var eBias = eMax >> 1;
    var nBits = -7;
    var i = isLE ? (nBytes - 1) : 0;
    var d = isLE ? -1 : 1;
    var s = buffer[offset + i];
  
    i += d;
  
    e = s & ((1 << (-nBits)) - 1);
    s >>= (-nBits);
    nBits += eLen;
    for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
  
    m = e & ((1 << (-nBits)) - 1);
    e >>= (-nBits);
    nBits += mLen;
    for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
  
    if (e === 0) {
      e = 1 - eBias;
    } else if (e === eMax) {
      return m ? NaN : ((s ? -1 : 1) * Infinity)
    } else {
      m = m + Math.pow(2, mLen);
      e = e - eBias;
    }
    return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
  }
  
  function write (buffer, value, offset, isLE, mLen, nBytes) {
    var e, m, c;
    var eLen = nBytes * 8 - mLen - 1;
    var eMax = (1 << eLen) - 1;
    var eBias = eMax >> 1;
    var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0);
    var i = isLE ? 0 : (nBytes - 1);
    var d = isLE ? 1 : -1;
    var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
  
    value = Math.abs(value);
  
    if (isNaN(value) || value === Infinity) {
      m = isNaN(value) ? 1 : 0;
      e = eMax;
    } else {
      e = Math.floor(Math.log(value) / Math.LN2);
      if (value * (c = Math.pow(2, -e)) < 1) {
        e--;
        c *= 2;
      }
      if (e + eBias >= 1) {
        value += rt / c;
      } else {
        value += rt * Math.pow(2, 1 - eBias);
      }
      if (value * c >= 2) {
        e++;
        c /= 2;
      }
  
      if (e + eBias >= eMax) {
        m = 0;
        e = eMax;
      } else if (e + eBias >= 1) {
        m = (value * c - 1) * Math.pow(2, mLen);
        e = e + eBias;
      } else {
        m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
        e = 0;
      }
    }
  
    for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
  
    e = (e << mLen) | m;
    eLen += mLen;
    for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
  
    buffer[offset + i - d] |= s * 128;
  }
  
  var toString = {}.toString;
  
  var isArray = Array.isArray || function (arr) {
    return toString.call(arr) == '[object Array]';
  };
  
  /*!
   * The buffer module from node.js, for the browser.
   *
   * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
   * @license  MIT
   */
  
  var INSPECT_MAX_BYTES = 50;
  
  /**
   * If `Buffer.TYPED_ARRAY_SUPPORT`:
   *   === true    Use Uint8Array implementation (fastest)
   *   === false   Use Object implementation (most compatible, even IE6)
   *
   * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
   * Opera 11.6+, iOS 4.2+.
   *
   * Due to various browser bugs, sometimes the Object implementation will be used even
   * when the browser supports typed arrays.
   *
   * Note:
   *
   *   - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
   *     See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
   *
   *   - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
   *
   *   - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
   *     incorrect length in some situations.
  
   * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
   * get the Object implementation, which is slower but behaves correctly.
   */
  Buffer.TYPED_ARRAY_SUPPORT = global$1.TYPED_ARRAY_SUPPORT !== undefined
    ? global$1.TYPED_ARRAY_SUPPORT
    : true;
  
  function kMaxLength () {
    return Buffer.TYPED_ARRAY_SUPPORT
      ? 0x7fffffff
      : 0x3fffffff
  }
  
  function createBuffer (that, length) {
    if (kMaxLength() < length) {
      throw new RangeError('Invalid typed array length')
    }
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      // Return an augmented `Uint8Array` instance, for best performance
      that = new Uint8Array(length);
      that.__proto__ = Buffer.prototype;
    } else {
      // Fallback: Return an object instance of the Buffer class
      if (that === null) {
        that = new Buffer(length);
      }
      that.length = length;
    }
  
    return that
  }
  
  /**
   * The Buffer constructor returns instances of `Uint8Array` that have their
   * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
   * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
   * and the `Uint8Array` methods. Square bracket notation works as expected -- it
   * returns a single octet.
   *
   * The `Uint8Array` prototype remains unmodified.
   */
  
  function Buffer (arg, encodingOrOffset, length) {
    if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
      return new Buffer(arg, encodingOrOffset, length)
    }
  
    // Common case.
    if (typeof arg === 'number') {
      if (typeof encodingOrOffset === 'string') {
        throw new Error(
          'If encoding is specified then the first argument must be a string'
        )
      }
      return allocUnsafe(this, arg)
    }
    return from(this, arg, encodingOrOffset, length)
  }
  
  Buffer.poolSize = 8192; // not used by this implementation
  
  // TODO: Legacy, not needed anymore. Remove in next major version.
  Buffer._augment = function (arr) {
    arr.__proto__ = Buffer.prototype;
    return arr
  };
  
  function from (that, value, encodingOrOffset, length) {
    if (typeof value === 'number') {
      throw new TypeError('"value" argument must not be a number')
    }
  
    if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
      return fromArrayBuffer(that, value, encodingOrOffset, length)
    }
  
    if (typeof value === 'string') {
      return fromString(that, value, encodingOrOffset)
    }
  
    return fromObject(that, value)
  }
  
  /**
   * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
   * if value is a number.
   * Buffer.from(str[, encoding])
   * Buffer.from(array)
   * Buffer.from(buffer)
   * Buffer.from(arrayBuffer[, byteOffset[, length]])
   **/
  Buffer.from = function (value, encodingOrOffset, length) {
    return from(null, value, encodingOrOffset, length)
  };
  
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    Buffer.prototype.__proto__ = Uint8Array.prototype;
    Buffer.__proto__ = Uint8Array;
  }
  
  function assertSize (size) {
    if (typeof size !== 'number') {
      throw new TypeError('"size" argument must be a number')
    } else if (size < 0) {
      throw new RangeError('"size" argument must not be negative')
    }
  }
  
  function alloc (that, size, fill, encoding) {
    assertSize(size);
    if (size <= 0) {
      return createBuffer(that, size)
    }
    if (fill !== undefined) {
      // Only pay attention to encoding if it's a string. This
      // prevents accidentally sending in a number that would
      // be interpretted as a start offset.
      return typeof encoding === 'string'
        ? createBuffer(that, size).fill(fill, encoding)
        : createBuffer(that, size).fill(fill)
    }
    return createBuffer(that, size)
  }
  
  /**
   * Creates a new filled Buffer instance.
   * alloc(size[, fill[, encoding]])
   **/
  Buffer.alloc = function (size, fill, encoding) {
    return alloc(null, size, fill, encoding)
  };
  
  function allocUnsafe (that, size) {
    assertSize(size);
    that = createBuffer(that, size < 0 ? 0 : checked(size) | 0);
    if (!Buffer.TYPED_ARRAY_SUPPORT) {
      for (var i = 0; i < size; ++i) {
        that[i] = 0;
      }
    }
    return that
  }
  
  /**
   * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
   * */
  Buffer.allocUnsafe = function (size) {
    return allocUnsafe(null, size)
  };
  /**
   * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
   */
  Buffer.allocUnsafeSlow = function (size) {
    return allocUnsafe(null, size)
  };
  
  function fromString (that, string, encoding) {
    if (typeof encoding !== 'string' || encoding === '') {
      encoding = 'utf8';
    }
  
    if (!Buffer.isEncoding(encoding)) {
      throw new TypeError('"encoding" must be a valid string encoding')
    }
  
    var length = byteLength(string, encoding) | 0;
    that = createBuffer(that, length);
  
    var actual = that.write(string, encoding);
  
    if (actual !== length) {
      // Writing a hex string, for example, that contains invalid characters will
      // cause everything after the first invalid character to be ignored. (e.g.
      // 'abxxcd' will be treated as 'ab')
      that = that.slice(0, actual);
    }
  
    return that
  }
  
  function fromArrayLike (that, array) {
    var length = array.length < 0 ? 0 : checked(array.length) | 0;
    that = createBuffer(that, length);
    for (var i = 0; i < length; i += 1) {
      that[i] = array[i] & 255;
    }
    return that
  }
  
  function fromArrayBuffer (that, array, byteOffset, length) {
    array.byteLength; // this throws if `array` is not a valid ArrayBuffer
  
    if (byteOffset < 0 || array.byteLength < byteOffset) {
      throw new RangeError('\'offset\' is out of bounds')
    }
  
    if (array.byteLength < byteOffset + (length || 0)) {
      throw new RangeError('\'length\' is out of bounds')
    }
  
    if (byteOffset === undefined && length === undefined) {
      array = new Uint8Array(array);
    } else if (length === undefined) {
      array = new Uint8Array(array, byteOffset);
    } else {
      array = new Uint8Array(array, byteOffset, length);
    }
  
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      // Return an augmented `Uint8Array` instance, for best performance
      that = array;
      that.__proto__ = Buffer.prototype;
    } else {
      // Fallback: Return an object instance of the Buffer class
      that = fromArrayLike(that, array);
    }
    return that
  }
  
  function fromObject (that, obj) {
    if (internalIsBuffer(obj)) {
      var len = checked(obj.length) | 0;
      that = createBuffer(that, len);
  
      if (that.length === 0) {
        return that
      }
  
      obj.copy(that, 0, 0, len);
      return that
    }
  
    if (obj) {
      if ((typeof ArrayBuffer !== 'undefined' &&
          obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
        if (typeof obj.length !== 'number' || isnan(obj.length)) {
          return createBuffer(that, 0)
        }
        return fromArrayLike(that, obj)
      }
  
      if (obj.type === 'Buffer' && isArray(obj.data)) {
        return fromArrayLike(that, obj.data)
      }
    }
  
    throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
  }
  
  function checked (length) {
    // Note: cannot use `length < kMaxLength()` here because that fails when
    // length is NaN (which is otherwise coerced to zero.)
    if (length >= kMaxLength()) {
      throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                           'size: 0x' + kMaxLength().toString(16) + ' bytes')
    }
    return length | 0
  }
  Buffer.isBuffer = isBuffer;
  function internalIsBuffer (b) {
    return !!(b != null && b._isBuffer)
  }
  
  Buffer.compare = function compare (a, b) {
    if (!internalIsBuffer(a) || !internalIsBuffer(b)) {
      throw new TypeError('Arguments must be Buffers')
    }
  
    if (a === b) return 0
  
    var x = a.length;
    var y = b.length;
  
    for (var i = 0, len = Math.min(x, y); i < len; ++i) {
      if (a[i] !== b[i]) {
        x = a[i];
        y = b[i];
        break
      }
    }
  
    if (x < y) return -1
    if (y < x) return 1
    return 0
  };
  
  Buffer.isEncoding = function isEncoding (encoding) {
    switch (String(encoding).toLowerCase()) {
      case 'hex':
      case 'utf8':
      case 'utf-8':
      case 'ascii':
      case 'latin1':
      case 'binary':
      case 'base64':
      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return true
      default:
        return false
    }
  };
  
  Buffer.concat = function concat (list, length) {
    if (!isArray(list)) {
      throw new TypeError('"list" argument must be an Array of Buffers')
    }
  
    if (list.length === 0) {
      return Buffer.alloc(0)
    }
  
    var i;
    if (length === undefined) {
      length = 0;
      for (i = 0; i < list.length; ++i) {
        length += list[i].length;
      }
    }
  
    var buffer = Buffer.allocUnsafe(length);
    var pos = 0;
    for (i = 0; i < list.length; ++i) {
      var buf = list[i];
      if (!internalIsBuffer(buf)) {
        throw new TypeError('"list" argument must be an Array of Buffers')
      }
      buf.copy(buffer, pos);
      pos += buf.length;
    }
    return buffer
  };
  
  function byteLength (string, encoding) {
    if (internalIsBuffer(string)) {
      return string.length
    }
    if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
        (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
      return string.byteLength
    }
    if (typeof string !== 'string') {
      string = '' + string;
    }
  
    var len = string.length;
    if (len === 0) return 0
  
    // Use a for loop to avoid recursion
    var loweredCase = false;
    for (;;) {
      switch (encoding) {
        case 'ascii':
        case 'latin1':
        case 'binary':
          return len
        case 'utf8':
        case 'utf-8':
        case undefined:
          return utf8ToBytes(string).length
        case 'ucs2':
        case 'ucs-2':
        case 'utf16le':
        case 'utf-16le':
          return len * 2
        case 'hex':
          return len >>> 1
        case 'base64':
          return base64ToBytes(string).length
        default:
          if (loweredCase) return utf8ToBytes(string).length // assume utf8
          encoding = ('' + encoding).toLowerCase();
          loweredCase = true;
      }
    }
  }
  Buffer.byteLength = byteLength;
  
  function slowToString (encoding, start, end) {
    var loweredCase = false;
  
    // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
    // property of a typed array.
  
    // This behaves neither like String nor Uint8Array in that we set start/end
    // to their upper/lower bounds if the value passed is out of range.
    // undefined is handled specially as per ECMA-262 6th Edition,
    // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
    if (start === undefined || start < 0) {
      start = 0;
    }
    // Return early if start > this.length. Done here to prevent potential uint32
    // coercion fail below.
    if (start > this.length) {
      return ''
    }
  
    if (end === undefined || end > this.length) {
      end = this.length;
    }
  
    if (end <= 0) {
      return ''
    }
  
    // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
    end >>>= 0;
    start >>>= 0;
  
    if (end <= start) {
      return ''
    }
  
    if (!encoding) encoding = 'utf8';
  
    while (true) {
      switch (encoding) {
        case 'hex':
          return hexSlice(this, start, end)
  
        case 'utf8':
        case 'utf-8':
          return utf8Slice(this, start, end)
  
        case 'ascii':
          return asciiSlice(this, start, end)
  
        case 'latin1':
        case 'binary':
          return latin1Slice(this, start, end)
  
        case 'base64':
          return base64Slice(this, start, end)
  
        case 'ucs2':
        case 'ucs-2':
        case 'utf16le':
        case 'utf-16le':
          return utf16leSlice(this, start, end)
  
        default:
          if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
          encoding = (encoding + '').toLowerCase();
          loweredCase = true;
      }
    }
  }
  
  // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
  // Buffer instances.
  Buffer.prototype._isBuffer = true;
  
  function swap (b, n, m) {
    var i = b[n];
    b[n] = b[m];
    b[m] = i;
  }
  
  Buffer.prototype.swap16 = function swap16 () {
    var len = this.length;
    if (len % 2 !== 0) {
      throw new RangeError('Buffer size must be a multiple of 16-bits')
    }
    for (var i = 0; i < len; i += 2) {
      swap(this, i, i + 1);
    }
    return this
  };
  
  Buffer.prototype.swap32 = function swap32 () {
    var len = this.length;
    if (len % 4 !== 0) {
      throw new RangeError('Buffer size must be a multiple of 32-bits')
    }
    for (var i = 0; i < len; i += 4) {
      swap(this, i, i + 3);
      swap(this, i + 1, i + 2);
    }
    return this
  };
  
  Buffer.prototype.swap64 = function swap64 () {
    var len = this.length;
    if (len % 8 !== 0) {
      throw new RangeError('Buffer size must be a multiple of 64-bits')
    }
    for (var i = 0; i < len; i += 8) {
      swap(this, i, i + 7);
      swap(this, i + 1, i + 6);
      swap(this, i + 2, i + 5);
      swap(this, i + 3, i + 4);
    }
    return this
  };
  
  Buffer.prototype.toString = function toString () {
    var length = this.length | 0;
    if (length === 0) return ''
    if (arguments.length === 0) return utf8Slice(this, 0, length)
    return slowToString.apply(this, arguments)
  };
  
  Buffer.prototype.equals = function equals (b) {
    if (!internalIsBuffer(b)) throw new TypeError('Argument must be a Buffer')
    if (this === b) return true
    return Buffer.compare(this, b) === 0
  };
  
  Buffer.prototype.inspect = function inspect () {
    var str = '';
    var max = INSPECT_MAX_BYTES;
    if (this.length > 0) {
      str = this.toString('hex', 0, max).match(/.{2}/g).join(' ');
      if (this.length > max) str += ' ... ';
    }
    return '<Buffer ' + str + '>'
  };
  
  Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
    if (!internalIsBuffer(target)) {
      throw new TypeError('Argument must be a Buffer')
    }
  
    if (start === undefined) {
      start = 0;
    }
    if (end === undefined) {
      end = target ? target.length : 0;
    }
    if (thisStart === undefined) {
      thisStart = 0;
    }
    if (thisEnd === undefined) {
      thisEnd = this.length;
    }
  
    if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
      throw new RangeError('out of range index')
    }
  
    if (thisStart >= thisEnd && start >= end) {
      return 0
    }
    if (thisStart >= thisEnd) {
      return -1
    }
    if (start >= end) {
      return 1
    }
  
    start >>>= 0;
    end >>>= 0;
    thisStart >>>= 0;
    thisEnd >>>= 0;
  
    if (this === target) return 0
  
    var x = thisEnd - thisStart;
    var y = end - start;
    var len = Math.min(x, y);
  
    var thisCopy = this.slice(thisStart, thisEnd);
    var targetCopy = target.slice(start, end);
  
    for (var i = 0; i < len; ++i) {
      if (thisCopy[i] !== targetCopy[i]) {
        x = thisCopy[i];
        y = targetCopy[i];
        break
      }
    }
  
    if (x < y) return -1
    if (y < x) return 1
    return 0
  };
  
  // Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
  // OR the last index of `val` in `buffer` at offset <= `byteOffset`.
  //
  // Arguments:
  // - buffer - a Buffer to search
  // - val - a string, Buffer, or number
  // - byteOffset - an index into `buffer`; will be clamped to an int32
  // - encoding - an optional encoding, relevant is val is a string
  // - dir - true for indexOf, false for lastIndexOf
  function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
    // Empty buffer means no match
    if (buffer.length === 0) return -1
  
    // Normalize byteOffset
    if (typeof byteOffset === 'string') {
      encoding = byteOffset;
      byteOffset = 0;
    } else if (byteOffset > 0x7fffffff) {
      byteOffset = 0x7fffffff;
    } else if (byteOffset < -0x80000000) {
      byteOffset = -0x80000000;
    }
    byteOffset = +byteOffset;  // Coerce to Number.
    if (isNaN(byteOffset)) {
      // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
      byteOffset = dir ? 0 : (buffer.length - 1);
    }
  
    // Normalize byteOffset: negative offsets start from the end of the buffer
    if (byteOffset < 0) byteOffset = buffer.length + byteOffset;
    if (byteOffset >= buffer.length) {
      if (dir) return -1
      else byteOffset = buffer.length - 1;
    } else if (byteOffset < 0) {
      if (dir) byteOffset = 0;
      else return -1
    }
  
    // Normalize val
    if (typeof val === 'string') {
      val = Buffer.from(val, encoding);
    }
  
    // Finally, search either indexOf (if dir is true) or lastIndexOf
    if (internalIsBuffer(val)) {
      // Special case: looking for empty string/buffer always fails
      if (val.length === 0) {
        return -1
      }
      return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
    } else if (typeof val === 'number') {
      val = val & 0xFF; // Search for a byte value [0-255]
      if (Buffer.TYPED_ARRAY_SUPPORT &&
          typeof Uint8Array.prototype.indexOf === 'function') {
        if (dir) {
          return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
        } else {
          return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
        }
      }
      return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
    }
  
    throw new TypeError('val must be string, number or Buffer')
  }
  
  function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
    var indexSize = 1;
    var arrLength = arr.length;
    var valLength = val.length;
  
    if (encoding !== undefined) {
      encoding = String(encoding).toLowerCase();
      if (encoding === 'ucs2' || encoding === 'ucs-2' ||
          encoding === 'utf16le' || encoding === 'utf-16le') {
        if (arr.length < 2 || val.length < 2) {
          return -1
        }
        indexSize = 2;
        arrLength /= 2;
        valLength /= 2;
        byteOffset /= 2;
      }
    }
  
    function read (buf, i) {
      if (indexSize === 1) {
        return buf[i]
      } else {
        return buf.readUInt16BE(i * indexSize)
      }
    }
  
    var i;
    if (dir) {
      var foundIndex = -1;
      for (i = byteOffset; i < arrLength; i++) {
        if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
          if (foundIndex === -1) foundIndex = i;
          if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
        } else {
          if (foundIndex !== -1) i -= i - foundIndex;
          foundIndex = -1;
        }
      }
    } else {
      if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength;
      for (i = byteOffset; i >= 0; i--) {
        var found = true;
        for (var j = 0; j < valLength; j++) {
          if (read(arr, i + j) !== read(val, j)) {
            found = false;
            break
          }
        }
        if (found) return i
      }
    }
  
    return -1
  }
  
  Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
    return this.indexOf(val, byteOffset, encoding) !== -1
  };
  
  Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
    return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
  };
  
  Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
    return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
  };
  
  function hexWrite (buf, string, offset, length) {
    offset = Number(offset) || 0;
    var remaining = buf.length - offset;
    if (!length) {
      length = remaining;
    } else {
      length = Number(length);
      if (length > remaining) {
        length = remaining;
      }
    }
  
    // must be an even number of digits
    var strLen = string.length;
    if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')
  
    if (length > strLen / 2) {
      length = strLen / 2;
    }
    for (var i = 0; i < length; ++i) {
      var parsed = parseInt(string.substr(i * 2, 2), 16);
      if (isNaN(parsed)) return i
      buf[offset + i] = parsed;
    }
    return i
  }
  
  function utf8Write (buf, string, offset, length) {
    return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
  }
  
  function asciiWrite (buf, string, offset, length) {
    return blitBuffer(asciiToBytes(string), buf, offset, length)
  }
  
  function latin1Write (buf, string, offset, length) {
    return asciiWrite(buf, string, offset, length)
  }
  
  function base64Write (buf, string, offset, length) {
    return blitBuffer(base64ToBytes(string), buf, offset, length)
  }
  
  function ucs2Write (buf, string, offset, length) {
    return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
  }
  
  Buffer.prototype.write = function write (string, offset, length, encoding) {
    // Buffer#write(string)
    if (offset === undefined) {
      encoding = 'utf8';
      length = this.length;
      offset = 0;
    // Buffer#write(string, encoding)
    } else if (length === undefined && typeof offset === 'string') {
      encoding = offset;
      length = this.length;
      offset = 0;
    // Buffer#write(string, offset[, length][, encoding])
    } else if (isFinite(offset)) {
      offset = offset | 0;
      if (isFinite(length)) {
        length = length | 0;
        if (encoding === undefined) encoding = 'utf8';
      } else {
        encoding = length;
        length = undefined;
      }
    // legacy write(string, encoding, offset, length) - remove in v0.13
    } else {
      throw new Error(
        'Buffer.write(string, encoding, offset[, length]) is no longer supported'
      )
    }
  
    var remaining = this.length - offset;
    if (length === undefined || length > remaining) length = remaining;
  
    if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
      throw new RangeError('Attempt to write outside buffer bounds')
    }
  
    if (!encoding) encoding = 'utf8';
  
    var loweredCase = false;
    for (;;) {
      switch (encoding) {
        case 'hex':
          return hexWrite(this, string, offset, length)
  
        case 'utf8':
        case 'utf-8':
          return utf8Write(this, string, offset, length)
  
        case 'ascii':
          return asciiWrite(this, string, offset, length)
  
        case 'latin1':
        case 'binary':
          return latin1Write(this, string, offset, length)
  
        case 'base64':
          // Warning: maxLength not taken into account in base64Write
          return base64Write(this, string, offset, length)
  
        case 'ucs2':
        case 'ucs-2':
        case 'utf16le':
        case 'utf-16le':
          return ucs2Write(this, string, offset, length)
  
        default:
          if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
          encoding = ('' + encoding).toLowerCase();
          loweredCase = true;
      }
    }
  };
  
  Buffer.prototype.toJSON = function toJSON () {
    return {
      type: 'Buffer',
      data: Array.prototype.slice.call(this._arr || this, 0)
    }
  };
  
  function base64Slice (buf, start, end) {
    if (start === 0 && end === buf.length) {
      return fromByteArray(buf)
    } else {
      return fromByteArray(buf.slice(start, end))
    }
  }
  
  function utf8Slice (buf, start, end) {
    end = Math.min(buf.length, end);
    var res = [];
  
    var i = start;
    while (i < end) {
      var firstByte = buf[i];
      var codePoint = null;
      var bytesPerSequence = (firstByte > 0xEF) ? 4
        : (firstByte > 0xDF) ? 3
        : (firstByte > 0xBF) ? 2
        : 1;
  
      if (i + bytesPerSequence <= end) {
        var secondByte, thirdByte, fourthByte, tempCodePoint;
  
        switch (bytesPerSequence) {
          case 1:
            if (firstByte < 0x80) {
              codePoint = firstByte;
            }
            break
          case 2:
            secondByte = buf[i + 1];
            if ((secondByte & 0xC0) === 0x80) {
              tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F);
              if (tempCodePoint > 0x7F) {
                codePoint = tempCodePoint;
              }
            }
            break
          case 3:
            secondByte = buf[i + 1];
            thirdByte = buf[i + 2];
            if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
              tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F);
              if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
                codePoint = tempCodePoint;
              }
            }
            break
          case 4:
            secondByte = buf[i + 1];
            thirdByte = buf[i + 2];
            fourthByte = buf[i + 3];
            if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
              tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F);
              if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
                codePoint = tempCodePoint;
              }
            }
        }
      }
  
      if (codePoint === null) {
        // we did not generate a valid codePoint so insert a
        // replacement char (U+FFFD) and advance only 1 byte
        codePoint = 0xFFFD;
        bytesPerSequence = 1;
      } else if (codePoint > 0xFFFF) {
        // encode to utf16 (surrogate pair dance)
        codePoint -= 0x10000;
        res.push(codePoint >>> 10 & 0x3FF | 0xD800);
        codePoint = 0xDC00 | codePoint & 0x3FF;
      }
  
      res.push(codePoint);
      i += bytesPerSequence;
    }
  
    return decodeCodePointsArray(res)
  }
  
  // Based on http://stackoverflow.com/a/22747272/680742, the browser with
  // the lowest limit is Chrome, with 0x10000 args.
  // We go 1 magnitude less, for safety
  var MAX_ARGUMENTS_LENGTH = 0x1000;
  
  function decodeCodePointsArray (codePoints) {
    var len = codePoints.length;
    if (len <= MAX_ARGUMENTS_LENGTH) {
      return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
    }
  
    // Decode in chunks to avoid "call stack size exceeded".
    var res = '';
    var i = 0;
    while (i < len) {
      res += String.fromCharCode.apply(
        String,
        codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
      );
    }
    return res
  }
  
  function asciiSlice (buf, start, end) {
    var ret = '';
    end = Math.min(buf.length, end);
  
    for (var i = start; i < end; ++i) {
      ret += String.fromCharCode(buf[i] & 0x7F);
    }
    return ret
  }
  
  function latin1Slice (buf, start, end) {
    var ret = '';
    end = Math.min(buf.length, end);
  
    for (var i = start; i < end; ++i) {
      ret += String.fromCharCode(buf[i]);
    }
    return ret
  }
  
  function hexSlice (buf, start, end) {
    var len = buf.length;
  
    if (!start || start < 0) start = 0;
    if (!end || end < 0 || end > len) end = len;
  
    var out = '';
    for (var i = start; i < end; ++i) {
      out += toHex(buf[i]);
    }
    return out
  }
  
  function utf16leSlice (buf, start, end) {
    var bytes = buf.slice(start, end);
    var res = '';
    for (var i = 0; i < bytes.length; i += 2) {
      res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256);
    }
    return res
  }
  
  Buffer.prototype.slice = function slice (start, end) {
    var len = this.length;
    start = ~~start;
    end = end === undefined ? len : ~~end;
  
    if (start < 0) {
      start += len;
      if (start < 0) start = 0;
    } else if (start > len) {
      start = len;
    }
  
    if (end < 0) {
      end += len;
      if (end < 0) end = 0;
    } else if (end > len) {
      end = len;
    }
  
    if (end < start) end = start;
  
    var newBuf;
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      newBuf = this.subarray(start, end);
      newBuf.__proto__ = Buffer.prototype;
    } else {
      var sliceLen = end - start;
      newBuf = new Buffer(sliceLen, undefined);
      for (var i = 0; i < sliceLen; ++i) {
        newBuf[i] = this[i + start];
      }
    }
  
    return newBuf
  };
  
  /*
   * Need to make sure that buffer isn't trying to write out of bounds.
   */
  function checkOffset (offset, ext, length) {
    if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
    if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
  }
  
  Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
    offset = offset | 0;
    byteLength = byteLength | 0;
    if (!noAssert) checkOffset(offset, byteLength, this.length);
  
    var val = this[offset];
    var mul = 1;
    var i = 0;
    while (++i < byteLength && (mul *= 0x100)) {
      val += this[offset + i] * mul;
    }
  
    return val
  };
  
  Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
    offset = offset | 0;
    byteLength = byteLength | 0;
    if (!noAssert) {
      checkOffset(offset, byteLength, this.length);
    }
  
    var val = this[offset + --byteLength];
    var mul = 1;
    while (byteLength > 0 && (mul *= 0x100)) {
      val += this[offset + --byteLength] * mul;
    }
  
    return val
  };
  
  Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 1, this.length);
    return this[offset]
  };
  
  Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 2, this.length);
    return this[offset] | (this[offset + 1] << 8)
  };
  
  Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 2, this.length);
    return (this[offset] << 8) | this[offset + 1]
  };
  
  Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length);
  
    return ((this[offset]) |
        (this[offset + 1] << 8) |
        (this[offset + 2] << 16)) +
        (this[offset + 3] * 0x1000000)
  };
  
  Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length);
  
    return (this[offset] * 0x1000000) +
      ((this[offset + 1] << 16) |
      (this[offset + 2] << 8) |
      this[offset + 3])
  };
  
  Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
    offset = offset | 0;
    byteLength = byteLength | 0;
    if (!noAssert) checkOffset(offset, byteLength, this.length);
  
    var val = this[offset];
    var mul = 1;
    var i = 0;
    while (++i < byteLength && (mul *= 0x100)) {
      val += this[offset + i] * mul;
    }
    mul *= 0x80;
  
    if (val >= mul) val -= Math.pow(2, 8 * byteLength);
  
    return val
  };
  
  Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
    offset = offset | 0;
    byteLength = byteLength | 0;
    if (!noAssert) checkOffset(offset, byteLength, this.length);
  
    var i = byteLength;
    var mul = 1;
    var val = this[offset + --i];
    while (i > 0 && (mul *= 0x100)) {
      val += this[offset + --i] * mul;
    }
    mul *= 0x80;
  
    if (val >= mul) val -= Math.pow(2, 8 * byteLength);
  
    return val
  };
  
  Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 1, this.length);
    if (!(this[offset] & 0x80)) return (this[offset])
    return ((0xff - this[offset] + 1) * -1)
  };
  
  Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 2, this.length);
    var val = this[offset] | (this[offset + 1] << 8);
    return (val & 0x8000) ? val | 0xFFFF0000 : val
  };
  
  Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 2, this.length);
    var val = this[offset + 1] | (this[offset] << 8);
    return (val & 0x8000) ? val | 0xFFFF0000 : val
  };
  
  Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length);
  
    return (this[offset]) |
      (this[offset + 1] << 8) |
      (this[offset + 2] << 16) |
      (this[offset + 3] << 24)
  };
  
  Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length);
  
    return (this[offset] << 24) |
      (this[offset + 1] << 16) |
      (this[offset + 2] << 8) |
      (this[offset + 3])
  };
  
  Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length);
    return read(this, offset, true, 23, 4)
  };
  
  Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 4, this.length);
    return read(this, offset, false, 23, 4)
  };
  
  Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 8, this.length);
    return read(this, offset, true, 52, 8)
  };
  
  Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
    if (!noAssert) checkOffset(offset, 8, this.length);
    return read(this, offset, false, 52, 8)
  };
  
  function checkInt (buf, value, offset, ext, max, min) {
    if (!internalIsBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
    if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
    if (offset + ext > buf.length) throw new RangeError('Index out of range')
  }
  
  Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
    value = +value;
    offset = offset | 0;
    byteLength = byteLength | 0;
    if (!noAssert) {
      var maxBytes = Math.pow(2, 8 * byteLength) - 1;
      checkInt(this, value, offset, byteLength, maxBytes, 0);
    }
  
    var mul = 1;
    var i = 0;
    this[offset] = value & 0xFF;
    while (++i < byteLength && (mul *= 0x100)) {
      this[offset + i] = (value / mul) & 0xFF;
    }
  
    return offset + byteLength
  };
  
  Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
    value = +value;
    offset = offset | 0;
    byteLength = byteLength | 0;
    if (!noAssert) {
      var maxBytes = Math.pow(2, 8 * byteLength) - 1;
      checkInt(this, value, offset, byteLength, maxBytes, 0);
    }
  
    var i = byteLength - 1;
    var mul = 1;
    this[offset + i] = value & 0xFF;
    while (--i >= 0 && (mul *= 0x100)) {
      this[offset + i] = (value / mul) & 0xFF;
    }
  
    return offset + byteLength
  };
  
  Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0);
    if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value);
    this[offset] = (value & 0xff);
    return offset + 1
  };
  
  function objectWriteUInt16 (buf, value, offset, littleEndian) {
    if (value < 0) value = 0xffff + value + 1;
    for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {
      buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
        (littleEndian ? i : 1 - i) * 8;
    }
  }
  
  Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0);
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value & 0xff);
      this[offset + 1] = (value >>> 8);
    } else {
      objectWriteUInt16(this, value, offset, true);
    }
    return offset + 2
  };
  
  Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0);
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value >>> 8);
      this[offset + 1] = (value & 0xff);
    } else {
      objectWriteUInt16(this, value, offset, false);
    }
    return offset + 2
  };
  
  function objectWriteUInt32 (buf, value, offset, littleEndian) {
    if (value < 0) value = 0xffffffff + value + 1;
    for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {
      buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff;
    }
  }
  
  Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0);
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset + 3] = (value >>> 24);
      this[offset + 2] = (value >>> 16);
      this[offset + 1] = (value >>> 8);
      this[offset] = (value & 0xff);
    } else {
      objectWriteUInt32(this, value, offset, true);
    }
    return offset + 4
  };
  
  Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0);
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value >>> 24);
      this[offset + 1] = (value >>> 16);
      this[offset + 2] = (value >>> 8);
      this[offset + 3] = (value & 0xff);
    } else {
      objectWriteUInt32(this, value, offset, false);
    }
    return offset + 4
  };
  
  Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) {
      var limit = Math.pow(2, 8 * byteLength - 1);
  
      checkInt(this, value, offset, byteLength, limit - 1, -limit);
    }
  
    var i = 0;
    var mul = 1;
    var sub = 0;
    this[offset] = value & 0xFF;
    while (++i < byteLength && (mul *= 0x100)) {
      if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
        sub = 1;
      }
      this[offset + i] = ((value / mul) >> 0) - sub & 0xFF;
    }
  
    return offset + byteLength
  };
  
  Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) {
      var limit = Math.pow(2, 8 * byteLength - 1);
  
      checkInt(this, value, offset, byteLength, limit - 1, -limit);
    }
  
    var i = byteLength - 1;
    var mul = 1;
    var sub = 0;
    this[offset + i] = value & 0xFF;
    while (--i >= 0 && (mul *= 0x100)) {
      if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
        sub = 1;
      }
      this[offset + i] = ((value / mul) >> 0) - sub & 0xFF;
    }
  
    return offset + byteLength
  };
  
  Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80);
    if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value);
    if (value < 0) value = 0xff + value + 1;
    this[offset] = (value & 0xff);
    return offset + 1
  };
  
  Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000);
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value & 0xff);
      this[offset + 1] = (value >>> 8);
    } else {
      objectWriteUInt16(this, value, offset, true);
    }
    return offset + 2
  };
  
  Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000);
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value >>> 8);
      this[offset + 1] = (value & 0xff);
    } else {
      objectWriteUInt16(this, value, offset, false);
    }
    return offset + 2
  };
  
  Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value & 0xff);
      this[offset + 1] = (value >>> 8);
      this[offset + 2] = (value >>> 16);
      this[offset + 3] = (value >>> 24);
    } else {
      objectWriteUInt32(this, value, offset, true);
    }
    return offset + 4
  };
  
  Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
    value = +value;
    offset = offset | 0;
    if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000);
    if (value < 0) value = 0xffffffff + value + 1;
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      this[offset] = (value >>> 24);
      this[offset + 1] = (value >>> 16);
      this[offset + 2] = (value >>> 8);
      this[offset + 3] = (value & 0xff);
    } else {
      objectWriteUInt32(this, value, offset, false);
    }
    return offset + 4
  };
  
  function checkIEEE754 (buf, value, offset, ext, max, min) {
    if (offset + ext > buf.length) throw new RangeError('Index out of range')
    if (offset < 0) throw new RangeError('Index out of range')
  }
  
  function writeFloat (buf, value, offset, littleEndian, noAssert) {
    if (!noAssert) {
      checkIEEE754(buf, value, offset, 4);
    }
    write(buf, value, offset, littleEndian, 23, 4);
    return offset + 4
  }
  
  Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
    return writeFloat(this, value, offset, true, noAssert)
  };
  
  Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
    return writeFloat(this, value, offset, false, noAssert)
  };
  
  function writeDouble (buf, value, offset, littleEndian, noAssert) {
    if (!noAssert) {
      checkIEEE754(buf, value, offset, 8);
    }
    write(buf, value, offset, littleEndian, 52, 8);
    return offset + 8
  }
  
  Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
    return writeDouble(this, value, offset, true, noAssert)
  };
  
  Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
    return writeDouble(this, value, offset, false, noAssert)
  };
  
  // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
  Buffer.prototype.copy = function copy (target, targetStart, start, end) {
    if (!start) start = 0;
    if (!end && end !== 0) end = this.length;
    if (targetStart >= target.length) targetStart = target.length;
    if (!targetStart) targetStart = 0;
    if (end > 0 && end < start) end = start;
  
    // Copy 0 bytes; we're done
    if (end === start) return 0
    if (target.length === 0 || this.length === 0) return 0
  
    // Fatal error conditions
    if (targetStart < 0) {
      throw new RangeError('targetStart out of bounds')
    }
    if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
    if (end < 0) throw new RangeError('sourceEnd out of bounds')
  
    // Are we oob?
    if (end > this.length) end = this.length;
    if (target.length - targetStart < end - start) {
      end = target.length - targetStart + start;
    }
  
    var len = end - start;
    var i;
  
    if (this === target && start < targetStart && targetStart < end) {
      // descending copy from end
      for (i = len - 1; i >= 0; --i) {
        target[i + targetStart] = this[i + start];
      }
    } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
      // ascending copy from start
      for (i = 0; i < len; ++i) {
        target[i + targetStart] = this[i + start];
      }
    } else {
      Uint8Array.prototype.set.call(
        target,
        this.subarray(start, start + len),
        targetStart
      );
    }
  
    return len
  };
  
  // Usage:
  //    buffer.fill(number[, offset[, end]])
  //    buffer.fill(buffer[, offset[, end]])
  //    buffer.fill(string[, offset[, end]][, encoding])
  Buffer.prototype.fill = function fill (val, start, end, encoding) {
    // Handle string cases:
    if (typeof val === 'string') {
      if (typeof start === 'string') {
        encoding = start;
        start = 0;
        end = this.length;
      } else if (typeof end === 'string') {
        encoding = end;
        end = this.length;
      }
      if (val.length === 1) {
        var code = val.charCodeAt(0);
        if (code < 256) {
          val = code;
        }
      }
      if (encoding !== undefined && typeof encoding !== 'string') {
        throw new TypeError('encoding must be a string')
      }
      if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
        throw new TypeError('Unknown encoding: ' + encoding)
      }
    } else if (typeof val === 'number') {
      val = val & 255;
    }
  
    // Invalid ranges are not set to a default, so can range check early.
    if (start < 0 || this.length < start || this.length < end) {
      throw new RangeError('Out of range index')
    }
  
    if (end <= start) {
      return this
    }
  
    start = start >>> 0;
    end = end === undefined ? this.length : end >>> 0;
  
    if (!val) val = 0;
  
    var i;
    if (typeof val === 'number') {
      for (i = start; i < end; ++i) {
        this[i] = val;
      }
    } else {
      var bytes = internalIsBuffer(val)
        ? val
        : utf8ToBytes(new Buffer(val, encoding).toString());
      var len = bytes.length;
      for (i = 0; i < end - start; ++i) {
        this[i + start] = bytes[i % len];
      }
    }
  
    return this
  };
  
  // HELPER FUNCTIONS
  // ================
  
  var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g;
  
  function base64clean (str) {
    // Node strips out invalid characters like \n and \t from the string, base64-js does not
    str = stringtrim(str).replace(INVALID_BASE64_RE, '');
    // Node converts strings with length < 2 to ''
    if (str.length < 2) return ''
    // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
    while (str.length % 4 !== 0) {
      str = str + '=';
    }
    return str
  }
  
  function stringtrim (str) {
    if (str.trim) return str.trim()
    return str.replace(/^\s+|\s+$/g, '')
  }
  
  function toHex (n) {
    if (n < 16) return '0' + n.toString(16)
    return n.toString(16)
  }
  
  function utf8ToBytes (string, units) {
    units = units || Infinity;
    var codePoint;
    var length = string.length;
    var leadSurrogate = null;
    var bytes = [];
  
    for (var i = 0; i < length; ++i) {
      codePoint = string.charCodeAt(i);
  
      // is surrogate component
      if (codePoint > 0xD7FF && codePoint < 0xE000) {
        // last char was a lead
        if (!leadSurrogate) {
          // no lead yet
          if (codePoint > 0xDBFF) {
            // unexpected trail
            if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD);
            continue
          } else if (i + 1 === length) {
            // unpaired lead
            if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD);
            continue
          }
  
          // valid lead
          leadSurrogate = codePoint;
  
          continue
        }
  
        // 2 leads in a row
        if (codePoint < 0xDC00) {
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD);
          leadSurrogate = codePoint;
          continue
        }
  
        // valid surrogate pair
        codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000;
      } else if (leadSurrogate) {
        // valid bmp char, but last char was a lead
        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD);
      }
  
      leadSurrogate = null;
  
      // encode utf8
      if (codePoint < 0x80) {
        if ((units -= 1) < 0) break
        bytes.push(codePoint);
      } else if (codePoint < 0x800) {
        if ((units -= 2) < 0) break
        bytes.push(
          codePoint >> 0x6 | 0xC0,
          codePoint & 0x3F | 0x80
        );
      } else if (codePoint < 0x10000) {
        if ((units -= 3) < 0) break
        bytes.push(
          codePoint >> 0xC | 0xE0,
          codePoint >> 0x6 & 0x3F | 0x80,
          codePoint & 0x3F | 0x80
        );
      } else if (codePoint < 0x110000) {
        if ((units -= 4) < 0) break
        bytes.push(
          codePoint >> 0x12 | 0xF0,
          codePoint >> 0xC & 0x3F | 0x80,
          codePoint >> 0x6 & 0x3F | 0x80,
          codePoint & 0x3F | 0x80
        );
      } else {
        throw new Error('Invalid code point')
      }
    }
  
    return bytes
  }
  
  function asciiToBytes (str) {
    var byteArray = [];
    for (var i = 0; i < str.length; ++i) {
      // Node's code seems to be doing this and not & 0x7F..
      byteArray.push(str.charCodeAt(i) & 0xFF);
    }
    return byteArray
  }
  
  function utf16leToBytes (str, units) {
    var c, hi, lo;
    var byteArray = [];
    for (var i = 0; i < str.length; ++i) {
      if ((units -= 2) < 0) break
  
      c = str.charCodeAt(i);
      hi = c >> 8;
      lo = c % 256;
      byteArray.push(lo);
      byteArray.push(hi);
    }
  
    return byteArray
  }
  
  
  function base64ToBytes (str) {
    return toByteArray(base64clean(str))
  }
  
  function blitBuffer (src, dst, offset, length) {
    for (var i = 0; i < length; ++i) {
      if ((i + offset >= dst.length) || (i >= src.length)) break
      dst[i + offset] = src[i];
    }
    return i
  }
  
  function isnan (val) {
    return val !== val // eslint-disable-line no-self-compare
  }
  
  
  // the following is from is-buffer, also by Feross Aboukhadijeh and with same lisence
  // The _isBuffer check is for Safari 5-7 support, because it's missing
  // Object.prototype.constructor. Remove this eventually
  function isBuffer(obj) {
    return obj != null && (!!obj._isBuffer || isFastBuffer(obj) || isSlowBuffer(obj))
  }
  
  function isFastBuffer (obj) {
    return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
  }
  
  // For Node v0.10 support. Remove this eventually.
  function isSlowBuffer (obj) {
    return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isFastBuffer(obj.slice(0, 0))
  }
  
  var types = {
    NUMBER: 'number',
    UNDEFINED: 'undefined',
    STRING: 'string',
    BOOLEAN: 'boolean',
    OBJECT: 'object',
    FUNCTION: 'function',
    NULL: 'null',
    ARRAY: 'array',
    REGEXP: 'regexp',
    DATE: 'date',
    ERROR: 'error',
    ARGUMENTS: 'arguments',
    SYMBOL: 'symbol',
    ARRAY_BUFFER: 'array-buffer',
    TYPED_ARRAY: 'typed-array',
    DATA_VIEW: 'data-view',
    MAP: 'map',
    SET: 'set',
    WEAK_SET: 'weak-set',
    WEAK_MAP: 'weak-map',
    PROMISE: 'promise',
  
  // node buffer
    BUFFER: 'buffer',
  
  // dom html element
    HTML_ELEMENT: 'html-element',
    HTML_ELEMENT_TEXT: 'html-element-text',
    DOCUMENT: 'document',
    WINDOW: 'window',
    FILE: 'file',
    FILE_LIST: 'file-list',
    BLOB: 'blob',
  
    HOST: 'host',
  
    XHR: 'xhr',
  
    // simd
    SIMD: 'simd'
  };
  
  /*
   * Simple data function to store type information
   * @param {string} type Usually what is returned from typeof
   * @param {string} cls  Sanitized @Class via Object.prototype.toString
   * @param {string} sub  If type and cls the same, and need to specify somehow
   * @private
   * @example
   *
   * //for null
   * new Type('null');
   *
   * //for Date
   * new Type('object', 'date');
   *
   * //for Uint8Array
   *
   * new Type('object', 'typed-array', 'uint8');
   */
  function Type(type, cls, sub) {
    if (!type) {
      throw new Error('Type class must be initialized at least with `type` information');
    }
    this.type = type;
    this.cls = cls;
    this.sub = sub;
  }
  
  Type.prototype = {
    toString: function(sep) {
      sep = sep || ';';
      var str = [this.type];
      if (this.cls) {
        str.push(this.cls);
      }
      if (this.sub) {
        str.push(this.sub);
      }
      return str.join(sep);
    },
  
    toTryTypes: function() {
      var _types = [];
      if (this.sub) {
        _types.push(new Type(this.type, this.cls, this.sub));
      }
      if (this.cls) {
        _types.push(new Type(this.type, this.cls));
      }
      _types.push(new Type(this.type));
  
      return _types;
    }
  };
  
  var toString$1 = Object.prototype.toString;
  
  
  
  /**
   * Function to store type checks
   * @private
   */
  function TypeChecker() {
    this.checks = [];
  }
  
  TypeChecker.prototype = {
    add: function(func) {
      this.checks.push(func);
      return this;
    },
  
    addBeforeFirstMatch: function(obj, func) {
      var match = this.getFirstMatch(obj);
      if (match) {
        this.checks.splice(match.index, 0, func);
      } else {
        this.add(func);
      }
    },
  
    addTypeOf: function(type, res) {
      return this.add(function(obj, tpeOf) {
        if (tpeOf === type) {
          return new Type(res);
        }
      });
    },
  
    addClass: function(cls, res, sub) {
      return this.add(function(obj, tpeOf, objCls) {
        if (objCls === cls) {
          return new Type(types.OBJECT, res, sub);
        }
      });
    },
  
    getFirstMatch: function(obj) {
      var typeOf = typeof obj;
      var cls = toString$1.call(obj);
  
      for (var i = 0, l = this.checks.length; i < l; i++) {
        var res = this.checks[i].call(this, obj, typeOf, cls);
        if (typeof res !== 'undefined') {
          return { result: res, func: this.checks[i], index: i };
        }
      }
    },
  
    getType: function(obj) {
      var match = this.getFirstMatch(obj);
      return match && match.result;
    }
  };
  
  var main = new TypeChecker();
  
  //TODO add iterators
  
  main
    .addTypeOf(types.NUMBER, types.NUMBER)
    .addTypeOf(types.UNDEFINED, types.UNDEFINED)
    .addTypeOf(types.STRING, types.STRING)
    .addTypeOf(types.BOOLEAN, types.BOOLEAN)
    .addTypeOf(types.FUNCTION, types.FUNCTION)
    .addTypeOf(types.SYMBOL, types.SYMBOL)
    .add(function(obj) {
      if (obj === null) {
        return new Type(types.NULL);
      }
    })
    .addClass('[object String]', types.STRING)
    .addClass('[object Boolean]', types.BOOLEAN)
    .addClass('[object Number]', types.NUMBER)
    .addClass('[object Array]', types.ARRAY)
    .addClass('[object RegExp]', types.REGEXP)
    .addClass('[object Error]', types.ERROR)
    .addClass('[object Date]', types.DATE)
    .addClass('[object Arguments]', types.ARGUMENTS)
  
    .addClass('[object ArrayBuffer]', types.ARRAY_BUFFER)
    .addClass('[object Int8Array]', types.TYPED_ARRAY, 'int8')
    .addClass('[object Uint8Array]', types.TYPED_ARRAY, 'uint8')
    .addClass('[object Uint8ClampedArray]', types.TYPED_ARRAY, 'uint8clamped')
    .addClass('[object Int16Array]', types.TYPED_ARRAY, 'int16')
    .addClass('[object Uint16Array]', types.TYPED_ARRAY, 'uint16')
    .addClass('[object Int32Array]', types.TYPED_ARRAY, 'int32')
    .addClass('[object Uint32Array]', types.TYPED_ARRAY, 'uint32')
    .addClass('[object Float32Array]', types.TYPED_ARRAY, 'float32')
    .addClass('[object Float64Array]', types.TYPED_ARRAY, 'float64')
  
    .addClass('[object Bool16x8]', types.SIMD, 'bool16x8')
    .addClass('[object Bool32x4]', types.SIMD, 'bool32x4')
    .addClass('[object Bool8x16]', types.SIMD, 'bool8x16')
    .addClass('[object Float32x4]', types.SIMD, 'float32x4')
    .addClass('[object Int16x8]', types.SIMD, 'int16x8')
    .addClass('[object Int32x4]', types.SIMD, 'int32x4')
    .addClass('[object Int8x16]', types.SIMD, 'int8x16')
    .addClass('[object Uint16x8]', types.SIMD, 'uint16x8')
    .addClass('[object Uint32x4]', types.SIMD, 'uint32x4')
    .addClass('[object Uint8x16]', types.SIMD, 'uint8x16')
  
    .addClass('[object DataView]', types.DATA_VIEW)
    .addClass('[object Map]', types.MAP)
    .addClass('[object WeakMap]', types.WEAK_MAP)
    .addClass('[object Set]', types.SET)
    .addClass('[object WeakSet]', types.WEAK_SET)
    .addClass('[object Promise]', types.PROMISE)
    .addClass('[object Blob]', types.BLOB)
    .addClass('[object File]', types.FILE)
    .addClass('[object FileList]', types.FILE_LIST)
    .addClass('[object XMLHttpRequest]', types.XHR)
    .add(function(obj) {
      if ((typeof Promise === types.FUNCTION && obj instanceof Promise) ||
          (typeof obj.then === types.FUNCTION)) {
            return new Type(types.OBJECT, types.PROMISE);
          }
    })
    .add(function(obj) {
      if (typeof Buffer !== 'undefined' && obj instanceof Buffer) {// eslint-disable-line no-undef
        return new Type(types.OBJECT, types.BUFFER);
      }
    })
    .add(function(obj) {
      if (typeof Node !== 'undefined' && obj instanceof Node) {
        return new Type(types.OBJECT, types.HTML_ELEMENT, obj.nodeName);
      }
    })
    .add(function(obj) {
      // probably at the begginging should be enough these checks
      if (obj.Boolean === Boolean && obj.Number === Number && obj.String === String && obj.Date === Date) {
        return new Type(types.OBJECT, types.HOST);
      }
    })
    .add(function() {
      return new Type(types.OBJECT);
    });
  
  /**
   * Get type information of anything
   *
   * @param  {any} obj Anything that could require type information
   * @return {Type}    type info
   * @private
   */
  function getGlobalType(obj) {
    return main.getType(obj);
  }
  
  getGlobalType.checker = main;
  getGlobalType.TypeChecker = TypeChecker;
  getGlobalType.Type = Type;
  
  Object.keys(types).forEach(function(typeName) {
    getGlobalType[typeName] = types[typeName];
  });
  
  var shouldType = getGlobalType;
  
  function format(msg) {
    var args = arguments;
    for (var i = 1, l = args.length; i < l; i++) {
      msg = msg.replace(/%s/, args[i]);
    }
    return msg;
  }
  
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  
  function EqualityFail(a, b, reason, path) {
    this.a = a;
    this.b = b;
    this.reason = reason;
    this.path = path;
  }
  
  function typeToString(tp) {
    return tp.type + (tp.cls ? "(" + tp.cls + (tp.sub ? " " + tp.sub : "") + ")" : "");
  }
  
  var PLUS_0_AND_MINUS_0 = "+0 is not equal to -0";
  var DIFFERENT_TYPES = "A has type %s and B has type %s";
  var EQUALITY = "A is not equal to B";
  var EQUALITY_PROTOTYPE = "A and B have different prototypes";
  var WRAPPED_VALUE = "A wrapped value is not equal to B wrapped value";
  var FUNCTION_SOURCES = "function A is not equal to B by source code value (via .toString call)";
  var MISSING_KEY = "%s has no key %s";
  var SET_MAP_MISSING_KEY = "Set/Map missing key %s";
  
  var DEFAULT_OPTIONS = {
    checkProtoEql: true,
    checkSubType: true,
    plusZeroAndMinusZeroEqual: true,
    collectAllFails: false
  };
  
  function setBooleanDefault(property, obj, opts, defaults) {
    obj[property] = typeof opts[property] !== "boolean" ? defaults[property] : opts[property];
  }
  
  var METHOD_PREFIX = "_check_";
  
  function EQ(opts, a, b, path) {
    opts = opts || {};
  
    setBooleanDefault("checkProtoEql", this, opts, DEFAULT_OPTIONS);
    setBooleanDefault("plusZeroAndMinusZeroEqual", this, opts, DEFAULT_OPTIONS);
    setBooleanDefault("checkSubType", this, opts, DEFAULT_OPTIONS);
    setBooleanDefault("collectAllFails", this, opts, DEFAULT_OPTIONS);
  
    this.a = a;
    this.b = b;
  
    this._meet = opts._meet || [];
  
    this.fails = opts.fails || [];
  
    this.path = path || [];
  }
  
  function ShortcutError(fail) {
    this.name = "ShortcutError";
    this.message = "fail fast";
    this.fail = fail;
  }
  
  ShortcutError.prototype = Object.create(Error.prototype);
  
  EQ.checkStrictEquality = function(a, b) {
    this.collectFail(a !== b, EQUALITY);
  };
  
  EQ.add = function add(type, cls, sub, f) {
    var args = Array.prototype.slice.call(arguments);
    f = args.pop();
    EQ.prototype[METHOD_PREFIX + args.join("_")] = f;
  };
  
  EQ.prototype = {
    check: function() {
      try {
        this.check0();
      } catch (e) {
        if (e instanceof ShortcutError) {
          return [e.fail];
        }
        throw e;
      }
      return this.fails;
    },
  
    check0: function() {
      var a = this.a;
      var b = this.b;
  
      // equal a and b exit early
      if (a === b) {
        // check for +0 !== -0;
        return this.collectFail(a === 0 && 1 / a !== 1 / b && !this.plusZeroAndMinusZeroEqual, PLUS_0_AND_MINUS_0);
      }
  
      var typeA = shouldType(a);
      var typeB = shouldType(b);
  
      // if objects has different types they are not equal
      if (typeA.type !== typeB.type || typeA.cls !== typeB.cls || typeA.sub !== typeB.sub) {
        return this.collectFail(true, format(DIFFERENT_TYPES, typeToString(typeA), typeToString(typeB)));
      }
  
      // as types the same checks type specific things
      var name1 = typeA.type,
        name2 = typeA.type;
      if (typeA.cls) {
        name1 += "_" + typeA.cls;
        name2 += "_" + typeA.cls;
      }
      if (typeA.sub) {
        name2 += "_" + typeA.sub;
      }
  
      var f =
        this[METHOD_PREFIX + name2] ||
        this[METHOD_PREFIX + name1] ||
        this[METHOD_PREFIX + typeA.type] ||
        this.defaultCheck;
  
      f.call(this, this.a, this.b);
    },
  
    collectFail: function(comparison, reason, showReason) {
      if (comparison) {
        var res = new EqualityFail(this.a, this.b, reason, this.path);
        res.showReason = !!showReason;
  
        this.fails.push(res);
  
        if (!this.collectAllFails) {
          throw new ShortcutError(res);
        }
      }
    },
  
    checkPlainObjectsEquality: function(a, b) {
      // compare deep objects and arrays
      // stacks contain references only
      //
      var meet = this._meet;
      var m = this._meet.length;
      while (m--) {
        var st = meet[m];
        if (st[0] === a && st[1] === b) {
          return;
        }
      }
  
      // add `a` and `b` to the stack of traversed objects
      meet.push([a, b]);
  
      // TODO maybe something else like getOwnPropertyNames
      var key;
      for (key in b) {
        if (hasOwnProperty.call(b, key)) {
          if (hasOwnProperty.call(a, key)) {
            this.checkPropertyEquality(key);
          } else {
            this.collectFail(true, format(MISSING_KEY, "A", key));
          }
        }
      }
  
      // ensure both objects have the same number of properties
      for (key in a) {
        if (hasOwnProperty.call(a, key)) {
          this.collectFail(!hasOwnProperty.call(b, key), format(MISSING_KEY, "B", key));
        }
      }
  
      meet.pop();
  
      if (this.checkProtoEql) {
        //TODO should i check prototypes for === or use eq?
        this.collectFail(Object.getPrototypeOf(a) !== Object.getPrototypeOf(b), EQUALITY_PROTOTYPE, true);
      }
    },
  
    checkPropertyEquality: function(propertyName) {
      var _eq = new EQ(this, this.a[propertyName], this.b[propertyName], this.path.concat([propertyName]));
      _eq.check0();
    },
  
    defaultCheck: EQ.checkStrictEquality
  };
  
  EQ.add(shouldType.NUMBER, function(a, b) {
    this.collectFail((a !== a && b === b) || (b !== b && a === a) || (a !== b && a === a && b === b), EQUALITY);
  });
  
  [shouldType.SYMBOL, shouldType.BOOLEAN, shouldType.STRING].forEach(function(tp) {
    EQ.add(tp, EQ.checkStrictEquality);
  });
  
  EQ.add(shouldType.FUNCTION, function(a, b) {
    // functions are compared by their source code
    this.collectFail(a.toString() !== b.toString(), FUNCTION_SOURCES);
    // check user properties
    this.checkPlainObjectsEquality(a, b);
  });
  
  EQ.add(shouldType.OBJECT, shouldType.REGEXP, function(a, b) {
    // check regexp flags
    var flags = ["source", "global", "multiline", "lastIndex", "ignoreCase", "sticky", "unicode"];
    while (flags.length) {
      this.checkPropertyEquality(flags.shift());
    }
    // check user properties
    this.checkPlainObjectsEquality(a, b);
  });
  
  EQ.add(shouldType.OBJECT, shouldType.DATE, function(a, b) {
    //check by timestamp only (using .valueOf)
    this.collectFail(+a !== +b, EQUALITY);
    // check user properties
    this.checkPlainObjectsEquality(a, b);
  });
  
  [shouldType.NUMBER, shouldType.BOOLEAN, shouldType.STRING].forEach(function(tp) {
    EQ.add(shouldType.OBJECT, tp, function(a, b) {
      //primitive type wrappers
      this.collectFail(a.valueOf() !== b.valueOf(), WRAPPED_VALUE);
      // check user properties
      this.checkPlainObjectsEquality(a, b);
    });
  });
  
  EQ.add(shouldType.OBJECT, function(a, b) {
    this.checkPlainObjectsEquality(a, b);
  });
  
  [shouldType.ARRAY, shouldType.ARGUMENTS, shouldType.TYPED_ARRAY].forEach(function(tp) {
    EQ.add(shouldType.OBJECT, tp, function(a, b) {
      this.checkPropertyEquality("length");
  
      this.checkPlainObjectsEquality(a, b);
    });
  });
  
  EQ.add(shouldType.OBJECT, shouldType.ARRAY_BUFFER, function(a, b) {
    this.checkPropertyEquality("byteLength");
  
    this.checkPlainObjectsEquality(a, b);
  });
  
  EQ.add(shouldType.OBJECT, shouldType.ERROR, function(a, b) {
    this.checkPropertyEquality("name");
    this.checkPropertyEquality("message");
  
    this.checkPlainObjectsEquality(a, b);
  });
  
  EQ.add(shouldType.OBJECT, shouldType.BUFFER, function(a) {
    this.checkPropertyEquality("length");
  
    var l = a.length;
    while (l--) {
      this.checkPropertyEquality(l);
    }
  
    //we do not check for user properties because
    //node Buffer have some strange hidden properties
  });
  
  function checkMapByKeys(a, b) {
    var iteratorA = a.keys();
  
    for (var nextA = iteratorA.next(); !nextA.done; nextA = iteratorA.next()) {
      var key = nextA.value;
      var hasKey = b.has(key);
      this.collectFail(!hasKey, format(SET_MAP_MISSING_KEY, key));
  
      if (hasKey) {
        var valueB = b.get(key);
        var valueA = a.get(key);
  
        eq(valueA, valueB, this);
      }
    }
  }
  
  function checkSetByKeys(a, b) {
    var iteratorA = a.keys();
  
    for (var nextA = iteratorA.next(); !nextA.done; nextA = iteratorA.next()) {
      var key = nextA.value;
      var hasKey = b.has(key);
      this.collectFail(!hasKey, format(SET_MAP_MISSING_KEY, key));
    }
  }
  
  EQ.add(shouldType.OBJECT, shouldType.MAP, function(a, b) {
    this._meet.push([a, b]);
  
    checkMapByKeys.call(this, a, b);
    checkMapByKeys.call(this, b, a);
  
    this._meet.pop();
  
    this.checkPlainObjectsEquality(a, b);
  });
  EQ.add(shouldType.OBJECT, shouldType.SET, function(a, b) {
    this._meet.push([a, b]);
  
    checkSetByKeys.call(this, a, b);
    checkSetByKeys.call(this, b, a);
  
    this._meet.pop();
  
    this.checkPlainObjectsEquality(a, b);
  });
  
  function eq(a, b, opts) {
    return new EQ(opts, a, b).check();
  }
  
  eq.EQ = EQ;
  
  function createCommonjsModule(fn, basedir, module) {
  	return module = {
  	  path: basedir,
  	  exports: {},
  	  require: function (path, base) {
        return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
      }
  	}, fn(module, module.exports), module.exports;
  }
  
  function commonjsRequire () {
  	throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
  }
  
  var _hasOwnProperty = Object.prototype.hasOwnProperty;
  var _propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
  
  function hasOwnProperty$1(obj, key) {
    return _hasOwnProperty.call(obj, key);
  }
  
  function propertyIsEnumerable(obj, key) {
    return _propertyIsEnumerable.call(obj, key);
  }
  
  function merge(a, b) {
    if (a && b) {
      for (var key in b) {
        a[key] = b[key];
      }
    }
    return a;
  }
  
  function isIterator(obj) {
    if (!obj) {
      return false;
    }
  
    if (obj.__shouldIterator__) {
      return true;
    }
  
    return typeof obj.next === 'function' &&
      typeof Symbol === 'function' &&
      typeof Symbol.iterator === 'symbol' &&
      typeof obj[Symbol.iterator] === 'function' &&
      obj[Symbol.iterator]() === obj;
  }
  
  //TODO find better way
  function isGeneratorFunction(f) {
    return typeof f === 'function' && /^function\s*\*\s*/.test(f.toString());
  }
  
  var shouldUtil = /*#__PURE__*/Object.freeze({
    __proto__: null,
    hasOwnProperty: hasOwnProperty$1,
    propertyIsEnumerable: propertyIsEnumerable,
    merge: merge,
    isIterator: isIterator,
    isGeneratorFunction: isGeneratorFunction
  });
  
  var shouldTypeAdaptors = createCommonjsModule(function (module, exports) {
  
  Object.defineProperty(exports, '__esModule', { value: true });
  
  function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
  
  
  var t = _interopDefault(shouldType);
  
  // TODO in future add generators instead of forEach and iterator implementation
  
  
  function ObjectIterator(obj) {
    this._obj = obj;
  }
  
  ObjectIterator.prototype = {
    __shouldIterator__: true, // special marker
  
    next: function() {
      if (this._done) {
        throw new Error('Iterator already reached the end');
      }
  
      if (!this._keys) {
        this._keys = Object.keys(this._obj);
        this._index = 0;
      }
  
      var key = this._keys[this._index];
      this._done = this._index === this._keys.length;
      this._index += 1;
  
      return {
        value: this._done ? void 0: [key, this._obj[key]],
        done: this._done
      };
    }
  };
  
  if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
    ObjectIterator.prototype[Symbol.iterator] = function() {
      return this;
    };
  }
  
  
  function TypeAdaptorStorage() {
    this._typeAdaptors = [];
    this._iterableTypes = {};
  }
  
  TypeAdaptorStorage.prototype = {
    add: function(type, cls, sub, adaptor) {
      return this.addType(new t.Type(type, cls, sub), adaptor);
    },
  
    addType: function(type, adaptor) {
      this._typeAdaptors[type.toString()] = adaptor;
    },
  
    getAdaptor: function(tp, funcName) {
      var tries = tp.toTryTypes();
      while (tries.length) {
        var toTry = tries.shift();
        var ad = this._typeAdaptors[toTry];
        if (ad && ad[funcName]) {
          return ad[funcName];
        }
      }
    },
  
    requireAdaptor: function(tp, funcName) {
      var a = this.getAdaptor(tp, funcName);
      if (!a) {
        throw new Error('There is no type adaptor `' + funcName + '` for ' + tp.toString());
      }
      return a;
    },
  
    addIterableType: function(tp) {
      this._iterableTypes[tp.toString()] = true;
    },
  
    isIterableType: function(tp) {
      return !!this._iterableTypes[tp.toString()];
    }
  };
  
  var defaultTypeAdaptorStorage = new TypeAdaptorStorage();
  
  var objectAdaptor = {
    forEach: function(obj, f, context) {
      for (var prop in obj) {
        if (shouldUtil.hasOwnProperty(obj, prop) && shouldUtil.propertyIsEnumerable(obj, prop)) {
          if (f.call(context, obj[prop], prop, obj) === false) {
            return;
          }
        }
      }
    },
  
    has: function(obj, prop) {
      return shouldUtil.hasOwnProperty(obj, prop);
    },
  
    get: function(obj, prop) {
      return obj[prop];
    },
  
    iterator: function(obj) {
      return new ObjectIterator(obj);
    }
  };
  
  // default for objects
  defaultTypeAdaptorStorage.addType(new t.Type(t.OBJECT), objectAdaptor);
  defaultTypeAdaptorStorage.addType(new t.Type(t.FUNCTION), objectAdaptor);
  
  var mapAdaptor = {
    has: function(obj, key) {
      return obj.has(key);
    },
  
    get: function(obj, key) {
      return obj.get(key);
    },
  
    forEach: function(obj, f, context) {
      var iter = obj.entries();
      forEach(iter, function(value) {
        return f.call(context, value[1], value[0], obj);
      });
    },
  
    size: function(obj) {
      return obj.size;
    },
  
    isEmpty: function(obj) {
      return obj.size === 0;
    },
  
    iterator: function(obj) {
      return obj.entries();
    }
  };
  
  var setAdaptor = shouldUtil.merge({}, mapAdaptor);
  setAdaptor.get = function(obj, key) {
    if (obj.has(key)) {
      return key;
    }
  };
  setAdaptor.iterator = function(obj) {
    return obj.values();
  };
  
  defaultTypeAdaptorStorage.addType(new t.Type(t.OBJECT, t.MAP), mapAdaptor);
  defaultTypeAdaptorStorage.addType(new t.Type(t.OBJECT, t.SET), setAdaptor);
  defaultTypeAdaptorStorage.addType(new t.Type(t.OBJECT, t.WEAK_SET), setAdaptor);
  defaultTypeAdaptorStorage.addType(new t.Type(t.OBJECT, t.WEAK_MAP), mapAdaptor);
  
  defaultTypeAdaptorStorage.addType(new t.Type(t.STRING), {
    isEmpty: function(obj) {
      return obj === '';
    },
  
    size: function(obj) {
      return obj.length;
    }
  });
  
  defaultTypeAdaptorStorage.addIterableType(new t.Type(t.OBJECT, t.ARRAY));
  defaultTypeAdaptorStorage.addIterableType(new t.Type(t.OBJECT, t.ARGUMENTS));
  defaultTypeAdaptorStorage.addIterableType(new t.Type(t.OBJECT, t.SET));
  
  function forEach(obj, f, context) {
    if (shouldUtil.isGeneratorFunction(obj)) {
      return forEach(obj(), f, context);
    } else if (shouldUtil.isIterator(obj)) {
      var value = obj.next();
      while (!value.done) {
        if (f.call(context, value.value, 'value', obj) === false) {
          return;
        }
        value = obj.next();
      }
    } else {
      var type = t(obj);
      var func = defaultTypeAdaptorStorage.requireAdaptor(type, 'forEach');
      func(obj, f, context);
    }
  }
  
  
  function size(obj) {
    var type = t(obj);
    var func = defaultTypeAdaptorStorage.getAdaptor(type, 'size');
    if (func) {
      return func(obj);
    } else {
      var len = 0;
      forEach(obj, function() {
        len += 1;
      });
      return len;
    }
  }
  
  function isEmpty(obj) {
    var type = t(obj);
    var func = defaultTypeAdaptorStorage.getAdaptor(type, 'isEmpty');
    if (func) {
      return func(obj);
    } else {
      var res = true;
      forEach(obj, function() {
        res = false;
        return false;
      });
      return res;
    }
  }
  
  // return boolean if obj has such 'key'
  function has(obj, key) {
    var type = t(obj);
    var func = defaultTypeAdaptorStorage.requireAdaptor(type, 'has');
    return func(obj, key);
  }
  
  // return value for given key
  function get(obj, key) {
    var type = t(obj);
    var func = defaultTypeAdaptorStorage.requireAdaptor(type, 'get');
    return func(obj, key);
  }
  
  function reduce(obj, f, initialValue) {
    var res = initialValue;
    forEach(obj, function(value, key) {
      res = f(res, value, key, obj);
    });
    return res;
  }
  
  function some(obj, f, context) {
    var res = false;
    forEach(obj, function(value, key) {
      if (f.call(context, value, key, obj)) {
        res = true;
        return false;
      }
    }, context);
    return res;
  }
  
  function every(obj, f, context) {
    var res = true;
    forEach(obj, function(value, key) {
      if (!f.call(context, value, key, obj)) {
        res = false;
        return false;
      }
    }, context);
    return res;
  }
  
  function isIterable(obj) {
    return defaultTypeAdaptorStorage.isIterableType(t(obj));
  }
  
  function iterator(obj) {
    return defaultTypeAdaptorStorage.requireAdaptor(t(obj), 'iterator')(obj);
  }
  
  exports.defaultTypeAdaptorStorage = defaultTypeAdaptorStorage;
  exports.forEach = forEach;
  exports.size = size;
  exports.isEmpty = isEmpty;
  exports.has = has;
  exports.get = get;
  exports.reduce = reduce;
  exports.some = some;
  exports.every = every;
  exports.isIterable = isIterable;
  exports.iterator = iterator;
  });
  
  function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
  
  var t = _interopDefault(shouldType);
  
  
  function looksLikeANumber(n) {
    return !!n.match(/\d+/);
  }
  
  function keyCompare(a, b) {
    var aNum = looksLikeANumber(a);
    var bNum = looksLikeANumber(b);
    if (aNum && bNum) {
      return 1*a - 1*b;
    } else if (aNum && !bNum) {
      return -1;
    } else if (!aNum && bNum) {
      return 1;
    } else {
      return a.localeCompare(b);
    }
  }
  
  function genKeysFunc(f) {
    return function(value) {
      var k = f(value);
      k.sort(keyCompare);
      return k;
    };
  }
  
  function Formatter(opts) {
    opts = opts || {};
  
    this.seen = [];
  
    var keysFunc;
    if (typeof opts.keysFunc === 'function') {
      keysFunc = opts.keysFunc;
    } else if (opts.keys === false) {
      keysFunc = Object.getOwnPropertyNames;
    } else {
      keysFunc = Object.keys;
    }
  
    this.getKeys = genKeysFunc(keysFunc);
  
    this.maxLineLength = typeof opts.maxLineLength === 'number' ? opts.maxLineLength : 60;
    this.propSep = opts.propSep || ',';
  
    this.isUTCdate = !!opts.isUTCdate;
  }
  
  
  
  Formatter.prototype = {
    constructor: Formatter,
  
    format: function(value) {
      var tp = t(value);
  
      if (this.alreadySeen(value)) {
        return '[Circular]';
      }
  
      var tries = tp.toTryTypes();
      var f = this.defaultFormat;
      while (tries.length) {
        var toTry = tries.shift();
        var name = Formatter.formatterFunctionName(toTry);
        if (this[name]) {
          f = this[name];
          break;
        }
      }
      return f.call(this, value).trim();
    },
  
    defaultFormat: function(obj) {
      return String(obj);
    },
  
    alreadySeen: function(value) {
      return this.seen.indexOf(value) >= 0;
    }
  
  };
  
  Formatter.addType = function addType(tp, f) {
    Formatter.prototype[Formatter.formatterFunctionName(tp)] = f;
  };
  
  Formatter.formatterFunctionName = function formatterFunctionName(tp) {
    return '_format_' + tp.toString('_');
  };
  
  var EOL = '\n';
  
  function indent(v, indentation) {
    return v
      .split(EOL)
      .map(function(vv) {
        return indentation + vv;
      })
      .join(EOL);
  }
  
  function pad(str, value, filler) {
    str = String(str);
    var isRight = false;
  
    if (value < 0) {
      isRight = true;
      value = -value;
    }
  
    if (str.length < value) {
      var padding = new Array(value - str.length + 1).join(filler);
      return isRight ? str + padding : padding + str;
    } else {
      return str;
    }
  }
  
  function pad0(str, value) {
    return pad(str, value, '0');
  }
  
  var functionNameRE = /^\s*function\s*(\S*)\s*\(/;
  
  function functionName(f) {
    if (f.name) {
      return f.name;
    }
    var matches = f.toString().match(functionNameRE);
    if (matches === null) {
      // `functionNameRE` doesn't match arrow functions.
      return '';
    }
    var name = matches[1];
    return name;
  }
  
  function constructorName(obj) {
    while (obj) {
      var descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor');
      if (descriptor !== undefined &&  typeof descriptor.value === 'function') {
        var name = functionName(descriptor.value);
        if (name !== '') {
          return name;
        }
      }
  
      obj = Object.getPrototypeOf(obj);
    }
  }
  
  var INDENT = '  ';
  
  function addSpaces(str) {
    return indent(str, INDENT);
  }
  
  function typeAdaptorForEachFormat(obj, opts) {
    opts = opts || {};
    var filterKey = opts.filterKey || function() { return true; };
  
    var formatKey = opts.formatKey || this.format;
    var formatValue = opts.formatValue || this.format;
  
    var keyValueSep = typeof opts.keyValueSep !== 'undefined' ? opts.keyValueSep : ': ';
  
    this.seen.push(obj);
  
    var formatLength = 0;
    var pairs = [];
  
    shouldTypeAdaptors.forEach(obj, function(value, key) {
      if (!filterKey(key)) {
        return;
      }
  
      var formattedKey = formatKey.call(this, key);
      var formattedValue = formatValue.call(this, value, key);
  
      var pair = formattedKey ? (formattedKey + keyValueSep + formattedValue) : formattedValue;
  
      formatLength += pair.length;
      pairs.push(pair);
    }, this);
  
    this.seen.pop();
  
    (opts.additionalKeys || []).forEach(function(keyValue) {
      var pair = keyValue[0] + keyValueSep + this.format(keyValue[1]);
      formatLength += pair.length;
      pairs.push(pair);
    }, this);
  
    var prefix = opts.prefix || constructorName(obj) || '';
    if (prefix.length > 0) {
      prefix += ' ';
    }
  
    var lbracket, rbracket;
    if (Array.isArray(opts.brackets)) {
      lbracket = opts.brackets[0];
      rbracket = opts.brackets[1];
    } else {
      lbracket = '{';
      rbracket = '}';
    }
  
    var rootValue = opts.value || '';
  
    if (pairs.length === 0) {
      return rootValue || (prefix + lbracket + rbracket);
    }
  
    if (formatLength <= this.maxLineLength) {
      return prefix + lbracket + ' ' + (rootValue ? rootValue + ' ' : '') + pairs.join(this.propSep + ' ') + ' ' + rbracket;
    } else {
      return prefix + lbracket + '\n' + (rootValue ? '  ' + rootValue + '\n' : '') + pairs.map(addSpaces).join(this.propSep + '\n') + '\n' + rbracket;
    }
  }
  
  function formatPlainObjectKey(key) {
    return typeof key === 'string' && key.match(/^[a-zA-Z_$][a-zA-Z_$0-9]*$/) ? key : this.format(key);
  }
  
  function getPropertyDescriptor(obj, key) {
    var desc;
    try {
      desc = Object.getOwnPropertyDescriptor(obj, key) || { value: obj[key] };
    } catch (e) {
      desc = { value: e };
    }
    return desc;
  }
  
  function formatPlainObjectValue(obj, key) {
    var desc = getPropertyDescriptor(obj, key);
    if (desc.get && desc.set) {
      return '[Getter/Setter]';
    }
    if (desc.get) {
      return '[Getter]';
    }
    if (desc.set) {
      return '[Setter]';
    }
  
    return this.format(desc.value);
  }
  
  function formatPlainObject(obj, opts) {
    opts = opts || {};
    opts.keyValueSep = ': ';
    opts.formatKey = opts.formatKey || formatPlainObjectKey;
    opts.formatValue = opts.formatValue || function(value, key) {
      return formatPlainObjectValue.call(this, obj, key);
    };
    return typeAdaptorForEachFormat.call(this, obj, opts);
  }
  
  function formatWrapper1(value) {
    return formatPlainObject.call(this, value, {
      additionalKeys: [['[[PrimitiveValue]]', value.valueOf()]]
    });
  }
  
  
  function formatWrapper2(value) {
    var realValue = value.valueOf();
  
    return formatPlainObject.call(this, value, {
      filterKey: function(key) {
        //skip useless indexed properties
        return !(key.match(/\d+/) && parseInt(key, 10) < realValue.length);
      },
      additionalKeys: [['[[PrimitiveValue]]', realValue]]
    });
  }
  
  function formatRegExp(value) {
    return formatPlainObject.call(this, value, {
      value: String(value)
    });
  }
  
  function formatFunction(value) {
    return formatPlainObject.call(this, value, {
      prefix: 'Function',
      additionalKeys: [['name', functionName(value)]]
    });
  }
  
  function formatArray(value) {
    return formatPlainObject.call(this, value, {
      formatKey: function(key) {
        if (!key.match(/\d+/)) {
          return formatPlainObjectKey.call(this, key);
        }
      },
      brackets: ['[', ']']
    });
  }
  
  function formatArguments(value) {
    return formatPlainObject.call(this, value, {
      formatKey: function(key) {
        if (!key.match(/\d+/)) {
          return formatPlainObjectKey.call(this, key);
        }
      },
      brackets: ['[', ']'],
      prefix: 'Arguments'
    });
  }
  
  function _formatDate(value, isUTC) {
    var prefix = isUTC ? 'UTC' : '';
  
    var date = value['get' + prefix + 'FullYear']() +
      '-' +
      pad0(value['get' + prefix + 'Month']() + 1, 2) +
      '-' +
      pad0(value['get' + prefix + 'Date'](), 2);
  
    var time = pad0(value['get' + prefix + 'Hours'](), 2) +
      ':' +
      pad0(value['get' + prefix + 'Minutes'](), 2) +
      ':' +
      pad0(value['get' + prefix + 'Seconds'](), 2) +
      '.' +
      pad0(value['get' + prefix + 'Milliseconds'](), 3);
  
    var to = value.getTimezoneOffset();
    var absTo = Math.abs(to);
    var hours = Math.floor(absTo / 60);
    var minutes = absTo - hours * 60;
    var tzFormat = (to < 0 ? '+' : '-') + pad0(hours, 2) + pad0(minutes, 2);
  
    return date + ' ' + time + (isUTC ? '' : ' ' + tzFormat);
  }
  
  function formatDate(value) {
    return formatPlainObject.call(this, value, { value: _formatDate(value, this.isUTCdate) });
  }
  
  function formatError(value) {
    return formatPlainObject.call(this, value, {
      prefix: value.name,
      additionalKeys: [['message', value.message]]
    });
  }
  
  function generateFormatForNumberArray(lengthProp, name, padding) {
    return function(value) {
      var max = this.byteArrayMaxLength || 50;
      var length = value[lengthProp];
      var formattedValues = [];
      var len = 0;
      for (var i = 0; i < max && i < length; i++) {
        var b = value[i] || 0;
        var v = pad0(b.toString(16), padding);
        len += v.length;
        formattedValues.push(v);
      }
      var prefix = value.constructor.name || name || '';
      if (prefix) {
        prefix += ' ';
      }
  
      if (formattedValues.length === 0) {
        return prefix + '[]';
      }
  
      if (len <= this.maxLineLength) {
        return prefix + '[ ' + formattedValues.join(this.propSep + ' ') + ' ' + ']';
      } else {
        return prefix + '[\n' + formattedValues.map(addSpaces).join(this.propSep + '\n') + '\n' + ']';
      }
    };
  }
  
  function formatMap(obj) {
    return typeAdaptorForEachFormat.call(this, obj, {
      keyValueSep: ' => '
    });
  }
  
  function formatSet(obj) {
    return typeAdaptorForEachFormat.call(this, obj, {
      keyValueSep: '',
      formatKey: function() { return ''; }
    });
  }
  
  function genSimdVectorFormat(constructorName, length) {
    return function(value) {
      var Constructor = value.constructor;
      var extractLane = Constructor.extractLane;
  
      var len = 0;
      var props = [];
  
      for (var i = 0; i < length; i ++) {
        var key = this.format(extractLane(value, i));
        len += key.length;
        props.push(key);
      }
  
      if (len <= this.maxLineLength) {
        return constructorName + ' [ ' + props.join(this.propSep + ' ') + ' ]';
      } else {
        return constructorName + ' [\n' + props.map(addSpaces).join(this.propSep + '\n') + '\n' + ']';
      }
    };
  }
  
  function defaultFormat(value, opts) {
    return new Formatter(opts).format(value);
  }
  
  defaultFormat.Formatter = Formatter;
  defaultFormat.addSpaces = addSpaces;
  defaultFormat.pad0 = pad0;
  defaultFormat.functionName = functionName;
  defaultFormat.constructorName = constructorName;
  defaultFormat.formatPlainObjectKey = formatPlainObjectKey;
  defaultFormat.formatPlainObject = formatPlainObject;
  defaultFormat.typeAdaptorForEachFormat = typeAdaptorForEachFormat;
  // adding primitive types
  Formatter.addType(new t.Type(t.UNDEFINED), function() {
    return 'undefined';
  });
  Formatter.addType(new t.Type(t.NULL), function() {
    return 'null';
  });
  Formatter.addType(new t.Type(t.BOOLEAN), function(value) {
    return value ? 'true': 'false';
  });
  Formatter.addType(new t.Type(t.SYMBOL), function(value) {
    return value.toString();
  });
  Formatter.addType(new t.Type(t.NUMBER), function(value) {
    if (value === 0 && 1 / value < 0) {
      return '-0';
    }
    return String(value);
  });
  
  Formatter.addType(new t.Type(t.STRING), function(value) {
    return '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
        .replace(/'/g, "\\'")
        .replace(/\\"/g, '"') + '\'';
  });
  
  Formatter.addType(new t.Type(t.FUNCTION), formatFunction);
  
  // plain object
  Formatter.addType(new t.Type(t.OBJECT), formatPlainObject);
  
  // type wrappers
  Formatter.addType(new t.Type(t.OBJECT, t.NUMBER), formatWrapper1);
  Formatter.addType(new t.Type(t.OBJECT, t.BOOLEAN), formatWrapper1);
  Formatter.addType(new t.Type(t.OBJECT, t.STRING), formatWrapper2);
  
  Formatter.addType(new t.Type(t.OBJECT, t.REGEXP), formatRegExp);
  Formatter.addType(new t.Type(t.OBJECT, t.ARRAY), formatArray);
  Formatter.addType(new t.Type(t.OBJECT, t.ARGUMENTS), formatArguments);
  Formatter.addType(new t.Type(t.OBJECT, t.DATE), formatDate);
  Formatter.addType(new t.Type(t.OBJECT, t.ERROR), formatError);
  Formatter.addType(new t.Type(t.OBJECT, t.SET), formatSet);
  Formatter.addType(new t.Type(t.OBJECT, t.MAP), formatMap);
  Formatter.addType(new t.Type(t.OBJECT, t.WEAK_MAP), formatMap);
  Formatter.addType(new t.Type(t.OBJECT, t.WEAK_SET), formatSet);
  
  Formatter.addType(new t.Type(t.OBJECT, t.BUFFER), generateFormatForNumberArray('length', 'Buffer', 2));
  
  Formatter.addType(new t.Type(t.OBJECT, t.ARRAY_BUFFER), generateFormatForNumberArray('byteLength', 'ArrayBuffer', 2));
  
  Formatter.addType(new t.Type(t.OBJECT, t.TYPED_ARRAY, 'int8'), generateFormatForNumberArray('length', 'Int8Array', 2));
  Formatter.addType(new t.Type(t.OBJECT, t.TYPED_ARRAY, 'uint8'), generateFormatForNumberArray('length', 'Uint8Array', 2));
  Formatter.addType(new t.Type(t.OBJECT, t.TYPED_ARRAY, 'uint8clamped'), generateFormatForNumberArray('length', 'Uint8ClampedArray', 2));
  
  Formatter.addType(new t.Type(t.OBJECT, t.TYPED_ARRAY, 'int16'), generateFormatForNumberArray('length', 'Int16Array', 4));
  Formatter.addType(new t.Type(t.OBJECT, t.TYPED_ARRAY, 'uint16'), generateFormatForNumberArray('length', 'Uint16Array', 4));
  
  Formatter.addType(new t.Type(t.OBJECT, t.TYPED_ARRAY, 'int32'), generateFormatForNumberArray('length', 'Int32Array', 8));
  Formatter.addType(new t.Type(t.OBJECT, t.TYPED_ARRAY, 'uint32'), generateFormatForNumberArray('length', 'Uint32Array', 8));
  
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'bool16x8'), genSimdVectorFormat('Bool16x8', 8));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'bool32x4'), genSimdVectorFormat('Bool32x4', 4));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'bool8x16'), genSimdVectorFormat('Bool8x16', 16));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'float32x4'), genSimdVectorFormat('Float32x4', 4));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'int16x8'), genSimdVectorFormat('Int16x8', 8));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'int32x4'), genSimdVectorFormat('Int32x4', 4));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'int8x16'), genSimdVectorFormat('Int8x16', 16));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'uint16x8'), genSimdVectorFormat('Uint16x8', 8));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'uint32x4'), genSimdVectorFormat('Uint32x4', 4));
  Formatter.addType(new t.Type(t.OBJECT, t.SIMD, 'uint8x16'), genSimdVectorFormat('Uint8x16', 16));
  
  
  Formatter.addType(new t.Type(t.OBJECT, t.PROMISE), function() {
    return '[Promise]';//TODO it could be nice to inspect its state and value
  });
  
  Formatter.addType(new t.Type(t.OBJECT, t.XHR), function() {
    return '[XMLHttpRequest]';//TODO it could be nice to inspect its state
  });
  
  Formatter.addType(new t.Type(t.OBJECT, t.HTML_ELEMENT), function(value) {
    return value.outerHTML;
  });
  
  Formatter.addType(new t.Type(t.OBJECT, t.HTML_ELEMENT, '#text'), function(value) {
    return value.nodeValue;
  });
  
  Formatter.addType(new t.Type(t.OBJECT, t.HTML_ELEMENT, '#document'), function(value) {
    return value.documentElement.outerHTML;
  });
  
  Formatter.addType(new t.Type(t.OBJECT, t.HOST), function() {
    return '[Host]';
  });
  
  var shouldFormat = defaultFormat;
  
  function _interopDefault$1 (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
  
  var getType = _interopDefault$1(shouldType);
  var eql = _interopDefault$1(eq);
  var sformat = _interopDefault$1(shouldFormat);
  
  
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  function isWrapperType(obj) {
    return obj instanceof Number || obj instanceof String || obj instanceof Boolean;
  }
  
  // XXX make it more strict: numbers, strings, symbols - and nothing else
  function convertPropertyName(name) {
    return typeof name === "symbol" ? name : String(name);
  }
  
  var functionName$1 = sformat.functionName;
  
  function isPlainObject(obj) {
    if (typeof obj == "object" && obj !== null) {
      var proto = Object.getPrototypeOf(obj);
      return proto === Object.prototype || proto === null;
    }
  
    return false;
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  var config = {
    typeAdaptors: shouldTypeAdaptors.defaultTypeAdaptorStorage,
  
    getFormatter: function(opts) {
      return new sformat.Formatter(opts || config);
    }
  };
  
  function format$1(value, opts) {
    return config.getFormatter(opts).format(value);
  }
  
  function formatProp(value) {
    var formatter = config.getFormatter();
    return sformat.formatPlainObjectKey.call(formatter, value);
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  /**
   * should AssertionError
   * @param {Object} options
   * @constructor
   * @memberOf should
   * @static
   */
  function AssertionError(options) {
    shouldUtil.merge(this, options);
  
    if (!options.message) {
      Object.defineProperty(this, "message", {
        get: function() {
          if (!this._message) {
            this._message = this.generateMessage();
            this.generatedMessage = true;
          }
          return this._message;
        },
        configurable: true,
        enumerable: false
      });
    }
  
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, this.stackStartFunction);
    } else {
      // non v8 browsers so we can have a stacktrace
      var err = new Error();
      if (err.stack) {
        var out = err.stack;
  
        if (this.stackStartFunction) {
          // try to strip useless frames
          var fn_name = functionName$1(this.stackStartFunction);
          var idx = out.indexOf("\n" + fn_name);
          if (idx >= 0) {
            // once we have located the function frame
            // we need to strip out everything before it (and its line)
            var next_line = out.indexOf("\n", idx + 1);
            out = out.substring(next_line + 1);
          }
        }
  
        this.stack = out;
      }
    }
  }
  
  var indent$1 = "    ";
  function prependIndent(line) {
    return indent$1 + line;
  }
  
  function indentLines(text) {
    return text
      .split("\n")
      .map(prependIndent)
      .join("\n");
  }
  
  // assert.AssertionError instanceof Error
  AssertionError.prototype = Object.create(Error.prototype, {
    name: {
      value: "AssertionError"
    },
  
    generateMessage: {
      value: function() {
        if (!this.operator && this.previous) {
          return this.previous.message;
        }
        var actual = format$1(this.actual);
        var expected = "expected" in this ? " " + format$1(this.expected) : "";
        var details =
          "details" in this && this.details ? " (" + this.details + ")" : "";
  
        var previous = this.previous
          ? "\n" + indentLines(this.previous.message)
          : "";
  
        return (
          "expected " +
          actual +
          (this.negate ? " not " : " ") +
          this.operator +
          expected +
          details +
          previous
        );
      }
    }
  });
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  // a bit hacky way how to get error to do not have stack
  function LightAssertionError(options) {
    shouldUtil.merge(this, options);
  
    if (!options.message) {
      Object.defineProperty(this, "message", {
        get: function() {
          if (!this._message) {
            this._message = this.generateMessage();
            this.generatedMessage = true;
          }
          return this._message;
        }
      });
    }
  }
  
  LightAssertionError.prototype = {
    generateMessage: AssertionError.prototype.generateMessage
  };
  
  /**
   * should Assertion
   * @param {*} obj Given object for assertion
   * @constructor
   * @memberOf should
   * @static
   */
  function Assertion(obj) {
    this.obj = obj;
  
    this.anyOne = false;
    this.negate = false;
  
    this.params = { actual: obj };
  }
  
  Assertion.prototype = {
    constructor: Assertion,
  
    /**
     * Base method for assertions.
     *
     * Before calling this method need to fill Assertion#params object. This method usually called from other assertion methods.
     * `Assertion#params` can contain such properties:
     * * `operator` - required string containing description of this assertion
     * * `obj` - optional replacement for this.obj, it is useful if you prepare more clear object then given
     * * `message` - if this property filled with string any others will be ignored and this one used as assertion message
     * * `expected` - any object used when you need to assert relation between given object and expected. Like given == expected (== is a relation)
     * * `details` - additional string with details to generated message
     *
     * @memberOf Assertion
     * @category assertion
     * @param {*} expr Any expression that will be used as a condition for asserting.
     * @example
     *
     * var a = new should.Assertion(42);
     *
     * a.params = {
     *  operator: 'to be magic number',
     * }
     *
     * a.assert(false);
     * //throws AssertionError: expected 42 to be magic number
     */
    assert: function(expr) {
      if (expr) {
        return this;
      }
  
      var params = this.params;
  
      if ("obj" in params && !("actual" in params)) {
        params.actual = params.obj;
      } else if (!("obj" in params) && !("actual" in params)) {
        params.actual = this.obj;
      }
  
      params.stackStartFunction = params.stackStartFunction || this.assert;
      params.negate = this.negate;
  
      params.assertion = this;
  
      if (this.light) {
        throw new LightAssertionError(params);
      } else {
        throw new AssertionError(params);
      }
    },
  
    /**
     * Shortcut for `Assertion#assert(false)`.
     *
     * @memberOf Assertion
     * @category assertion
     * @example
     *
     * var a = new should.Assertion(42);
     *
     * a.params = {
     *  operator: 'to be magic number',
     * }
     *
     * a.fail();
     * //throws AssertionError: expected 42 to be magic number
     */
    fail: function() {
      return this.assert(false);
    },
  
    assertZeroArguments: function(args) {
      if (args.length !== 0) {
        throw new TypeError("This assertion does not expect any arguments. You may need to check your code");
      }
    }
  };
  
  /**
   * Assertion used to delegate calls of Assertion methods inside of Promise.
   * It has almost all methods of Assertion.prototype
   *
   * @param {Promise} obj
   */
  function PromisedAssertion(/* obj */) {
    Assertion.apply(this, arguments);
  }
  
  /**
   * Make PromisedAssertion to look like promise. Delegate resolve and reject to given promise.
   *
   * @private
   * @returns {Promise}
   */
  PromisedAssertion.prototype.then = function(resolve, reject) {
    return this.obj.then(resolve, reject);
  };
  
  /**
   * Way to extend Assertion function. It uses some logic
   * to define only positive assertions and itself rule with negative assertion.
   *
   * All actions happen in subcontext and this method take care about negation.
   * Potentially we can add some more modifiers that does not depends from state of assertion.
   *
   * @memberOf Assertion
   * @static
   * @param {String} name Name of assertion. It will be used for defining method or getter on Assertion.prototype
   * @param {Function} func Function that will be called on executing assertion
   * @example
   *
   * Assertion.add('asset', function() {
   *      this.params = { operator: 'to be asset' }
   *
   *      this.obj.should.have.property('id').which.is.a.Number()
   *      this.obj.should.have.property('path')
   * })
   */
  Assertion.add = function(name, func) {
    Object.defineProperty(Assertion.prototype, name, {
      enumerable: true,
      configurable: true,
      value: function() {
        var context = new Assertion(this.obj, this, name);
        context.anyOne = this.anyOne;
        context.onlyThis = this.onlyThis;
        // hack
        context.light = true;
  
        try {
          func.apply(context, arguments);
        } catch (e) {
          // check for fail
          if (e instanceof AssertionError || e instanceof LightAssertionError) {
            // negative fail
            if (this.negate) {
              this.obj = context.obj;
              this.negate = false;
              return this;
            }
  
            if (context !== e.assertion) {
              context.params.previous = e;
            }
  
            // positive fail
            context.negate = false;
            // hack
            context.light = false;
            context.fail();
          }
          // throw if it is another exception
          throw e;
        }
  
        // negative pass
        if (this.negate) {
          context.negate = true; // because .fail will set negate
          context.params.details = "false negative fail";
          // hack
          context.light = false;
          context.fail();
        }
  
        // positive pass
        if (!this.params.operator) {
          this.params = context.params; // shortcut
        }
        this.obj = context.obj;
        this.negate = false;
        return this;
      }
    });
  
    Object.defineProperty(PromisedAssertion.prototype, name, {
      enumerable: true,
      configurable: true,
      value: function() {
        var args = arguments;
        this.obj = this.obj.then(function(a) {
          return a[name].apply(a, args);
        });
  
        return this;
      }
    });
  };
  
  /**
   * Add chaining getter to Assertion like .a, .which etc
   *
   * @memberOf Assertion
   * @static
   * @param  {string} name   name of getter
   * @param  {function} [onCall] optional function to call
   */
  Assertion.addChain = function(name, onCall) {
    onCall = onCall || function() {};
    Object.defineProperty(Assertion.prototype, name, {
      get: function() {
        onCall.call(this);
        return this;
      },
      enumerable: true
    });
  
    Object.defineProperty(PromisedAssertion.prototype, name, {
      enumerable: true,
      configurable: true,
      get: function() {
        this.obj = this.obj.then(function(a) {
          return a[name];
        });
  
        return this;
      }
    });
  };
  
  /**
   * Create alias for some `Assertion` property
   *
   * @memberOf Assertion
   * @static
   * @param {String} from Name of to map
   * @param {String} to Name of alias
   * @example
   *
   * Assertion.alias('true', 'True')
   */
  Assertion.alias = function(from, to) {
    var desc = Object.getOwnPropertyDescriptor(Assertion.prototype, from);
    if (!desc) {
      throw new Error("Alias " + from + " -> " + to + " could not be created as " + from + " not defined");
    }
    Object.defineProperty(Assertion.prototype, to, desc);
  
    var desc2 = Object.getOwnPropertyDescriptor(PromisedAssertion.prototype, from);
    if (desc2) {
      Object.defineProperty(PromisedAssertion.prototype, to, desc2);
    }
  };
  /**
   * Negation modifier. Current assertion chain become negated. Each call invert negation on current assertion.
   *
   * @name not
   * @property
   * @memberOf Assertion
   * @category assertion
   */
  Assertion.addChain("not", function() {
    this.negate = !this.negate;
  });
  
  /**
   * Any modifier - it affect on execution of sequenced assertion to do not `check all`, but `check any of`.
   *
   * @name any
   * @property
   * @memberOf Assertion
   * @category assertion
   */
  Assertion.addChain("any", function() {
    this.anyOne = true;
  });
  
  /**
   * Only modifier - currently used with .keys to check if object contains only exactly this .keys
   *
   * @name only
   * @property
   * @memberOf Assertion
   * @category assertion
   */
  Assertion.addChain("only", function() {
    this.onlyThis = true;
  });
  
  // implement assert interface using already written peaces of should.js
  
  // http://wiki.commonjs.org/wiki/Unit_Testing/1.0
  //
  // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
  //
  // Originally from narwhal.js (http://narwhaljs.org)
  // Copyright (c) 2009 Thomas Robinson <280north.com>
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the 'Software'), to
  // deal in the Software without restriction, including without limitation the
  // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  // sell copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  // when used in node, this will actually load the util module we depend on
  // versus loading the builtin util module as happens otherwise
  // this is a bug in node module loading as far as I am concerned
  var pSlice = Array.prototype.slice;
  
  // 1. The assert module provides functions that throw
  // AssertionError's when particular conditions are not met. The
  // assert module must conform to the following interface.
  
  var assert = ok;
  // 3. All of the following functions must throw an AssertionError
  // when a corresponding condition is not met, with a message that
  // may be undefined if not provided.  All assertion methods provide
  // both the actual and expected values to the assertion error for
  // display purposes.
  /**
   * Node.js standard [`assert.fail`](http://nodejs.org/api/assert.html#assert_assert_fail_actual_expected_message_operator).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual Actual object
   * @param {*} expected Expected object
   * @param {string} message Message for assertion
   * @param {string} operator Operator text
   */
  function fail(actual, expected, message, operator, stackStartFunction) {
    var a = new Assertion(actual);
    a.params = {
      operator: operator,
      expected: expected,
      message: message,
      stackStartFunction: stackStartFunction || fail
    };
  
    a.fail();
  }
  
  // EXTENSION! allows for well behaved errors defined elsewhere.
  assert.fail = fail;
  
  // 4. Pure assertion tests whether a value is truthy, as determined
  // by !!guard.
  // assert.ok(guard, message_opt);
  // This statement is equivalent to assert.equal(true, !!guard,
  // message_opt);. To test strictly for the value true, use
  // assert.strictEqual(true, guard, message_opt);.
  /**
   * Node.js standard [`assert.ok`](http://nodejs.org/api/assert.html#assert_assert_value_message_assert_ok_value_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} value
   * @param {string} [message]
   */
  function ok(value, message) {
    if (!value) {
      fail(value, true, message, "==", assert.ok);
    }
  }
  assert.ok = ok;
  
  // 5. The equality assertion tests shallow, coercive equality with
  // ==.
  // assert.equal(actual, expected, message_opt);
  
  /**
   * Node.js standard [`assert.equal`](http://nodejs.org/api/assert.html#assert_assert_equal_actual_expected_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.equal = function equal(actual, expected, message) {
    if (actual != expected) {
      fail(actual, expected, message, "==", assert.equal);
    }
  };
  
  // 6. The non-equality assertion tests for whether two objects are not equal
  // with != assert.notEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.notEqual`](http://nodejs.org/api/assert.html#assert_assert_notequal_actual_expected_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.notEqual = function notEqual(actual, expected, message) {
    if (actual == expected) {
      fail(actual, expected, message, "!=", assert.notEqual);
    }
  };
  
  // 7. The equivalence assertion tests a deep equality relation.
  // assert.deepEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.deepEqual`](http://nodejs.org/api/assert.html#assert_assert_deepequal_actual_expected_message).
   * But uses should.js .eql implementation instead of Node.js own deepEqual.
   *
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.deepEqual = function deepEqual(actual, expected, message) {
    if (eql(actual, expected).length !== 0) {
      fail(actual, expected, message, "deepEqual", assert.deepEqual);
    }
  };
  
  // 8. The non-equivalence assertion tests for any deep inequality.
  // assert.notDeepEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.notDeepEqual`](http://nodejs.org/api/assert.html#assert_assert_notdeepequal_actual_expected_message).
   * But uses should.js .eql implementation instead of Node.js own deepEqual.
   *
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.notDeepEqual = function notDeepEqual(actual, expected, message) {
    if (eql(actual, expected).result) {
      fail(actual, expected, message, "notDeepEqual", assert.notDeepEqual);
    }
  };
  
  // 9. The strict equality assertion tests strict equality, as determined by ===.
  // assert.strictEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.strictEqual`](http://nodejs.org/api/assert.html#assert_assert_strictequal_actual_expected_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.strictEqual = function strictEqual(actual, expected, message) {
    if (actual !== expected) {
      fail(actual, expected, message, "===", assert.strictEqual);
    }
  };
  
  // 10. The strict non-equality assertion tests for strict inequality, as
  // determined by !==.  assert.notStrictEqual(actual, expected, message_opt);
  /**
   * Node.js standard [`assert.notStrictEqual`](http://nodejs.org/api/assert.html#assert_assert_notstrictequal_actual_expected_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {*} actual
   * @param {*} expected
   * @param {string} [message]
   */
  assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
    if (actual === expected) {
      fail(actual, expected, message, "!==", assert.notStrictEqual);
    }
  };
  
  function expectedException(actual, expected) {
    if (!actual || !expected) {
      return false;
    }
  
    if (Object.prototype.toString.call(expected) == "[object RegExp]") {
      return expected.test(actual);
    } else if (actual instanceof expected) {
      return true;
    } else if (expected.call({}, actual) === true) {
      return true;
    }
  
    return false;
  }
  
  function _throws(shouldThrow, block, expected, message) {
    var actual;
  
    if (typeof expected == "string") {
      message = expected;
      expected = null;
    }
  
    try {
      block();
    } catch (e) {
      actual = e;
    }
  
    message =
      (expected && expected.name ? " (" + expected.name + ")" : ".") +
      (message ? " " + message : ".");
  
    if (shouldThrow && !actual) {
      fail(actual, expected, "Missing expected exception" + message);
    }
  
    if (!shouldThrow && expectedException(actual, expected)) {
      fail(actual, expected, "Got unwanted exception" + message);
    }
  
    if (
      (shouldThrow &&
        actual &&
        expected &&
        !expectedException(actual, expected)) ||
      (!shouldThrow && actual)
    ) {
      throw actual;
    }
  }
  
  // 11. Expected to throw an error:
  // assert.throws(block, Error_opt, message_opt);
  /**
   * Node.js standard [`assert.throws`](http://nodejs.org/api/assert.html#assert_assert_throws_block_error_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {Function} block
   * @param {Function} [error]
   * @param {String} [message]
   */
  assert.throws = function(/*block, error, message*/) {
    _throws.apply(this, [true].concat(pSlice.call(arguments)));
  };
  
  // EXTENSION! This is annoying to write outside this module.
  /**
   * Node.js standard [`assert.doesNotThrow`](http://nodejs.org/api/assert.html#assert_assert_doesnotthrow_block_message).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {Function} block
   * @param {String} [message]
   */
  assert.doesNotThrow = function(/*block, message*/) {
    _throws.apply(this, [false].concat(pSlice.call(arguments)));
  };
  
  /**
   * Node.js standard [`assert.ifError`](http://nodejs.org/api/assert.html#assert_assert_iferror_value).
   * @static
   * @memberOf should
   * @category assertion assert
   * @param {Error} err
   */
  assert.ifError = function(err) {
    if (err) {
      throw err;
    }
  };
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function assertExtensions(should) {
    var i = should.format;
  
    /*
     * Expose assert to should
     *
     * This allows you to do things like below
     * without require()ing the assert module.
     *
     *    should.equal(foo.bar, undefined);
     *
     */
    shouldUtil.merge(should, assert);
  
    /**
     * Assert _obj_ exists, with optional message.
     *
     * @static
     * @memberOf should
     * @category assertion assert
     * @alias should.exists
     * @param {*} obj
     * @param {String} [msg]
     * @example
     *
     * should.exist(1);
     * should.exist(new Date());
     */
    should.exist = should.exists = function(obj, msg) {
      if (null == obj) {
        throw new AssertionError({
          message: msg || "expected " + i(obj) + " to exist",
          stackStartFunction: should.exist
        });
      }
    };
  
    should.not = {};
    /**
     * Asserts _obj_ does not exist, with optional message.
     *
     * @name not.exist
     * @static
     * @memberOf should
     * @category assertion assert
     * @alias should.not.exists
     * @param {*} obj
     * @param {String} [msg]
     * @example
     *
     * should.not.exist(null);
     * should.not.exist(void 0);
     */
    should.not.exist = should.not.exists = function(obj, msg) {
      if (null != obj) {
        throw new AssertionError({
          message: msg || "expected " + i(obj) + " to not exist",
          stackStartFunction: should.not.exist
        });
      }
    };
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function chainAssertions(should, Assertion) {
    /**
     * Simple chaining to improve readability. Does nothing.
     *
     * @memberOf Assertion
     * @name be
     * @property {should.Assertion} be
     * @alias Assertion#an
     * @alias Assertion#of
     * @alias Assertion#a
     * @alias Assertion#and
     * @alias Assertion#been
     * @alias Assertion#have
     * @alias Assertion#has
     * @alias Assertion#with
     * @alias Assertion#is
     * @alias Assertion#which
     * @alias Assertion#the
     * @alias Assertion#it
     * @category assertion chaining
     */
    [
      "an",
      "of",
      "a",
      "and",
      "be",
      "been",
      "has",
      "have",
      "with",
      "is",
      "which",
      "the",
      "it"
    ].forEach(function(name) {
      Assertion.addChain(name);
    });
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function booleanAssertions(should, Assertion) {
    /**
     * Assert given object is exactly `true`.
     *
     * @name true
     * @memberOf Assertion
     * @category assertion bool
     * @alias Assertion#True
     * @param {string} [message] Optional message
     * @example
     *
     * (true).should.be.true();
     * false.should.not.be.true();
     *
     * ({ a: 10}).should.not.be.true();
     */
    Assertion.add("true", function(message) {
      this.is.exactly(true, message);
    });
  
    Assertion.alias("true", "True");
  
    /**
     * Assert given object is exactly `false`.
     *
     * @name false
     * @memberOf Assertion
     * @category assertion bool
     * @alias Assertion#False
     * @param {string} [message] Optional message
     * @example
     *
     * (true).should.not.be.false();
     * false.should.be.false();
     */
    Assertion.add("false", function(message) {
      this.is.exactly(false, message);
    });
  
    Assertion.alias("false", "False");
  
    /**
     * Assert given object is truthy according javascript type conversions.
     *
     * @name ok
     * @memberOf Assertion
     * @category assertion bool
     * @example
     *
     * (true).should.be.ok();
     * ''.should.not.be.ok();
     * should(null).not.be.ok();
     * should(void 0).not.be.ok();
     *
     * (10).should.be.ok();
     * (0).should.not.be.ok();
     */
    Assertion.add("ok", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be truthy" };
  
      this.assert(this.obj);
    });
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function numberAssertions(should, Assertion) {
    /**
     * Assert given object is NaN
     * @name NaN
     * @memberOf Assertion
     * @category assertion numbers
     * @example
     *
     * (10).should.not.be.NaN();
     * NaN.should.be.NaN();
     */
    Assertion.add("NaN", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be NaN" };
  
      this.assert(this.obj !== this.obj);
    });
  
    /**
     * Assert given object is not finite (positive or negative)
     *
     * @name Infinity
     * @memberOf Assertion
     * @category assertion numbers
     * @example
     *
     * (10).should.not.be.Infinity();
     * NaN.should.not.be.Infinity();
     */
    Assertion.add("Infinity", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be Infinity" };
  
      this.is.a
        .Number()
        .and.not.a.NaN()
        .and.assert(!isFinite(this.obj));
    });
  
    /**
     * Assert given number between `start` and `finish` or equal one of them.
     *
     * @name within
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} start Start number
     * @param {number} finish Finish number
     * @param {string} [description] Optional message
     * @example
     *
     * (10).should.be.within(0, 20);
     */
    Assertion.add("within", function(start, finish, description) {
      this.params = {
        operator: "to be within " + start + ".." + finish,
        message: description
      };
  
      this.assert(this.obj >= start && this.obj <= finish);
    });
  
    /**
     * Assert given number near some other `value` within `delta`
     *
     * @name approximately
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} value Center number
     * @param {number} delta Radius
     * @param {string} [description] Optional message
     * @example
     *
     * (9.99).should.be.approximately(10, 0.1);
     */
    Assertion.add("approximately", function(value, delta, description) {
      this.params = {
        operator: "to be approximately " + value + " ±" + delta,
        message: description
      };
  
      this.assert(Math.abs(this.obj - value) <= delta);
    });
  
    /**
     * Assert given number above `n`.
     *
     * @name above
     * @alias Assertion#greaterThan
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} n Margin number
     * @param {string} [description] Optional message
     * @example
     *
     * (10).should.be.above(0);
     */
    Assertion.add("above", function(n, description) {
      this.params = { operator: "to be above " + n, message: description };
  
      this.assert(this.obj > n);
    });
  
    /**
     * Assert given number below `n`.
     *
     * @name below
     * @alias Assertion#lessThan
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} n Margin number
     * @param {string} [description] Optional message
     * @example
     *
     * (0).should.be.below(10);
     */
    Assertion.add("below", function(n, description) {
      this.params = { operator: "to be below " + n, message: description };
  
      this.assert(this.obj < n);
    });
  
    Assertion.alias("above", "greaterThan");
    Assertion.alias("below", "lessThan");
  
    /**
     * Assert given number above `n`.
     *
     * @name aboveOrEqual
     * @alias Assertion#greaterThanOrEqual
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} n Margin number
     * @param {string} [description] Optional message
     * @example
     *
     * (10).should.be.aboveOrEqual(0);
     * (10).should.be.aboveOrEqual(10);
     */
    Assertion.add("aboveOrEqual", function(n, description) {
      this.params = {
        operator: "to be above or equal " + n,
        message: description
      };
  
      this.assert(this.obj >= n);
    });
  
    /**
     * Assert given number below `n`.
     *
     * @name belowOrEqual
     * @alias Assertion#lessThanOrEqual
     * @memberOf Assertion
     * @category assertion numbers
     * @param {number} n Margin number
     * @param {string} [description] Optional message
     * @example
     *
     * (0).should.be.belowOrEqual(10);
     * (0).should.be.belowOrEqual(0);
     */
    Assertion.add("belowOrEqual", function(n, description) {
      this.params = {
        operator: "to be below or equal " + n,
        message: description
      };
  
      this.assert(this.obj <= n);
    });
  
    Assertion.alias("aboveOrEqual", "greaterThanOrEqual");
    Assertion.alias("belowOrEqual", "lessThanOrEqual");
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function typeAssertions(should, Assertion) {
    /**
     * Assert given object is number
     * @name Number
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("Number", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be a number" };
  
      this.have.type("number");
    });
  
    /**
     * Assert given object is arguments
     * @name arguments
     * @alias Assertion#Arguments
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("arguments", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be arguments" };
  
      this.have.class("Arguments");
    });
  
    Assertion.alias("arguments", "Arguments");
  
    /**
     * Assert given object has some type using `typeof`
     * @name type
     * @memberOf Assertion
     * @param {string} type Type name
     * @param {string} [description] Optional message
     * @category assertion types
     */
    Assertion.add("type", function(type, description) {
      this.params = { operator: "to have type " + type, message: description };
  
      should(typeof this.obj).be.exactly(type);
    });
  
    /**
     * Assert given object is instance of `constructor`
     * @name instanceof
     * @alias Assertion#instanceOf
     * @memberOf Assertion
     * @param {Function} constructor Constructor function
     * @param {string} [description] Optional message
     * @category assertion types
     */
    Assertion.add("instanceof", function(constructor, description) {
      this.params = {
        operator: "to be an instance of " + functionName$1(constructor),
        message: description
      };
  
      this.assert(Object(this.obj) instanceof constructor);
    });
  
    Assertion.alias("instanceof", "instanceOf");
  
    /**
     * Assert given object is function
     * @name Function
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("Function", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be a function" };
  
      this.have.type("function");
    });
  
    /**
     * Assert given object is object
     * @name Object
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("Object", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be an object" };
  
      this.is.not.null().and.have.type("object");
    });
  
    /**
     * Assert given object is string
     * @name String
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("String", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be a string" };
  
      this.have.type("string");
    });
  
    /**
     * Assert given object is array
     * @name Array
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("Array", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be an array" };
  
      this.have.class("Array");
    });
  
    /**
     * Assert given object is boolean
     * @name Boolean
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("Boolean", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be a boolean" };
  
      this.have.type("boolean");
    });
  
    /**
     * Assert given object is error
     * @name Error
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("Error", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be an error" };
  
      this.have.instanceOf(Error);
    });
  
    /**
     * Assert given object is a date
     * @name Date
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("Date", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be a date" };
  
      this.have.instanceOf(Date);
    });
  
    /**
     * Assert given object is null
     * @name null
     * @alias Assertion#Null
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("null", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be null" };
  
      this.assert(this.obj === null);
    });
  
    Assertion.alias("null", "Null");
  
    /**
     * Assert given object has some internal [[Class]], via Object.prototype.toString call
     * @name class
     * @alias Assertion#Class
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("class", function(cls) {
      this.params = { operator: "to have [[Class]] " + cls };
  
      this.assert(Object.prototype.toString.call(this.obj) === "[object " + cls + "]");
    });
  
    Assertion.alias("class", "Class");
  
    /**
     * Assert given object is undefined
     * @name undefined
     * @alias Assertion#Undefined
     * @memberOf Assertion
     * @category assertion types
     */
    Assertion.add("undefined", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be undefined" };
  
      this.assert(this.obj === void 0);
    });
  
    Assertion.alias("undefined", "Undefined");
  
    /**
     * Assert given object supports es6 iterable protocol (just check
     * that object has property Symbol.iterator, which is a function)
     * @name iterable
     * @memberOf Assertion
     * @category assertion es6
     */
    Assertion.add("iterable", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be iterable" };
  
      should(this.obj)
        .have.property(Symbol.iterator)
        .which.is.a.Function();
    });
  
    /**
     * Assert given object supports es6 iterator protocol (just check
     * that object has property next, which is a function)
     * @name iterator
     * @memberOf Assertion
     * @category assertion es6
     */
    Assertion.add("iterator", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be iterator" };
  
      should(this.obj)
        .have.property("next")
        .which.is.a.Function();
    });
  
    /**
     * Assert given object is a generator object
     * @name generator
     * @memberOf Assertion
     * @category assertion es6
     */
    Assertion.add("generator", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be generator" };
  
      should(this.obj).be.iterable.and.iterator.and.it.is.equal(this.obj[Symbol.iterator]());
    });
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function formatEqlResult(r, a, b) {
    return ((r.path.length > 0
      ? "at " + r.path.map(formatProp).join(" -> ")
      : "") +
      (r.a === a ? "" : ", A has " + format$1(r.a)) +
      (r.b === b ? "" : " and B has " + format$1(r.b)) +
      (r.showReason ? " because " + r.reason : "")).trim();
  }
  
  function equalityAssertions(should, Assertion) {
    /**
     * Deep object equality comparison. For full spec see [`should-equal tests`](https://github.com/shouldjs/equal/blob/master/test.js).
     *
     * @name eql
     * @memberOf Assertion
     * @category assertion equality
     * @alias Assertion#eqls
     * @alias Assertion#deepEqual
     * @param {*} val Expected value
     * @param {string} [description] Optional message
     * @example
     *
     * (10).should.be.eql(10);
     * ('10').should.not.be.eql(10);
     * (-0).should.not.be.eql(+0);
     *
     * NaN.should.be.eql(NaN);
     *
     * ({ a: 10}).should.be.eql({ a: 10 });
     * [ 'a' ].should.not.be.eql({ '0': 'a' });
     */
    Assertion.add("eql", function(val, description) {
      this.params = { operator: "to equal", expected: val, message: description };
      var obj = this.obj;
      var fails = eql(this.obj, val, should.config);
      this.params.details = fails
        .map(function(fail) {
          return formatEqlResult(fail, obj, val);
        })
        .join(", ");
  
      this.params.showDiff = eql(getType(obj), getType(val)).length === 0;
  
      this.assert(fails.length === 0);
    });
  
    /**
     * Exact comparison using ===.
     *
     * @name equal
     * @memberOf Assertion
     * @category assertion equality
     * @alias Assertion#equals
     * @alias Assertion#exactly
     * @param {*} val Expected value
     * @param {string} [description] Optional message
     * @example
     *
     * 10.should.be.equal(10);
     * 'a'.should.be.exactly('a');
     *
     * should(null).be.exactly(null);
     */
    Assertion.add("equal", function(val, description) {
      this.params = { operator: "to be", expected: val, message: description };
  
      this.params.showDiff = eql(getType(this.obj), getType(val)).length === 0;
  
      this.assert(val === this.obj);
    });
  
    Assertion.alias("equal", "equals");
    Assertion.alias("equal", "exactly");
    Assertion.alias("eql", "eqls");
    Assertion.alias("eql", "deepEqual");
  
    function addOneOf(name, message, method) {
      Assertion.add(name, function(vals) {
        if (arguments.length !== 1) {
          vals = Array.prototype.slice.call(arguments);
        } else {
          should(vals).be.Array();
        }
  
        this.params = { operator: message, expected: vals };
  
        var obj = this.obj;
        var found = false;
  
        shouldTypeAdaptors.forEach(vals, function(val) {
          try {
            should(val)[method](obj);
            found = true;
            return false;
          } catch (e) {
            if (e instanceof should.AssertionError) {
              return; //do nothing
            }
            throw e;
          }
        });
  
        this.assert(found);
      });
    }
  
    /**
     * Exact comparison using === to be one of supplied objects.
     *
     * @name equalOneOf
     * @memberOf Assertion
     * @category assertion equality
     * @param {Array|*} vals Expected values
     * @example
     *
     * 'ab'.should.be.equalOneOf('a', 10, 'ab');
     * 'ab'.should.be.equalOneOf(['a', 10, 'ab']);
     */
    addOneOf("equalOneOf", "to be equals one of", "equal");
  
    /**
     * Exact comparison using .eql to be one of supplied objects.
     *
     * @name oneOf
     * @memberOf Assertion
     * @category assertion equality
     * @param {Array|*} vals Expected values
     * @example
     *
     * ({a: 10}).should.be.oneOf('a', 10, 'ab', {a: 10});
     * ({a: 10}).should.be.oneOf(['a', 10, 'ab', {a: 10}]);
     */
    addOneOf("oneOf", "to be one of", "eql");
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function promiseAssertions(should, Assertion$$1) {
    /**
     * Assert given object is a Promise
     *
     * @name Promise
     * @memberOf Assertion
     * @category assertion promises
     * @example
     *
     * promise.should.be.Promise()
     * (new Promise(function(resolve, reject) { resolve(10); })).should.be.a.Promise()
     * (10).should.not.be.a.Promise()
     */
    Assertion$$1.add("Promise", function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be promise" };
  
      var obj = this.obj;
  
      should(obj)
        .have.property("then")
        .which.is.a.Function();
    });
  
    /**
     * Assert given promise will be fulfilled. Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name fulfilled
     * @memberOf Assertion
     * @alias Assertion#resolved
     * @returns {Promise}
     * @category assertion promises
     * @example
     *
     * // don't forget to handle async nature
     * (new Promise(function(resolve, reject) { resolve(10); })).should.be.fulfilled();
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return new Promise(resolve => resolve(10))
     *      .should.be.fulfilled();
     * });
     */
    Assertion$$1.prototype.fulfilled = function Assertion$fulfilled() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be fulfilled" };
  
      should(this.obj).be.a.Promise();
  
      var that = this;
      return this.obj.then(
        function next$onResolve(value) {
          if (that.negate) {
            that.fail();
          }
          return value;
        },
        function next$onReject(err) {
          if (!that.negate) {
            that.params.operator += ", but it was rejected with " + should.format(err);
            that.fail();
          }
          return err;
        }
      );
    };
  
    Assertion$$1.prototype.resolved = Assertion$$1.prototype.fulfilled;
  
    /**
     * Assert given promise will be rejected. Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name rejected
     * @memberOf Assertion
     * @category assertion promises
     * @returns {Promise}
     * @example
     *
     * // don't forget to handle async nature
     * (new Promise(function(resolve, reject) { resolve(10); }))
     *    .should.not.be.rejected();
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return new Promise((resolve, reject) => reject(new Error('boom')))
     *      .should.be.rejected();
     * });
     */
    Assertion$$1.prototype.rejected = function() {
      this.assertZeroArguments(arguments);
      this.params = { operator: "to be rejected" };
  
      should(this.obj).be.a.Promise();
  
      var that = this;
      return this.obj.then(
        function(value) {
          if (!that.negate) {
            that.params.operator += ", but it was fulfilled";
            if (arguments.length != 0) {
              that.params.operator += " with " + should.format(value);
            }
            that.fail();
          }
          return value;
        },
        function next$onError(err) {
          if (that.negate) {
            that.fail();
          }
          return err;
        }
      );
    };
  
    /**
     * Assert given promise will be fulfilled with some expected value (value compared using .eql).
     * Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name fulfilledWith
     * @memberOf Assertion
     * @alias Assertion#resolvedWith
     * @category assertion promises
     * @returns {Promise}
     * @example
     *
     * // don't forget to handle async nature
     * (new Promise(function(resolve, reject) { resolve(10); }))
     *    .should.be.fulfilledWith(10);
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return new Promise((resolve, reject) => resolve(10))
     *       .should.be.fulfilledWith(10);
     * });
     */
    Assertion$$1.prototype.fulfilledWith = function(expectedValue) {
      this.params = {
        operator: "to be fulfilled with " + should.format(expectedValue)
      };
  
      should(this.obj).be.a.Promise();
  
      var that = this;
      return this.obj.then(
        function(value) {
          if (that.negate) {
            that.fail();
          }
          should(value).eql(expectedValue);
          return value;
        },
        function next$onError(err) {
          if (!that.negate) {
            that.params.operator += ", but it was rejected with " + should.format(err);
            that.fail();
          }
          return err;
        }
      );
    };
  
    Assertion$$1.prototype.resolvedWith = Assertion$$1.prototype.fulfilledWith;
  
    /**
     * Assert given promise will be rejected with some sort of error. Arguments is the same for Assertion#throw.
     * Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name rejectedWith
     * @memberOf Assertion
     * @category assertion promises
     * @returns {Promise}
     * @example
     *
     * function failedPromise() {
     *   return new Promise(function(resolve, reject) {
     *     reject(new Error('boom'))
     *   })
     * }
     * failedPromise().should.be.rejectedWith(Error);
     * failedPromise().should.be.rejectedWith('boom');
     * failedPromise().should.be.rejectedWith(/boom/);
     * failedPromise().should.be.rejectedWith(Error, { message: 'boom' });
     * failedPromise().should.be.rejectedWith({ message: 'boom' });
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return failedPromise().should.be.rejectedWith({ message: 'boom' });
     * });
     */
    Assertion$$1.prototype.rejectedWith = function(message, properties) {
      this.params = { operator: "to be rejected" };
  
      should(this.obj).be.a.Promise();
  
      var that = this;
      return this.obj.then(
        function(value) {
          if (!that.negate) {
            that.fail();
          }
          return value;
        },
        function next$onError(err) {
          if (that.negate) {
            that.fail();
          }
  
          var errorMatched = true;
          var errorInfo = "";
  
          if ("string" === typeof message) {
            errorMatched = message === err.message;
          } else if (message instanceof RegExp) {
            errorMatched = message.test(err.message);
          } else if ("function" === typeof message) {
            errorMatched = err instanceof message;
          } else if (message !== null && typeof message === "object") {
            try {
              should(err).match(message);
            } catch (e) {
              if (e instanceof should.AssertionError) {
                errorInfo = ": " + e.message;
                errorMatched = false;
              } else {
                throw e;
              }
            }
          }
  
          if (!errorMatched) {
            if (typeof message === "string" || message instanceof RegExp) {
              errorInfo = " with a message matching " + should.format(message) + ", but got '" + err.message + "'";
            } else if ("function" === typeof message) {
              errorInfo = " of type " + functionName$1(message) + ", but got " + functionName$1(err.constructor);
            }
          } else if ("function" === typeof message && properties) {
            try {
              should(err).match(properties);
            } catch (e) {
              if (e instanceof should.AssertionError) {
                errorInfo = ": " + e.message;
                errorMatched = false;
              } else {
                throw e;
              }
            }
          }
  
          that.params.operator += errorInfo;
  
          that.assert(errorMatched);
  
          return err;
        }
      );
    };
  
    /**
     * Assert given object is promise and wrap it in PromisedAssertion, which has all properties of Assertion.
     * That means you can chain as with usual Assertion.
     * Result of assertion is still .thenable and should be handled accordingly.
     *
     * @name finally
     * @memberOf Assertion
     * @alias Assertion#eventually
     * @category assertion promises
     * @returns {PromisedAssertion} Like Assertion, but .then this.obj in Assertion
     * @example
     *
     * (new Promise(function(resolve, reject) { resolve(10); }))
     *    .should.be.eventually.equal(10);
     *
     * // test example with mocha it is possible to return promise
     * it('is async', () => {
     *    return new Promise(resolve => resolve(10))
     *      .should.be.finally.equal(10);
     * });
     */
    Object.defineProperty(Assertion$$1.prototype, "finally", {
      get: function() {
        should(this.obj).be.a.Promise();
  
        var that = this;
  
        return new PromisedAssertion(
          this.obj.then(function(obj) {
            var a = should(obj);
  
            a.negate = that.negate;
            a.anyOne = that.anyOne;
  
            return a;
          })
        );
      }
    });
  
    Assertion$$1.alias("finally", "eventually");
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function stringAssertions(should, Assertion) {
    /**
     * Assert given string starts with prefix
     * @name startWith
     * @memberOf Assertion
     * @category assertion strings
     * @param {string} str Prefix
     * @param {string} [description] Optional message
     * @example
     *
     * 'abc'.should.startWith('a');
     */
    Assertion.add("startWith", function(str, description) {
      this.params = {
        operator: "to start with " + should.format(str),
        message: description
      };
  
      this.assert(0 === this.obj.indexOf(str));
    });
  
    /**
     * Assert given string ends with prefix
     * @name endWith
     * @memberOf Assertion
     * @category assertion strings
     * @param {string} str Prefix
     * @param {string} [description] Optional message
     * @example
     *
     * 'abca'.should.endWith('a');
     */
    Assertion.add("endWith", function(str, description) {
      this.params = {
        operator: "to end with " + should.format(str),
        message: description
      };
  
      this.assert(this.obj.indexOf(str, this.obj.length - str.length) >= 0);
    });
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function containAssertions(should, Assertion) {
    var i = should.format;
  
    /**
     * Assert that given object contain something that equal to `other`. It uses `should-equal` for equality checks.
     * If given object is array it search that one of elements was equal to `other`.
     * If given object is string it checks if `other` is a substring - expected that `other` is a string.
     * If given object is Object it checks that `other` is a subobject - expected that `other` is a object.
     *
     * @name containEql
     * @memberOf Assertion
     * @category assertion contain
     * @param {*} other Nested object
     * @example
     *
     * [1, 2, 3].should.containEql(1);
     * [{ a: 1 }, 'a', 10].should.containEql({ a: 1 });
     *
     * 'abc'.should.containEql('b');
     * 'ab1c'.should.containEql(1);
     *
     * ({ a: 10, c: { d: 10 }}).should.containEql({ a: 10 });
     * ({ a: 10, c: { d: 10 }}).should.containEql({ c: { d: 10 }});
     * ({ a: 10, c: { d: 10 }}).should.containEql({ b: 10 });
     * // throws AssertionError: expected { a: 10, c: { d: 10 } } to contain { b: 10 }
     * //            expected { a: 10, c: { d: 10 } } to have property b
     */
    Assertion.add("containEql", function(other) {
      this.params = { operator: "to contain " + i(other) };
  
      this.is.not.null().and.not.undefined();
  
      var obj = this.obj;
  
      if (typeof obj == "string") {
        this.assert(obj.indexOf(String(other)) >= 0);
      } else if (shouldTypeAdaptors.isIterable(obj)) {
        this.assert(
          shouldTypeAdaptors.some(obj, function(v) {
            return eql(v, other).length === 0;
          })
        );
      } else {
        shouldTypeAdaptors.forEach(
          other,
          function(value, key) {
            should(obj).have.value(key, value);
          },
          this
        );
      }
    });
  
    /**
     * Assert that given object is contain equally structured object on the same depth level.
     * If given object is an array and `other` is an array it checks that the eql elements is going in the same sequence in given array (recursive)
     * If given object is an object it checks that the same keys contain deep equal values (recursive)
     * On other cases it try to check with `.eql`
     *
     * @name containDeepOrdered
     * @memberOf Assertion
     * @category assertion contain
     * @param {*} other Nested object
     * @example
     *
     * [ 1, 2, 3].should.containDeepOrdered([1, 2]);
     * [ 1, 2, [ 1, 2, 3 ]].should.containDeepOrdered([ 1, [ 2, 3 ]]);
     *
     * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({a: 10});
     * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({b: {c: 10}});
     * ({ a: 10, b: { c: 10, d: [1, 2, 3] }}).should.containDeepOrdered({b: {d: [1, 3]}});
     */
    Assertion.add("containDeepOrdered", function(other) {
      this.params = { operator: "to contain " + i(other) };
  
      var obj = this.obj;
      if (typeof obj == "string") {
        // expect other to be string
        this.is.equal(String(other));
      } else if (shouldTypeAdaptors.isIterable(obj) && shouldTypeAdaptors.isIterable(other)) {
        var objIterator = shouldTypeAdaptors.iterator(obj);
        var otherIterator = shouldTypeAdaptors.iterator(other);
  
        var nextObj = objIterator.next();
        var nextOther = otherIterator.next();
        while (!nextObj.done && !nextOther.done) {
          try {
            should(nextObj.value[1]).containDeepOrdered(nextOther.value[1]);
            nextOther = otherIterator.next();
          } catch (e) {
            if (!(e instanceof should.AssertionError)) {
              throw e;
            }
          }
          nextObj = objIterator.next();
        }
  
        this.assert(nextOther.done);
      } else if (obj != null && typeof obj == "object" && other != null && typeof other == "object") {
        //TODO compare types object contains object case
        shouldTypeAdaptors.forEach(other, function(value, key) {
          should(obj[key]).containDeepOrdered(value);
        });
  
        // if both objects is empty means we finish traversing - and we need to compare for hidden values
        if (shouldTypeAdaptors.isEmpty(other)) {
          this.eql(other);
        }
      } else {
        this.eql(other);
      }
    });
  
    /**
     * The same like `Assertion#containDeepOrdered` but all checks on arrays without order.
     *
     * @name containDeep
     * @memberOf Assertion
     * @category assertion contain
     * @param {*} other Nested object
     * @example
     *
     * [ 1, 2, 3].should.containDeep([2, 1]);
     * [ 1, 2, [ 1, 2, 3 ]].should.containDeep([ 1, [ 3, 1 ]]);
     */
    Assertion.add("containDeep", function(other) {
      this.params = { operator: "to contain " + i(other) };
  
      var obj = this.obj;
      if (typeof obj === "string" && typeof other === "string") {
        // expect other to be string
        this.is.equal(String(other));
      } else if (shouldTypeAdaptors.isIterable(obj) && shouldTypeAdaptors.isIterable(other)) {
        var usedKeys = {};
        shouldTypeAdaptors.forEach(
          other,
          function(otherItem) {
            this.assert(
              shouldTypeAdaptors.some(obj, function(item, index) {
                if (index in usedKeys) {
                  return false;
                }
  
                try {
                  should(item).containDeep(otherItem);
                  usedKeys[index] = true;
                  return true;
                } catch (e) {
                  if (e instanceof should.AssertionError) {
                    return false;
                  }
                  throw e;
                }
              })
            );
          },
          this
        );
      } else if (obj != null && other != null && typeof obj == "object" && typeof other == "object") {
        // object contains object case
        shouldTypeAdaptors.forEach(other, function(value, key) {
          should(obj[key]).containDeep(value);
        });
  
        // if both objects is empty means we finish traversing - and we need to compare for hidden values
        if (shouldTypeAdaptors.isEmpty(other)) {
          this.eql(other);
        }
      } else {
        this.eql(other);
      }
    });
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  var aSlice = Array.prototype.slice;
  
  function propertyAssertions(should, Assertion) {
    var i = should.format;
    /**
     * Asserts given object has some descriptor. **On success it change given object to be value of property**.
     *
     * @name propertyWithDescriptor
     * @memberOf Assertion
     * @category assertion property
     * @param {string} name Name of property
     * @param {Object} desc Descriptor like used in Object.defineProperty (not required to add all properties)
     * @example
     *
     * ({ a: 10 }).should.have.propertyWithDescriptor('a', { enumerable: true });
     */
    Assertion.add("propertyWithDescriptor", function(name, desc) {
      this.params = {
        actual: this.obj,
        operator: "to have own property with descriptor " + i(desc)
      };
      var obj = this.obj;
      this.have.ownProperty(name);
      should(Object.getOwnPropertyDescriptor(Object(obj), name)).have.properties(desc);
    });
  
    /**
     * Asserts given object has property with optionally value. **On success it change given object to be value of property**.
     *
     * @name property
     * @memberOf Assertion
     * @category assertion property
     * @param {string} name Name of property
     * @param {*} [val] Optional property value to check
     * @example
     *
     * ({ a: 10 }).should.have.property('a');
     */
    Assertion.add("property", function(name, val) {
      name = convertPropertyName(name);
      if (arguments.length > 1) {
        var p = {};
        p[name] = val;
        this.have.properties(p);
      } else {
        this.have.properties(name);
      }
      this.obj = this.obj[name];
    });
  
    /**
     * Asserts given object has properties. On this method affect .any modifier, which allow to check not all properties.
     *
     * @name properties
     * @memberOf Assertion
     * @category assertion property
     * @param {Array|...string|Object} names Names of property
     * @example
     *
     * ({ a: 10 }).should.have.properties('a');
     * ({ a: 10, b: 20 }).should.have.properties([ 'a' ]);
     * ({ a: 10, b: 20 }).should.have.properties({ b: 20 });
     */
    Assertion.add("properties", function(names) {
      var values = {};
      if (arguments.length > 1) {
        names = aSlice.call(arguments);
      } else if (!Array.isArray(names)) {
        if (typeof names == "string" || typeof names == "symbol") {
          names = [names];
        } else {
          values = names;
          names = Object.keys(names);
        }
      }
  
      var obj = Object(this.obj),
        missingProperties = [];
  
      //just enumerate properties and check if they all present
      names.forEach(function(name) {
        if (!(name in obj)) {
          missingProperties.push(formatProp(name));
        }
      });
  
      var props = missingProperties;
      if (props.length === 0) {
        props = names.map(formatProp);
      } else if (this.anyOne) {
        props = names
          .filter(function(name) {
            return missingProperties.indexOf(formatProp(name)) < 0;
          })
          .map(formatProp);
      }
  
      var operator =
        (props.length === 1
          ? "to have property "
          : "to have " + (this.anyOne ? "any of " : "") + "properties ") + props.join(", ");
  
      this.params = { obj: this.obj, operator: operator };
  
      //check that all properties presented
      //or if we request one of them that at least one them presented
      this.assert(
        missingProperties.length === 0 || (this.anyOne && missingProperties.length != names.length)
      );
  
      // check if values in object matched expected
      var valueCheckNames = Object.keys(values);
      if (valueCheckNames.length) {
        var wrongValues = [];
        props = [];
  
        // now check values, as there we have all properties
        valueCheckNames.forEach(function(name) {
          var value = values[name];
          if (eql(obj[name], value).length !== 0) {
            wrongValues.push(formatProp(name) + " of " + i(value) + " (got " + i(obj[name]) + ")");
          } else {
            props.push(formatProp(name) + " of " + i(value));
          }
        });
  
        if ((wrongValues.length !== 0 && !this.anyOne) || (this.anyOne && props.length === 0)) {
          props = wrongValues;
        }
  
        operator =
          (props.length === 1
            ? "to have property "
            : "to have " + (this.anyOne ? "any of " : "") + "properties ") + props.join(", ");
  
        this.params = { obj: this.obj, operator: operator };
  
        //if there is no not matched values
        //or there is at least one matched
        this.assert(
          wrongValues.length === 0 || (this.anyOne && wrongValues.length != valueCheckNames.length)
        );
      }
    });
  
    /**
     * Asserts given object has property `length` with given value `n`
     *
     * @name length
     * @alias Assertion#lengthOf
     * @memberOf Assertion
     * @category assertion property
     * @param {number} n Expected length
     * @param {string} [description] Optional message
     * @example
     *
     * [1, 2].should.have.length(2);
     */
    Assertion.add("length", function(n, description) {
      this.have.property("length", n, description);
    });
  
    Assertion.alias("length", "lengthOf");
  
    /**
     * Asserts given object has own property. **On success it change given object to be value of property**.
     *
     * @name ownProperty
     * @alias Assertion#hasOwnProperty
     * @memberOf Assertion
     * @category assertion property
     * @param {string} name Name of property
     * @param {string} [description] Optional message
     * @example
     *
     * ({ a: 10 }).should.have.ownProperty('a');
     */
    Assertion.add("ownProperty", function(name, description) {
      name = convertPropertyName(name);
      this.params = {
        actual: this.obj,
        operator: "to have own property " + formatProp(name),
        message: description
      };
  
      this.assert(shouldUtil.hasOwnProperty(this.obj, name));
  
      this.obj = this.obj[name];
    });
  
    Assertion.alias("ownProperty", "hasOwnProperty");
  
    /**
     * Asserts given object is empty. For strings, arrays and arguments it checks .length property, for objects it checks keys.
     *
     * @name empty
     * @memberOf Assertion
     * @category assertion property
     * @example
     *
     * ''.should.be.empty();
     * [].should.be.empty();
     * ({}).should.be.empty();
     */
    Assertion.add(
      "empty",
      function() {
        this.params = { operator: "to be empty" };
        this.assert(shouldTypeAdaptors.isEmpty(this.obj));
      },
      true
    );
  
    /**
     * Asserts given object has such keys. Compared to `properties`, `keys` does not accept Object as a argument.
     * When calling via .key current object in assertion changed to value of this key
     *
     * @name keys
     * @alias Assertion#key
     * @memberOf Assertion
     * @category assertion property
     * @param {...*} keys Keys to check
     * @example
     *
     * ({ a: 10 }).should.have.keys('a');
     * ({ a: 10, b: 20 }).should.have.keys('a', 'b');
     * (new Map([[1, 2]])).should.have.key(1);
     *
     * json.should.have.only.keys('type', 'version')
     */
    Assertion.add("keys", function(keys) {
      keys = aSlice.call(arguments);
  
      var obj = Object(this.obj);
  
      // first check if some keys are missing
      var missingKeys = keys.filter(function(key) {
        return !shouldTypeAdaptors.has(obj, key);
      });
  
      var verb = "to have " + (this.onlyThis ? "only " : "") + (keys.length === 1 ? "key " : "keys ");
  
      this.params = { operator: verb + keys.join(", ") };
  
      if (missingKeys.length > 0) {
        this.params.operator += "\n\tmissing keys: " + missingKeys.join(", ");
      }
  
      this.assert(missingKeys.length === 0);
  
      if (this.onlyThis) {
        should(obj).have.size(keys.length);
      }
    });
  
    Assertion.add("key", function(key) {
      this.have.keys(key);
      this.obj = shouldTypeAdaptors.get(this.obj, key);
    });
  
    /**
     * Asserts given object has such value for given key
     *
     * @name value
     * @memberOf Assertion
     * @category assertion property
     * @param {*} key Key to check
     * @param {*} value Value to check
     * @example
     *
     * ({ a: 10 }).should.have.value('a', 10);
     * (new Map([[1, 2]])).should.have.value(1, 2);
     */
    Assertion.add("value", function(key, value) {
      this.have.key(key).which.is.eql(value);
    });
  
    /**
     * Asserts given object has such size.
     *
     * @name size
     * @memberOf Assertion
     * @category assertion property
     * @param {number} s Size to check
     * @example
     *
     * ({ a: 10 }).should.have.size(1);
     * (new Map([[1, 2]])).should.have.size(1);
     */
    Assertion.add("size", function(s) {
      this.params = { operator: "to have size " + s };
      should(shouldTypeAdaptors.size(this.obj)).be.exactly(s);
    });
  
    /**
     * Asserts given object has nested property in depth by path. **On success it change given object to be value of final property**.
     *
     * @name propertyByPath
     * @memberOf Assertion
     * @category assertion property
     * @param {Array|...string} properties Properties path to search
     * @example
     *
     * ({ a: {b: 10}}).should.have.propertyByPath('a', 'b').eql(10);
     */
    Assertion.add("propertyByPath", function(properties) {
      properties = aSlice.call(arguments);
  
      var allProps = properties.map(formatProp);
  
      properties = properties.map(convertPropertyName);
  
      var obj = should(Object(this.obj));
  
      var currentProperty;
      while (properties.length) {
        currentProperty = properties.shift();
        this.params = {
          operator:
            "to have property by path " +
            allProps.join(", ") +
            " - failed on " +
            formatProp(currentProperty)
        };
        obj = obj.have.property(currentProperty);
      }
  
      this.params = {
        obj: this.obj,
        operator: "to have property by path " + allProps.join(", ")
      };
  
      this.obj = obj.obj;
    });
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  function errorAssertions(should, Assertion) {
    var i = should.format;
  
    /**
     * Assert given function throws error with such message.
     *
     * @name throw
     * @memberOf Assertion
     * @category assertion errors
     * @alias Assertion#throwError
     * @param {string|RegExp|Function|Object|GeneratorFunction|GeneratorObject} [message] Message to match or properties
     * @param {Object} [properties] Optional properties that will be matched to thrown error
     * @example
     *
     * (function(){ throw new Error('fail') }).should.throw();
     * (function(){ throw new Error('fail') }).should.throw('fail');
     * (function(){ throw new Error('fail') }).should.throw(/fail/);
     *
     * (function(){ throw new Error('fail') }).should.throw(Error);
     * var error = new Error();
     * error.a = 10;
     * (function(){ throw error; }).should.throw(Error, { a: 10 });
     * (function(){ throw error; }).should.throw({ a: 10 });
     * (function*() {
     *   yield throwError();
     * }).should.throw();
     */
    Assertion.add("throw", function(message, properties) {
      var fn = this.obj;
      var err = {};
      var errorInfo = "";
      var thrown = false;
  
      if (shouldUtil.isGeneratorFunction(fn)) {
        return should(fn()).throw(message, properties);
      } else if (shouldUtil.isIterator(fn)) {
        return should(fn.next.bind(fn)).throw(message, properties);
      }
  
      this.is.a.Function();
  
      var errorMatched = true;
  
      try {
        fn();
      } catch (e) {
        thrown = true;
        err = e;
      }
  
      if (thrown) {
        if (message) {
          if ("string" == typeof message) {
            errorMatched = message == err.message;
          } else if (message instanceof RegExp) {
            errorMatched = message.test(err.message);
          } else if ("function" == typeof message) {
            errorMatched = err instanceof message;
          } else if (null != message) {
            try {
              should(err).match(message);
            } catch (e) {
              if (e instanceof should.AssertionError) {
                errorInfo = ": " + e.message;
                errorMatched = false;
              } else {
                throw e;
              }
            }
          }
  
          if (!errorMatched) {
            if ("string" == typeof message || message instanceof RegExp) {
              errorInfo =
                " with a message matching " +
                i(message) +
                ", but got '" +
                err.message +
                "'";
            } else if ("function" == typeof message) {
              errorInfo =
                " of type " +
                functionName$1(message) +
                ", but got " +
                functionName$1(err.constructor);
            }
          } else if ("function" == typeof message && properties) {
            try {
              should(err).match(properties);
            } catch (e) {
              if (e instanceof should.AssertionError) {
                errorInfo = ": " + e.message;
                errorMatched = false;
              } else {
                throw e;
              }
            }
          }
        } else {
          errorInfo = " (got " + i(err) + ")";
        }
      }
  
      this.params = { operator: "to throw exception" + errorInfo };
  
      this.assert(thrown);
      this.assert(errorMatched);
    });
  
    Assertion.alias("throw", "throwError");
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  
  function matchingAssertions(should, Assertion) {
    var i = should.format;
  
    /**
     * Asserts if given object match `other` object, using some assumptions:
     * First object matched if they are equal,
     * If `other` is a regexp and given object is a string check on matching with regexp
     * If `other` is a regexp and given object is an array check if all elements matched regexp
     * If `other` is a regexp and given object is an object check values on matching regexp
     * If `other` is a function check if this function throws AssertionError on given object or return false - it will be assumed as not matched
     * If `other` is an object check if the same keys matched with above rules
     * All other cases failed.
     *
     * Usually it is right idea to add pre type assertions, like `.String()` or `.Object()` to be sure assertions will do what you are expecting.
     * Object iteration happen by keys (properties with enumerable: true), thus some objects can cause small pain. Typical example is js
     * Error - it by default has 2 properties `name` and `message`, but they both non-enumerable. In this case make sure you specify checking props (see examples).
     *
     * @name match
     * @memberOf Assertion
     * @category assertion matching
     * @param {*} other Object to match
     * @param {string} [description] Optional message
     * @example
     * 'foobar'.should.match(/^foo/);
     * 'foobar'.should.not.match(/^bar/);
     *
     * ({ a: 'foo', c: 'barfoo' }).should.match(/foo$/);
     *
     * ['a', 'b', 'c'].should.match(/[a-z]/);
     *
     * (5).should.not.match(function(n) {
     *   return n < 0;
     * });
     * (5).should.not.match(function(it) {
     *    it.should.be.an.Array();
     * });
     * ({ a: 10, b: 'abc', c: { d: 10 }, d: 0 }).should
     * .match({ a: 10, b: /c$/, c: function(it) {
     *    return it.should.have.property('d', 10);
     * }});
     *
     * [10, 'abc', { d: 10 }, 0].should
     * .match({ '0': 10, '1': /c$/, '2': function(it) {
     *    return it.should.have.property('d', 10);
     * }});
     *
     * var myString = 'abc';
     *
     * myString.should.be.a.String().and.match(/abc/);
     *
     * myString = {};
     *
     * myString.should.match(/abc/); //yes this will pass
     * //better to do
     * myString.should.be.an.Object().and.not.empty().and.match(/abc/);//fixed
     *
     * (new Error('boom')).should.match(/abc/);//passed because no keys
     * (new Error('boom')).should.not.match({ message: /abc/ });//check specified property
     */
    Assertion.add("match", function(other, description) {
      this.params = { operator: "to match " + i(other), message: description };
  
      if (eql(this.obj, other).length !== 0) {
        if (other instanceof RegExp) {
          // something - regex
  
          if (typeof this.obj == "string") {
            this.assert(other.exec(this.obj));
          } else if (null != this.obj && typeof this.obj == "object") {
            var notMatchedProps = [],
              matchedProps = [];
            shouldTypeAdaptors.forEach(
              this.obj,
              function(value, name) {
                if (other.exec(value)) {
                  matchedProps.push(formatProp(name));
                } else {
                  notMatchedProps.push(formatProp(name) + " (" + i(value) + ")");
                }
              },
              this
            );
  
            if (notMatchedProps.length) {
              this.params.operator += "\n    not matched properties: " + notMatchedProps.join(", ");
            }
            if (matchedProps.length) {
              this.params.operator += "\n    matched properties: " + matchedProps.join(", ");
            }
  
            this.assert(notMatchedProps.length === 0);
          } else {
            // should we try to convert to String and exec?
            this.assert(false);
          }
        } else if (typeof other == "function") {
          var res;
  
          res = other(this.obj);
  
          //if we throw exception ok - it is used .should inside
          if (typeof res == "boolean") {
            this.assert(res); // if it is just boolean function assert on it
          }
        } else if (typeof this.obj == "object" && this.obj != null && (isPlainObject(other) || Array.isArray(other))) {
          // try to match properties (for Object and Array)
          notMatchedProps = [];
          matchedProps = [];
  
          shouldTypeAdaptors.forEach(
            other,
            function(value, key) {
              try {
                should(this.obj)
                  .have.property(key)
                  .which.match(value);
                matchedProps.push(formatProp(key));
              } catch (e) {
                if (e instanceof should.AssertionError) {
                  notMatchedProps.push(formatProp(key) + " (" + i(this.obj[key]) + ")");
                } else {
                  throw e;
                }
              }
            },
            this
          );
  
          if (notMatchedProps.length) {
            this.params.operator += "\n    not matched properties: " + notMatchedProps.join(", ");
          }
          if (matchedProps.length) {
            this.params.operator += "\n    matched properties: " + matchedProps.join(", ");
          }
  
          this.assert(notMatchedProps.length === 0);
        } else {
          this.assert(false);
        }
      }
    });
  
    /**
     * Asserts if given object values or array elements all match `other` object, using some assumptions:
     * First object matched if they are equal,
     * If `other` is a regexp - matching with regexp
     * If `other` is a function check if this function throws AssertionError on given object or return false - it will be assumed as not matched
     * All other cases check if this `other` equal to each element
     *
     * @name matchEach
     * @memberOf Assertion
     * @category assertion matching
     * @alias Assertion#matchEvery
     * @param {*} other Object to match
     * @param {string} [description] Optional message
     * @example
     * [ 'a', 'b', 'c'].should.matchEach(/\w+/);
     * [ 'a', 'a', 'a'].should.matchEach('a');
     *
     * [ 'a', 'a', 'a'].should.matchEach(function(value) { value.should.be.eql('a') });
     *
     * { a: 'a', b: 'a', c: 'a' }.should.matchEach(function(value) { value.should.be.eql('a') });
     */
    Assertion.add("matchEach", function(other, description) {
      this.params = {
        operator: "to match each " + i(other),
        message: description
      };
  
      shouldTypeAdaptors.forEach(
        this.obj,
        function(value) {
          should(value).match(other);
        },
        this
      );
    });
  
    /**
    * Asserts if any of given object values or array elements match `other` object, using some assumptions:
    * First object matched if they are equal,
    * If `other` is a regexp - matching with regexp
    * If `other` is a function check if this function throws AssertionError on given object or return false - it will be assumed as not matched
    * All other cases check if this `other` equal to each element
    *
    * @name matchAny
    * @memberOf Assertion
    * @category assertion matching
    * @param {*} other Object to match
    * @alias Assertion#matchSome
    * @param {string} [description] Optional message
    * @example
    * [ 'a', 'b', 'c'].should.matchAny(/\w+/);
    * [ 'a', 'b', 'c'].should.matchAny('a');
    *
    * [ 'a', 'b', 'c'].should.matchAny(function(value) { value.should.be.eql('a') });
    *
    * { a: 'a', b: 'b', c: 'c' }.should.matchAny(function(value) { value.should.be.eql('a') });
    */
    Assertion.add("matchAny", function(other, description) {
      this.params = {
        operator: "to match any " + i(other),
        message: description
      };
  
      this.assert(
        shouldTypeAdaptors.some(this.obj, function(value) {
          try {
            should(value).match(other);
            return true;
          } catch (e) {
            if (e instanceof should.AssertionError) {
              // Caught an AssertionError, return false to the iterator
              return false;
            }
            throw e;
          }
        })
      );
    });
  
    Assertion.alias("matchAny", "matchSome");
    Assertion.alias("matchEach", "matchEvery");
  }
  
  /*
   * should.js - assertion library
   * Copyright(c) 2010-2013 TJ Holowaychuk <tj@vision-media.ca>
   * Copyright(c) 2013-2017 Denis Bardadym <bardadymchik@gmail.com>
   * MIT Licensed
   */
  /**
   * Our function should
   *
   * @param {*} obj Object to assert
   * @returns {should.Assertion} Returns new Assertion for beginning assertion chain
   * @example
   *
   * var should = require('should');
   * should('abc').be.a.String();
   */
  function should(obj) {
    return new Assertion(obj);
  }
  
  should.AssertionError = AssertionError;
  should.Assertion = Assertion;
  
  // exposing modules dirty way
  should.modules = {
    format: sformat,
    type: getType,
    equal: eql
  };
  should.format = format$1;
  
  /**
   * Object with configuration.
   * It contains such properties:
   * * `checkProtoEql` boolean - Affect if `.eql` will check objects prototypes
   * * `plusZeroAndMinusZeroEqual` boolean - Affect if `.eql` will treat +0 and -0 as equal
   * Also it can contain options for should-format.
   *
   * @type {Object}
   * @memberOf should
   * @static
   * @example
   *
   * var a = { a: 10 }, b = Object.create(null);
   * b.a = 10;
   *
   * a.should.be.eql(b);
   * //not throws
   *
   * should.config.checkProtoEql = true;
   * a.should.be.eql(b);
   * //throws AssertionError: expected { a: 10 } to equal { a: 10 } (because A and B have different prototypes)
   */
  should.config = config;
  
  /**
   * Allow to extend given prototype with should property using given name. This getter will **unwrap** all standard wrappers like `Number`, `Boolean`, `String`.
   * Using `should(obj)` is the equivalent of using `obj.should` with known issues (like nulls and method calls etc).
   *
   * To add new assertions, need to use Assertion.add method.
   *
   * @param {string} [propertyName] Name of property to add. Default is `'should'`.
   * @param {Object} [proto] Prototype to extend with. Default is `Object.prototype`.
   * @memberOf should
   * @returns {{ name: string, descriptor: Object, proto: Object }} Descriptor enough to return all back
   * @static
   * @example
   *
   * var prev = should.extend('must', Object.prototype);
   *
   * 'abc'.must.startWith('a');
   *
   * var should = should.noConflict(prev);
   * should.not.exist(Object.prototype.must);
   */
  should.extend = function(propertyName, proto) {
    propertyName = propertyName || "should";
    proto = proto || Object.prototype;
  
    var prevDescriptor = Object.getOwnPropertyDescriptor(proto, propertyName);
  
    Object.defineProperty(proto, propertyName, {
      set: function() {},
      get: function() {
        return should(isWrapperType(this) ? this.valueOf() : this);
      },
      configurable: true
    });
  
    return { name: propertyName, descriptor: prevDescriptor, proto: proto };
  };
  
  /**
   * Delete previous extension. If `desc` missing it will remove default extension.
   *
   * @param {{ name: string, descriptor: Object, proto: Object }} [desc] Returned from `should.extend` object
   * @memberOf should
   * @returns {Function} Returns should function
   * @static
   * @example
   *
   * var should = require('should').noConflict();
   *
   * should(Object.prototype).not.have.property('should');
   *
   * var prev = should.extend('must', Object.prototype);
   * 'abc'.must.startWith('a');
   * should.noConflict(prev);
   *
   * should(Object.prototype).not.have.property('must');
   */
  should.noConflict = function(desc) {
    desc = desc || should._prevShould;
  
    if (desc) {
      delete desc.proto[desc.name];
  
      if (desc.descriptor) {
        Object.defineProperty(desc.proto, desc.name, desc.descriptor);
      }
    }
    return should;
  };
  
  /**
   * Simple utility function for a bit more easier should assertion extension
   * @param {Function} f So called plugin function. It should accept 2 arguments: `should` function and `Assertion` constructor
   * @memberOf should
   * @returns {Function} Returns `should` function
   * @static
   * @example
   *
   * should.use(function(should, Assertion) {
   *   Assertion.add('asset', function() {
   *      this.params = { operator: 'to be asset' };
   *
   *      this.obj.should.have.property('id').which.is.a.Number();
   *      this.obj.should.have.property('path');
   *  })
   * })
   */
  should.use = function(f) {
    f(should, should.Assertion);
    return this;
  };
  
  should
    .use(assertExtensions)
    .use(chainAssertions)
    .use(booleanAssertions)
    .use(numberAssertions)
    .use(equalityAssertions)
    .use(typeAssertions)
    .use(stringAssertions)
    .use(propertyAssertions)
    .use(errorAssertions)
    .use(matchingAssertions)
    .use(containAssertions)
    .use(promiseAssertions);
  
  var asFunction = should;
  
  export default asFunction;
  