text: 'use strict'
  
  module.exports = match
  
  function isArguments (obj) {
    return Object.prototype.toString.call(obj) === '[object Arguments]'
  }
  
  function regexpSame (a, b) {
    return a.source === b.source &&
      a.global === b.global &&
      a.multiline === b.multiline &&
      a.lastIndex === b.lastIndex &&
      a.ignoreCase === b.ignoreCase
  }
  
  function arrayFrom (obj) {
    return Array.isArray(obj) ? obj
      : Array.from ? Array.from(obj)
      : Array.prototype.slice.call(obj)
  }
  
  var hasSet = typeof Set === 'function'
  
  function isSet (object) {
    return hasSet && (object instanceof Set)
  }
  
  function isMap (object) {
    return hasSet && (object instanceof Map)
  }
  
  function bufferSame (a, b) {
    var ret
    if (a.equals) {
      ret = a.equals(b)
    } else if (a.length !== b.length) {
      ret = false
    } else {
      ret = true
      for (var j = 0; j < a.length && ret; j++) {
        if (a[j] != b[j])
          ret = false
      }
    }
    return ret
  }
  
  function match (obj, pattern) {
    return match_(obj, pattern, [], [])
  }
  
  function setMatch (obj, pattern) {
    var ret = true
    if (!isSet(obj))
      ret = false
    else if (pattern.size === 0)
      ret = true
    else {
      pattern.forEach(function (entry) {
        if (ret)
          ret = obj.has(entry)
      })
    }
    return ret
  }
  
  function mapMatch (obj, pattern, ca, cb) {
    var ret = true
    if (!isMap(obj))
      ret = false
    else if (pattern.size === 0)
      ret = true
    else {
      pattern.forEach(function (value, key) {
        if (ret)
          ret = obj.has(key)
        if (ret)
          ret = match_(value, obj.get(key), ca, cb)
      })
    }
    return ret
  }
  
  
  function match_ (obj, pattern, ca, cb) {
    return obj == pattern ? (
      obj === null || pattern === null ? true
      : typeof obj === 'object' && typeof pattern === 'object' ? true
      : typeof obj === 'object' && typeof pattern !== 'object' ? false
      : typeof obj !== 'object' && typeof pattern === 'object' ? false
      : true
    )
    : obj === null || pattern === null ? false
    : pattern instanceof RegExp ? (
      typeof obj === 'string' ? pattern.test(obj)
      : obj instanceof RegExp ? regexpSame(obj, pattern)
      : pattern.test('' + obj)
    )
    : isSet(pattern) ? setMatch(obj, pattern)
    : isMap(pattern) ? mapMatch(obj, pattern, ca, cb)
    : typeof obj === 'string' && typeof pattern === 'string' && pattern ?
      obj.indexOf(pattern) !== -1
    : obj instanceof Date && pattern instanceof Date ?
      obj.getTime() === pattern.getTime()
    : obj instanceof Date && typeof pattern === 'string' ?
      obj.getTime() === new Date(pattern).getTime()
    : isArguments(obj) || isArguments(pattern) ?
      match_(arrayFrom(obj), arrayFrom(pattern), ca, cb)
    : pattern === Buffer ? Buffer.isBuffer(obj)
    : pattern === Function ? typeof obj === 'function'
    : pattern === Number ?
      typeof obj === 'number' && obj === obj && isFinite(obj)
    : pattern !== pattern ? obj !== obj
    : pattern === String ? typeof obj === 'string'
    : pattern === Boolean ? typeof obj === 'boolean'
    : pattern === Array ? Array.isArray(obj)
    : typeof pattern === 'function' && typeof obj === 'object' ?
      obj instanceof pattern
    : typeof obj !== 'object' || typeof pattern !== 'object' ? false
    : Buffer.isBuffer(obj) && Buffer.isBuffer(pattern) ?
      bufferSame(obj, pattern)
    : matchObj(obj, pattern, Object.keys(obj), Object.keys(pattern), ca, cb)
  }
  
  function matchObj (obj, pattern, kobj, kpat, ca, cb) {
    var ret = true
  
    // don't bother with stack acrobatics if there's nothing there
    if (kobj.length === 0 && kpat.length === 0)
      ret = true
    else {
      // if we've seen this exact pattern and object already, then
      // it means that pattern and obj have matching cyclicalness
      // however, non-cyclical patterns can match cyclical objects
      var cal = ca.length
      var go = true
      while (cal-- && go) {
        if (ca[cal] === obj && cb[cal] === pattern) {
          ret = true
          go = false
        }
      }
  
      if (go) {
        ca.push(obj)
        cb.push(pattern)
  
        var key
        for (var l = kpat.length - 1; l >= 0 && ret; l--) {
          key = kpat[l]
          if (!match_(obj[key], pattern[key], ca, cb))
            ret = false
        }
  
        if (ret) {
          ca.pop()
          cb.pop()
        }
      }
    }
  
    return ret
  }
  