text: 'use strict';
  
  const { call3, callProxyReciever } = require('./util');
  
  const [setParallel, setParallelWithOrder, setSeries] = [
    [iterateArrayParallel, iterateObjectParallel],
    [iterateArrayParallel, iterateObjectParallelWithOrder],
    [iterateArraySeries, iterateObjectSeries],
  ].map(createSet);
  
  const arrayIteratorList = [
    iterateArrayParallel,
    iterateArrayWithString,
    iterateArrayWithObject,
    iterateArrayWithArray,
  ];
  const objectIteratorList = [
    iterateObjectParallel,
    iterateObjectWithString,
    iterateObjectWithObject,
    iterateObjectWithArray,
  ];
  const [setShorthand, setShorthandWithOrder, setPickShorthand, setOmitShorthand] = [
    [arrayIteratorList, objectIteratorList],
    [arrayIteratorList, [iterateObjectParallelWithOrder, ...objectIteratorList.slice(1)]],
    [
      [...arrayIteratorList.slice(0, 3), iteratePickWithArray],
      [...objectIteratorList.slice(0, 3), iteratePickWithArray],
    ],
    [
      [...arrayIteratorList.slice(0, 3), iterateOmitWithArray],
      [...objectIteratorList.slice(0, 3), iterateOmitWithArray],
    ],
  ].map(createSetShorthand);
  
  module.exports = {
    execute,
    setParallel,
    setParallelWithOrder,
    setShorthand,
    setShorthandWithOrder,
    setPickShorthand,
    setOmitShorthand,
    setSeries,
    setLimit,
  };
  
  function execute(collection) {
    this._callResolve = this._iterate;
    this._set(collection);
    this._execute();
  }
  
  function createSet([iterateArray, iterateObject]) {
    return function set(collection) {
      if (Array.isArray(collection)) {
        this._coll = collection;
        this._size = collection.length;
        this._iterate = iterateArray;
      } else if (collection && typeof collection === 'object') {
        const keys = Object.keys(collection);
        this._coll = collection;
        this._size = keys.length;
        this._keys = keys;
        this._iterate = iterateObject;
      } else {
        this._size = 0;
      }
      this._rest = this._size;
      return this;
    };
  }
  
  function createSetShorthand(list) {
    const [getArrayIterator, getObjectIterator] = list.map(createIteratorGetter);
    return function set(collection) {
      if (Array.isArray(collection)) {
        this._coll = collection;
        this._size = collection.length;
        this._iterate = getArrayIterator(this._iterator);
      } else if (collection && typeof collection === 'object') {
        const keys = Object.keys(collection);
        this._coll = collection;
        this._size = keys.length;
        this._keys = keys;
        this._iterate = getObjectIterator(this._iterator);
      } else {
        this._size = 0;
      }
      this._rest = this._size;
      return this;
    };
  }
  
  function createIteratorGetter([
    iterateParallel,
    iterateWithString,
    iterateWithObject,
    iterateWithArray,
  ]) {
    return (iterator) => {
      switch (typeof iterator) {
        case 'function':
          return iterateParallel;
        case 'string':
          return iterateWithString;
        case 'object':
          return Array.isArray(iterator) ? iterateWithArray : iterateWithObject;
      }
    };
  }
  
  function setLimit(collection) {
    setSeries.call(this, collection);
    const { _limit, _size } = this;
    this._limit = _limit < _size ? _limit : _size;
    this._callRest = _size - this._limit;
    return this;
  }
  
  function iterateArrayParallel() {
    const { _rest, _iterator, _coll } = this;
    let i = -1;
    while (++i < _rest && callProxyReciever(call3(_iterator, _coll[i], i, _coll), this, i)) {}
  }
  
  function iterateObjectParallel() {
    const { _rest, _iterator, _coll, _keys } = this;
    let i = -1;
    while (++i < _rest) {
      const key = _keys[i];
      if (callProxyReciever(call3(_iterator, _coll[key], key, _coll), this, i) === false) {
        break;
      }
    }
  }
  function iterateObjectParallelWithOrder() {
    const { _rest, _iterator, _coll, _keys, _result } = this;
    let i = -1;
    while (++i < _rest) {
      const key = _keys[i];
      _result[key] = undefined;
      if (callProxyReciever(call3(_iterator, _coll[key], key, _coll), this, i) === false) {
        break;
      }
    }
  }
  
  function iterateArraySeries() {
    const { _coll } = this;
    const i = this._index++;
    callProxyReciever(call3(this._iterator, _coll[i], i, _coll), this, i);
  }
  
  function iterateObjectSeries() {
    const { _coll } = this;
    const i = this._index++;
    const key = this._keys[i];
    callProxyReciever(call3(this._iterator, _coll[key], key, _coll), this, i);
  }
  
  function iterateArrayWithString() {
    const { _iterator, _coll } = this;
    let i = -1;
    while (++i < this._size) {
      const obj = _coll[i];
      if (obj) {
        this._callResolve(obj[_iterator], i);
      } else {
        this._callResolve(undefined, i);
      }
    }
  }
  
  function iterateObjectWithString() {
    const { _iterator, _coll, _keys } = this;
    let i = -1;
    while (++i < this._size) {
      const obj = _coll[_keys[i]];
      if (obj) {
        this._callResolve(obj[_iterator], i);
      } else {
        this._callResolve(undefined, i);
      }
    }
  }
  
  function iterateArrayWithArray() {
    const { _coll } = this;
    const [key, value] = this._iterator;
    let i = -1;
    while (++i < this._size) {
      const obj = _coll[i];
      if (obj) {
        this._callResolve(obj[key] === value, i);
      } else {
        this._callResolve(undefined, i);
      }
    }
  }
  
  function iterateObjectWithArray() {
    const { _coll, _keys } = this;
    const [key, value] = this._iterator;
    let i = -1;
    while (++i < this._size) {
      const obj = _coll[_keys[i]];
      if (obj) {
        this._callResolve(obj[key] === value, i);
      } else {
        this._callResolve(undefined, i);
      }
    }
  }
  
  function iterateArrayWithObject() {
    const { _iterator: object, _coll } = this;
    const keys = Object.keys(object);
    let i = -1;
    first: while (++i < this._size) {
      const obj = _coll[i];
      if (!obj) {
        this._callResolve(undefined, i);
        continue;
      }
      let l = keys.length;
      while (l--) {
        const key = keys[l];
        if (obj[key] !== object[key]) {
          this._callResolve(false, i);
          continue first;
        }
      }
      this._callResolve(true, i);
    }
  }
  
  function iterateObjectWithObject() {
    const { _iterator: object, _coll, _keys } = this;
    const keys = Object.keys(object);
    let i = -1;
    first: while (++i < this._size) {
      const obj = _coll[_keys[i]];
      if (!obj) {
        this._callResolve(undefined, i);
        continue;
      }
      let l = keys.length;
      while (l--) {
        const key = keys[l];
        if (obj[key] !== object[key]) {
          this._callResolve(false, i);
          continue first;
        }
      }
      this._callResolve(true, i);
    }
  }
  
  function iteratePickWithArray() {
    const { _coll, _result } = this;
    pick(this._iterator);
    this._promise._resolve(_result);
  
    function pick(array) {
      let i = -1;
      while (++i < array.length) {
        const key = array[i];
        if (Array.isArray(key)) {
          pick(key);
          continue;
        }
        if (_coll.hasOwnProperty(key)) {
          _result[key] = _coll[key];
        }
      }
    }
  }
  
  function iterateOmitWithArray() {
    const { _coll, _result } = this;
    const map = {};
    createMap(this._iterator);
    Object.keys(_coll).forEach((key) => {
      if (map.hasOwnProperty(key) === false) {
        _result[key] = _coll[key];
      }
    });
    this._promise._resolve(_result);
  
    function createMap(array) {
      let i = -1;
      while (++i < array.length) {
        const key = array[i];
        if (Array.isArray(key)) {
          createMap(key);
          continue;
        }
        map[key] = true;
      }
    }
  }
  