function zip() {
	var nargs;
	var args;
	var fill;
	var opts;
	var arg;
	var flg;
	var len;
	var arr;
	var out;
	var val;
	var i;
	var j;

	opts = {};
	fill = null;
	args = Array.prototype.slice.call( arguments );
	nargs = args.length;

	for ( i = 0; i < nargs-1; i++ ) {
		if ( !isArray( args[i] ) ) {
			throw new TypeError( format( 'invalid argument. Must provide array arguments. Value: `%s`.', args[i] ) );
		}
	}
	arg = args[ nargs-1 ];
	flg = isObject( arg );
	if ( !flg && !isArray( arg ) ) {
		throw new TypeError( format( 'invalid argument. Last argument must be either an array or an options object. Value: `%s`.', arg ) );
	}
	if ( flg ) {
		opts = args.pop();
	}
	nargs = args.length;
	if ( nargs === 0 ) {
		throw new Error( 'insufficient arguments. Must provide at least one array.' );
	}
	if ( hasOwnProp( opts, 'trunc' ) ) {
		if ( !isBoolean( opts.trunc ) ) {
			throw new TypeError( format( 'invalid option. `%s` option must be a boolean. Option: `%s`.', 'trunc', opts.trunc ) );
		}
	} else {
		opts.trunc = true;
	}
	if ( hasOwnProp( opts, 'fill' ) ) {
		fill = opts.fill;
	}
	if ( hasOwnProp( opts, 'arrays' ) ) {
		if ( !isBoolean( opts.arrays ) ) {
			throw new TypeError( format( 'invalid option. `%s` option must be a boolean. Option: `%s`.', 'arrays', opts.arrays ) );
		}
	} else {
		opts.arrays = false;
	}
	if ( nargs === 1 && opts.arrays ) {
		// Treat the lone array argument as an array of arrays to be zipped...
		args = args[ 0 ];
		nargs = args.length;
	}
	len = args[ 0 ].length;
	if ( opts.trunc ) {
		// Find the min array length...
		for ( i = 0; i < nargs; i++ ) {
			val = args[ i ].length;
			if ( val < len ) {
				len = val;
			}
		}
	} else {
		// Find the max array length...
		for ( i = 0; i < nargs; i++ ) {
			val = args[ i ].length;
			if ( val > len ) {
				len = val;
			}
		}
	}
	out = new Array( len );
	for ( j = 0; j < len; j++ ) {
		// Temporary array to store tuples...
		arr = new Array( nargs );

		// Create the tuples...
		for ( i = 0; i < nargs; i++ ) {
			arg = args[ i ];

			// If an array is too short, use a fill value...
			if ( arg.length <= j ) {
				arr[ i ] = fill;
				continue;
			}
			arr[ i ] = arg[ j ];
		}
		out[ j ] = arr;
	}
	return out;
}
Generates array tuples from input arrays.

@param {...Array} arr - input arrays to be zipped
@param {Object} [opts] - function options
@param {boolean} [opts.trunc=true] - boolean indicating whether to truncate arrays longer than the shortest input array
@param {*} [opts.fill=null] - fill value used for arrays of unequal length
@param {boolean} [opts.arrays=false] - boolean indicating whether an input array should be interpreted as an array of arrays to be zipped
@throws {TypeError} must provide array arguments
@throws {Error} must provide at least one array
@throws {TypeError} options argument must be an object
@throws {TypeError} must provide valid options
@returns {Array} output array of arrays

@example
var zipped = zip( [ 1, 2 ], [ 'a', 'b' ] );
// returns [ [ 1, 'a' ], [ 2, 'b' ] ]

@example
var zipped = zip( [ 1, 2, 3 ], [ 'a', 'b' ] );
// returns [ [ 1, 'a' ], [ 2, 'b' ] ]

@example
var opts = {
    'trunc': false
};

var zipped = zip( [ 1, 2, 3 ], [ 'a', 'b' ], opts );
// returns [ [ 1, 'a' ], [ 2, 'b' ], [ 3, null ] ]

@example
var opts = {
    'trunc': false,
    'fill': ''
};

var zipped = zip( [ 1, 2, 3 ], [ 'a', 'b' ], opts );
// returns [ [ 1, 'a' ], [ 2, 'b' ], [ 3, '' ] ]

@example
var arr = [ [ 1, 2 ], [ 'a', 'b' ] ];

// Default behavior:
var zipped = zip( arr );
// returns [ [ [ 1, 2 ] ], [ [ 'a', 'b' ] ] ]

// Array of arrays:
zipped = zip( arr, { 'arrays': true } );
// returns [ [ 1, 'a' ], [ 2, 'b' ] ]