text: 'use strict';
  
  /*!
   * Module dependencies.
   */
  
  var Buffer = require('safe-buffer').Buffer;
  var RegExpClone = require('regexp-clone');
  
  /**
   * Clones objects
   *
   * @param {Object} obj the object to clone
   * @param {Object} options
   * @return {Object} the cloned object
   * @api private
   */
  
  var clone = exports.clone = function clone(obj, options) {
    if (obj === undefined || obj === null)
      return obj;
  
    if (Array.isArray(obj))
      return exports.cloneArray(obj, options);
  
    if (obj.constructor) {
      if (/ObjectI[dD]$/.test(obj.constructor.name)) {
        return 'function' == typeof obj.clone
          ? obj.clone()
          : new obj.constructor(obj.id);
      }
  
      if (obj.constructor.name === 'ReadPreference') {
        return new obj.constructor(obj.mode, clone(obj.tags, options));
      }
  
      if ('Binary' == obj._bsontype && obj.buffer && obj.value) {
        return 'function' == typeof obj.clone
          ? obj.clone()
          : new obj.constructor(obj.value(true), obj.sub_type);
      }
  
      if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name)
        return new obj.constructor(+obj);
  
      if ('RegExp' === obj.constructor.name)
        return RegExpClone(obj);
  
      if ('Buffer' === obj.constructor.name)
        return exports.cloneBuffer(obj);
    }
  
    if (isObject(obj))
      return exports.cloneObject(obj, options);
  
    if (obj.valueOf)
      return obj.valueOf();
  };
  
  /*!
   * ignore
   */
  
  exports.cloneObject = function cloneObject(obj, options) {
    var minimize = options && options.minimize;
    var ret = {};
    var hasKeys;
    var val;
    var k;
  
    for (k in obj) {
      val = clone(obj[k], options);
  
      if (!minimize || ('undefined' !== typeof val)) {
        hasKeys || (hasKeys = true);
        ret[k] = val;
      }
    }
  
    return minimize
      ? hasKeys && ret
      : ret;
  };
  
  exports.cloneArray = function cloneArray(arr, options) {
    var ret = [];
    for (var i = 0, l = arr.length; i < l; i++)
      ret.push(clone(arr[i], options));
    return ret;
  };
  
  /**
   * process.nextTick helper.
   *
   * Wraps the given `callback` in a try/catch. If an error is
   * caught it will be thrown on nextTick.
   *
   * node-mongodb-native had a habit of state corruption when
   * an error was immediately thrown from within a collection
   * method (find, update, etc) callback.
   *
   * @param {Function} [callback]
   * @api private
   */
  
  exports.tick = function tick(callback) {
    if ('function' !== typeof callback) return;
    return function() {
      // callbacks should always be fired on the next
      // turn of the event loop. A side benefit is
      // errors thrown from executing the callback
      // will not cause drivers state to be corrupted
      // which has historically been a problem.
      var args = arguments;
      soon(function() {
        callback.apply(this, args);
      });
    };
  };
  
  /**
   * Merges `from` into `to` without overwriting existing properties.
   *
   * @param {Object} to
   * @param {Object} from
   * @api private
   */
  
  exports.merge = function merge(to, from) {
    var keys = Object.keys(from),
        i = keys.length,
        key;
  
    while (i--) {
      key = keys[i];
      if ('undefined' === typeof to[key]) {
        to[key] = from[key];
      } else {
        if (exports.isObject(from[key])) {
          merge(to[key], from[key]);
        } else {
          to[key] = from[key];
        }
      }
    }
  };
  
  /**
   * Same as merge but clones the assigned values.
   *
   * @param {Object} to
   * @param {Object} from
   * @api private
   */
  
  exports.mergeClone = function mergeClone(to, from) {
    var keys = Object.keys(from),
        i = keys.length,
        key;
  
    while (i--) {
      key = keys[i];
      if ('undefined' === typeof to[key]) {
        to[key] = clone(from[key]);
      } else {
        if (exports.isObject(from[key])) {
          mergeClone(to[key], from[key]);
        } else {
          to[key] = clone(from[key]);
        }
      }
    }
  };
  
  /**
   * Read pref helper (mongo 2.2 drivers support this)
   *
   * Allows using aliases instead of full preference names:
   *
   *     p   primary
   *     pp  primaryPreferred
   *     s   secondary
   *     sp  secondaryPreferred
   *     n   nearest
   *
   * @param {String} pref
   */
  
  exports.readPref = function readPref(pref) {
    switch (pref) {
      case 'p':
        pref = 'primary';
        break;
      case 'pp':
        pref = 'primaryPreferred';
        break;
      case 's':
        pref = 'secondary';
        break;
      case 'sp':
        pref = 'secondaryPreferred';
        break;
      case 'n':
        pref = 'nearest';
        break;
    }
  
    return pref;
  };
  
  
  /**
   * Read Concern helper (mongo 3.2 drivers support this)
   *
   * Allows using string to specify read concern level:
   *
   *     local          3.2+
   *     available      3.6+
   *     majority       3.2+
   *     linearizable   3.4+
   *     snapshot       4.0+
   *
   * @param {String|Object} concern
   */
  
  exports.readConcern = function readConcern(concern) {
    if ('string' === typeof concern) {
      switch (concern) {
        case 'l':
          concern = 'local';
          break;
        case 'a':
          concern = 'available';
          break;
        case 'm':
          concern = 'majority';
          break;
        case 'lz':
          concern = 'linearizable';
          break;
        case 's':
          concern = 'snapshot';
          break;
      }
      concern = { level: concern };
    }
    return concern;
  };
  
  /**
   * Object.prototype.toString.call helper
   */
  
  var _toString = Object.prototype.toString;
  exports.toString = function(arg) {
    return _toString.call(arg);
  };
  
  /**
   * Determines if `arg` is an object.
   *
   * @param {Object|Array|String|Function|RegExp|any} arg
   * @return {Boolean}
   */
  
  var isObject = exports.isObject = function(arg) {
    return '[object Object]' == exports.toString(arg);
  };
  
  /**
   * Determines if `arg` is an array.
   *
   * @param {Object}
   * @return {Boolean}
   * @see nodejs utils
   */
  
  exports.isArray = function(arg) {
    return Array.isArray(arg) ||
      'object' == typeof arg && '[object Array]' == exports.toString(arg);
  };
  
  /**
   * Object.keys helper
   */
  
  exports.keys = Object.keys || function(obj) {
    var keys = [];
    for (var k in obj) if (obj.hasOwnProperty(k)) {
      keys.push(k);
    }
    return keys;
  };
  
  /**
   * Basic Object.create polyfill.
   * Only one argument is supported.
   *
   * Based on https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create
   */
  
  exports.create = 'function' == typeof Object.create
    ? Object.create
    : create;
  
  function create(proto) {
    if (arguments.length > 1) {
      throw new Error('Adding properties is not supported');
    }
  
    function F() {}
    F.prototype = proto;
    return new F;
  }
  
  /**
   * inheritance
   */
  
  exports.inherits = function(ctor, superCtor) {
    ctor.prototype = exports.create(superCtor.prototype);
    ctor.prototype.constructor = ctor;
  };
  
  /**
   * nextTick helper
   * compat with node 0.10 which behaves differently than previous versions
   */
  
  var soon = exports.soon = 'function' == typeof setImmediate
    ? setImmediate
    : process.nextTick;
  
  /**
   * Clones the contents of a buffer.
   *
   * @param {Buffer} buff
   * @return {Buffer}
   */
  
  exports.cloneBuffer = function(buff) {
    var dupe = Buffer.alloc(buff.length);
    buff.copy(dupe, 0, 0, buff.length);
    return dupe;
  };
  
  /**
   * Check if this object is an arguments object
   *
   * @param {Any} v
   * @return {Boolean}
   */
  
  exports.isArgumentsObject = function(v) {
    return Object.prototype.toString.call(v) === '[object Arguments]';
  };
  