<?php

namespace Stax;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Underscore.php
 * https://github.com/bdelespierre/underscore.php
 * (c) 2013-2014 Benjamin Delespierre
 * Underscore may be freely distributed under the LGPL license.
 */
class Underscore {
	const BREAKER = "\0\0"; // loop breaker
	const VERSION = "0.2.0";

	/**
	 * Collection Functions
	 * --------------------
	 */

	/**
	 * Iterates over a list of elements, yielding each in turn to an iterator function. The iterator is bound to
	 * the context object, if one is passed. Each invocation of iterator is called with three arguments:
	 * (element, index, list). If list is an object, iterator's arguments will be (value, key, list).
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list to iterate over
	 * @param callable $iterator the iteration function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @throws InvalidArgumentException when $list cannot be iterated over
	 *
	 * @return void
	 */
	public static function each( $list, callable $iterator, $context = null ) {
		if ( $list === null ) {
			return null;
		}

		static::associate( $iterator, $context );

		if ( ! static::isTraversable( $list ) ) {
			throw new \InvalidArgumentException( "cannot iterate over " . static::typeOf( $list ) );
		}

		foreach ( $list as $index => $item ) {
			if ( $iterator( $item, $index, $list ) === static::BREAKER ) {
				break;
			}
		}

		return $list;
	}

	/**
	 * @see Underscore::eachReference
	 */
	public static function walk( & $list, callable $iterator, $context = null ) {
		return static::eachReference( $list, $iterator, $context );
	}

	/**
	 * Does the very same job as each but provide a reference of every list item to the iterator function.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list to iterate over
	 * @param callable $iterator the iteration function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @throws InvalidArgumentException if $list cannot be iterated over
	 * @throws UnexpectedValueException if $list is an instance of Iterator
	 *
	 * @return void
	 */
	public static function eachReference( & $list, callable $iterator, $context = null ) {
		if ( $list === null ) {
			return $list;
		}

		static::associate( $iterator, $context );

		if ( ! static::isTraversable( $list ) ) {
			throw new \InvalidArgumentException( "canot iterate over " . static::typeOf( $list ) );
		}

		if ( $list instanceof \Iterator ) {
			throw new \UnexpectedValueException( "cannot iterate over an iterator by reference" );
		}

		foreach ( $list as $index => & $item ) {
			if ( $iterator( $item, $index, $list ) === static::BREAKER ) {
				break;
			}
		}

		return $list;
	}

	/**
	 * @see Underscore::map
	 */
	public static function collect( $list, callable $iterator, $context = null ) {
		return static::map( $list, $iterator, $context );
	}

	/**
	 * Produces a new array of values by mapping each value in list through a transformation function (iterator). The
	 * iterator is bound to the context object, if one is passed. Each invocation of iterator is called with three
	 * arguments: (element, index, list). If list is an object, iterator's arguments will be (value, key, list).
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to map
	 * @param callable $iterator the transformation function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function map( $list, callable $iterator, $context = null ) {
		static::associate( $iterator, $context );

		$result = [];
		static::each( $list, function ( $item, $index, $list ) use ( $iterator, & $result ) {
			$result[ $index ] = $iterator( $item, $index, $list );
		} );

		return $result;
	}

	/**
	 * @see Underscore::reduce
	 */
	public static function inject( $list, callable $iterator, $memo, $context = null ) {
		return static::reduce( $list, $iterator, $memo, $context );
	}

	/**
	 * @see Underscore::reduce
	 */
	public static function foldl( $list, callable $iterator, $memo, $context = null ) {
		return static::reduce( $list, $iterator, $memo, $context );
	}

	/**
	 * Also known as inject and foldl, reduce boils down a list of values into a single value. Memo is the initial
	 * state of the reduction, and each successive step of it should be returned by iterator. The iterator is passed
	 * four arguments: the memo, then the value and index (or key) of the iteration, and finally a reference to
	 * the entire list.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to reduce
	 * @param callable $iterator the reduction function
	 * @param mixed $memo The initial reduction state
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return mixed
	 */
	public static function reduce( $list, callable $iterator, $memo, $context = null ) {
		static::associate( $iterator, $context );

		static::each( $list, function ( $item, $index, $list ) use ( $iterator, & $memo ) {
			$memo = $iterator( $memo, $item, $index, $list );
		} );

		return $memo;
	}

	/**
	 * @see Underscore::reduceRight
	 */
	public static function foldr( $list, callable $iterator, $memo, $context = null ) {
		return static::reduceRight( $list, $iterator, $memo, $context );
	}

	/**
	 * The right-associative version of reduce.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to reduce
	 * @param callable $iterator the reduction function
	 * @param mixed $memo The initial reduction state
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return mixed
	 */
	public static function reduceRight( $list, callable $iterator, $memo, $context = null ) {
		return static::reduce( array_reverse( static::toArray( $list ) ), $iterator, $memo, $context );
	}

	/**
	 * @see Underscore::find
	 */
	public static function detect( $list, callable $iterator, $context = null ) {
		return static::find( $list, $iterator, $context );
	}

	/**
	 * Looks through each value in the list, returning the first one that passes a truth test (iterator), or null if
	 * no value passes the test. The function returns as soon as it finds an acceptable element, and doesn't traverse
	 * the entire list.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to iterate over
	 * @param callable $iterator the truth-test function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return mixed
	 */
	public static function find( $list, callable $iterator, $context = null ) {
		static::associate( $iterator, $context );

		$result = null;
		static::each( $list, function ( $item ) use ( $iterator, & $result ) {
			if ( $iterator( $item ) ) {
				$result = $item;

				return static::BREAKER;
			}
		} );

		return $result;
	}

	/**
	 * @see Underscore::filter
	 */
	public static function select( $list, callable $iterator, $context = null ) {
		return static::filter( $list, $iterator, $context );
	}

	/**
	 * Looks through each value in the list, returning an array of all the values that pass a truth test (iterator). If
	 * iterator isn't provided, each value will be evaluated as a boolean.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to filter
	 * @param callable $iterator the filtering function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function filter( $list, callable $iterator = null, $context = null ) {
		if ( empty( $list ) ) {
			return [];
		}

		static::associate( $iterator, $context );

		! $iterator && $iterator = function ( $item ) {
			return (boolean) $item;
		};

		$result = [];
		static::each( $list, function ( $item, $index ) use ( $iterator, & $result ) {
			if ( $iterator( $item ) ) {
				$result[ $index ] = $item;
			}
		} );

		return $result;
	}

	/**
	 * Looks through each value in the list, returning an array of all the values that contain all of the key-value
	 * pairs listed in properties.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to filter
	 * @param traversable $properties the key-values pairs each filtered item must match
	 *
	 * @return array
	 */
	public static function where( $list, $properties ) {
		return static::filter( $list, static::getListfilter( $properties ) );
	}

	/**
	 * Looks through the list and returns the first value that matches all of the key-value pairs listed in properties.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to filter
	 * @param traversable $properties the key-values pairs each filtered item must match
	 *
	 * @return mixed
	 */
	public static function findWhere( $list, $properties ) {
		return static::find( $list, static::getListFilter( $properties ) );
	}

	/**
	 * Returns the values in list without the elements that the truth test (iterator) passes. The opposite of filter.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to filter
	 * @param callable $iterator the truth-test function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function reject( $list, callable $iterator, $context = null ) {
		static::associate( $iterator, $context );

		$result = [];
		static::each( $list, function ( $item, $index ) use ( $iterator, & $result ) {
			if ( ! $iterator( $item ) ) {
				$result[ $index ] = $item;
			}
		} );

		return $result;
	}

	/**
	 * @see Underscore::every
	 */
	public static function all( $list, callable $iterator = null, $context = null ) {
		return static::every( $list, $iterator, $context );
	}

	/**
	 * Returns true if all of the values in the list pass the iterator truth test. Short-circuits and stops traversing
	 * the list if a false element is found.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to filter
	 * @param callable $iterator the truth-test function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return bool
	 */
	public static function every( $list, callable $iterator = null, $context = null ) {
		static::associate( $iterator, $context );

		! $iterator && $iterator = function ( $item ) {
			return static::identity( $item );
		};

		$result = true;
		static::each( $list, function ( $item, $index ) use ( $iterator, & $result ) {
			if ( ! $iterator( $item ) ) {
				$result = false;

				return static::BREAKER;
			}
		} );

		return $result;
	}

	/**
	 * @see Underscore::some
	 */
	public static function any( $list, callable $iterator = null, $context = null ) {
		return static::some( $list, $iterator, $context );
	}

	/**
	 * Returns true if any of the values in the list pass the iterator truth test. Short-circuits and stops traversing
	 * the list if a true element is found.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to filter
	 * @param callable $iterator the truth-test function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return bool
	 */
	public static function some( $list, callable $iterator = null, $context = null ) {
		static::associate( $iterator, $context );

		! $iterator && $iterator = function ( $item ) {
			return static::identity( $item );
		};

		return ! static::every( $list, function ( $item ) use ( $iterator ) {
			return ! $iterator( $item );
		} );
	}

	/**
	 * @see Underscore::contains
	 */
	public static function includes( $list, $value ) {
		return static::contains( $list, $value );
	}

	/**
	 * Returns true if the value is present in the list.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items
	 * @param mixed $value the value to look for
	 * @param boolean $strict type of value is also used in coparision
	 *
	 * @return array
	 */
	public static function contains( $list, $value, $strict = false ) {
		return static::some( $list, function ( $item ) use ( $value, $strict ) {
			return $strict ? $item === $value : $item == $value;
		} );
	}

	/**
	 * Calls the method named by methodName on each value in the list. Any extra arguments passed to invoke will be
	 * forwarded on to the method invocation. If your list items are arrays (instead of objects) methods from
	 * ArrayObject can be used (like asort). If the wanted method is not found on the current item during iteration,
	 * the item will be left untouched.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to invoke method/function onto
	 * @param callable,string $methodName the name of the method to invoke or a closure
	 * @param array $arguments the method's arguments
	 *
	 * @return array
	 */
	public static function invoke( $list, $methodName, $arguments = array() ) {
		if ( func_num_args() > 2 && ! is_array( $arguments ) ) {
			$arguments = array_slice( func_get_args(), 2 );
		}

		return static::map( $list, function ( $item ) use ( $methodName, $arguments ) {
			if ( is_scalar( $item ) || is_resource( $item ) ) {
				return $item;
			}

			if ( $cast_down = static::isArray( $item, true ) ) {
				$item = new \ArrayObject( $item );
			}

			if ( static::isString( $methodName, true ) && method_exists( $item, $methodName ) ) {
				$methodName = [ $item, $methodName ];
			} elseif ( $methodName instanceof \Closure ) {
				$methodName = $methodName->bindTo( $item );
			} else {
				return $item;
			}

			call_user_func_array( $methodName, $arguments );

			if ( $cast_down ) {
				$item = $item->getArrayCopy();
			}

			return $item;
		} );
	}

	/**
	 * A convenient version of what is perhaps the most common use-case for map: extracting a list of property values.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items
	 * @param string $propertyName the name of the property to extract from each item
	 *
	 * @return array
	 */
	public static function pluck( $list, $propertyName ) {
		return static::map( $list, function ( $item ) use ( $propertyName ) {
			return static::get( $item, $propertyName );
		} );
	}

	/**
	 * Returns the maximum value in list. If iterator is passed, it will be used on each value to generate the
	 * criterion by which the value is ranked.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items
	 * @param callable $iterator optional, the comparision function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return mixed
	 */
	public static function max( $list, callable $iterator = null, $context = null ) {
		static::associate( $iterator, $context );

		! $iterator && $iterator = function ( $item ) {
			return static::identity( $item );
		};

		$max    = null;
		$result = null;
		static::each( $list, function ( $item ) use ( $iterator, & $max, & $result ) {
			$num = $iterator( $item );
			if ( ! isset( $max ) || $num > $max ) {
				list( $result, $max ) = [ $item, $num ];
			}
		} );

		return $result;
	}

	/**
	 * Returns the minimum value in list. If iterator is passed, it will be used on each value to generate the
	 * criterion by which the value is ranked.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items
	 * @param callable $iterator optional, the comparision function
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return mixed
	 */
	public static function min( $list, callable $iterator = null, $context = null ) {
		static::associate( $iterator, $context );

		! $iterator && $iterator = function ( $item ) {
			return static::identity( $item );
		};

		$min    = null;
		$result = null;
		static::each( $list, function ( $item ) use ( $iterator, & $min, & $result ) {
			$num = $iterator( $item );
			if ( ! isset( $min ) || $num < $min ) {
				list( $result, $min ) = [ $item, $num ];
			}
		} );

		return $result;
	}

	/**
	 * Returns a (stably) sorted copy of list, ranked in ascending order by the results of running each value
	 * through iterator. Returns NULL in case of error.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to sort
	 * @param callable $iterator the function that generates the criteria by which items are sorted
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function sortBy( $list, callable $iterator, $context = null ) {
		static::associate( $iterator, $context );

		$result = static::map( $list, function ( $value ) use ( $iterator ) {
			return [ 'value' => $value, 'criteria' => $iterator( $value ) ];
		} );

		return uasort( $result, function ( $left, $right ) {
			if ( $left['criteria'] == $right['criteria'] ) {
				return 0;
			}

			return $left['criteria'] < $right['criteria'] ? - 1 : 1;
		} ) ? static::pluck( $result, 'value' ) : null;
	}

	/**
	 * Given a list, and an iterator function that returns a key for each element in the list (or a property name),
	 * returns an object with an index of each item. Just like groupBy, but for when you know your keys are unique.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to index
	 * @param callable,scalar $iterator the function to generate the key or a property name
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function indexBy( $list, $iterator, $context = null ) {
		static::associate( $iterator, $context );

		$result = [];
		static::each( $list, function ( $item ) use ( $iterator, & $result ) {
			static::isScalar( $iterator )
				? $result[ static::get( $item, $iterator ) ] = $item
				: $result[ $iterator( $item ) ] = $item;
		} );

		return $result;
	}

	/**
	 * Splits a collection into sets, grouped by the result of running each value through iterator. If iterator is a
	 * string instead of a function, groups by the property named by iterator on each of the values.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to group
	 * @param callable,scalar $iterator the function to generate the key or a property name
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function groupBy( $list, $iterator, $context = null ) {
		static::associate( $iterator, $context );

		$result = [];
		static::each( $list, function ( $item ) use ( $iterator, & $result ) {
			static::isScalar( $iterator )
				? $result[ static::get( $item, $iterator ) ][] = $item
				: $result[ $iterator( $item ) ][] = $item;
		} );

		return $result;
	}

	/**
	 * Sorts a list into groups and returns a count for the number of objects in each group. Similar to groupBy, but
	 * instead of returning a list of values, returns a count for the number of values in that group.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to group and count
	 * @param callable,scalar $iterator the function to generate the key or a property name
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function countBy( $list, $iterator, $context = null ) {
		return static::map( static::groupBy( $list, $iterator, $context ), function ( $item ) {
			return static::size( $item );
		} );
	}

	/**
	 * Returns a shuffled copy of the list.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items to shuffle
	 *
	 * @return array
	 */
	public static function shuffle( $list ) {
		$list = static::toArray( $list );

		return shuffle( $list ) ? $list : null;
	}

	/**
	 * Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a
	 * single random item will be returned.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items
	 * @param int $n optional, the number of items to pick
	 *
	 * @return array
	 */
	public static function sample( $list, $n = 1 ) {
		return $n == 1
			? static::get( $list, static::keys( $list )[ static::random( static::size( $list ) - 1 ) ] )
			: array_slice( static::shuffle( $list ), 0, $n );
	}

	/**
	 * Creates a real Array from the list (anything that can be iterated over). This method will also accept scalars
	 * such as string, number and even null and will *cast* them into arrays, for instance Underscore::toArray(null)
	 * is [] altough Underscore::toArray('a') is ['a'].
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items
	 *
	 * @return array
	 */
	public static function toArray( $list ) {
		if ( func_num_args() > 1 ) {
			return func_get_args();
		}

		if ( static::isArray( $list, true ) ) {
			return $list;
		}

		if ( $list instanceof \ArrayObject || $list instanceof \ArrayIterator ) {
			return $list->getArrayCopy();
		}

		if ( $list instanceof \Traversable ) {
			return iterator_to_array( $list, true );
		}

		return (array) $list;
	}

	/**
	 * Return the number of values in the list. This method will also accept scalars such as string, number and even
	 * null or resources but will return 1 in that case.
	 *
	 * @category Collection Functions
	 *
	 * @param traversable $list the list of items
	 *
	 * @return int
	 */
	public static function size( $list ) {
		if ( $list === null ) {
			return 0;
		}

		if ( is_scalar( $list ) || is_resource( $list ) ) {
			return 1;
		}

		if ( is_array( $list ) || $list instanceof \Countable ) {
			return count( $list );
		}

		if ( $list instanceof \Traversable ) {
			return iterator_count( $list );
		}

		$count = 0;
		static::each( $list, function () use ( & $count ) {
			$count ++;
		} );

		return $count;
	}

	/**
	 * Split a collection into two arrays: one whose elements all satisfy the given predicate, and one whose elements
	 * all do not satisfy the predicate.
	 *
	 * @param  traversable $list the list of items
	 * @param  callable $iterator the predicate
	 * @param  object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function partition( $list, callable $iterator, $context = null ) {
		static::associate( $iterator, $context );

		$pass = [];
		$fail = [];
		static::each( $list, function ( $item ) use ( & $pass, & $fail, $iterator ) {
			$iterator( $item ) ? $pass[] = $item : $fail[] = $item;
		} );

		return [ $pass, $fail ];
	}

	/**
	 * Array Functions
	 * ---------------
	 */

	/**
	 * @see Underscore::first
	 */
	public static function head( $array, $n = 1 ) {
		return static::first( $array, $n );
	}

	/**
	 * @see Underscore::first
	 */
	public static function take( $array, $n = 1 ) {
		return static::first( $array, $n );
	}

	/**
	 * Returns the first element of an array. Passing n will return the first n elements of the array. Passing guard
	 * will force the returned value to be an array.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param int $n optional, the number of items to pick
	 * @param bool $guard optional, true to always return an array
	 *
	 * @throws UnexpectedValueException if $n is negative or zero
	 *
	 * @return mixed,array
	 */
	public static function first( $array, $n = 1, $guard = false ) {
		if ( empty( $array ) ) {
			return;
		}

		if ( $n <= 0 ) {
			throw new \UnexpectedValueException( "invalid number of items $n" );
		}

		$result = [];
		static::each( $array, function ( $item ) use ( & $n, & $result ) {
			$result[] = $item;

			if ( -- $n == 0 ) {
				return static::BREAKER;
			}
		} );

		return $guard || isset( $result[1] ) ? $result : $result[0];
	}

	/**
	 * Returns everything but the last entry of the array. Pass n to exclude the last n elements from the result.
	 * Passing guard will force the returned value to be an array.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param int $n optional, the number of items to exclude
	 * @param bool $guard optional, true to always return an array
	 *
	 * @return mixed,array
	 */
	public static function initial( $array, $n = 1, $guard = false ) {
		return static::first( $array, static::size( $array ) - $n, $guard );
	}

	/**
	 * Returns the last element of an array. Passing n will return the last n elements of the array. Passing guard
	 * will force the returned value to be an array.
	 *
	 * @category Array Functions
	 *
	 * @param traversabel $array the list of items
	 * @param int $n optional, the number of items to pick
	 * @param bool $guard optional, true to always return an array
	 *
	 * @return mixed,array
	 */
	public static function last( $array, $n = 1, $guard = false ) {
		$array  = static::toArray( $array );
		$result = array_values( array_slice( $array, - $n ) );

		return $guard || isset( $result[1] ) ? $result : $result[0];
	}

	/**
	 * @see Underscore::rest
	 */
	public static function tail( $array, $index = 1, $guard = false ) {
		return static::rest( $array, $index, $guard );
	}

	/**
	 * @see Underscore::rest
	 */
	public static function drop( $array, $index = 1, $guard = false ) {
		return static::rest( $array, $index, $guard );
	}

	/**
	 * Returns the rest of the elements in an array. Pass an index to return the values of the array from that index
	 * onward. Passing guard will force the returned value to be an array.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param int $index optional, the index from which the items are picked
	 * @param bool $guard optional, true to always return an array
	 *
	 * @return mixed,array
	 */
	public static function rest( $array, $index = 1, $guard = false ) {
		return static::last( $array, - $index, $guard );
	}

	/**
	 * Returns a copy of the array with all falsy values removed. In PHP, false, null, 0, "", array() and "0" are all
	 * falsy.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 *
	 * @return array
	 */
	public static function compact( $array ) {
		return static::filter( $array, function ( $item ) {
			return static::identity( $item );
		} );
	}

	/**
	 * Flattens a nested array (the nesting can be to any depth). If you pass shallow, the array will only be
	 * flattened a single level.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param bool $shallow optional, if true will only flatten on single level
	 *
	 * @return array
	 */
	public static function flatten( $array, $shallow = false ) {
		if ( empty( $array ) ) {
			return [];
		}

		$output  = [];
		$flatten = function ( $input, $shallow ) use ( & $output, & $flatten ) {
			if ( $shallow && static::every( $input, 'is_array' ) ) {
				return call_user_func_array( 'array_merge', static::toArray( $input ) );
			}

			static::each( $input, function ( $item ) use ( $shallow, & $output, & $flatten ) {
				if ( static::isTraversable( $item ) ) {
					$shallow
						? ( $output = array_merge( $output, array_values( static::toArray( $item ) ) ) )
						: $flatten( $item, $shallow );
				} else {
					$output[] = $item;
				}
			} );

			return $output;
		};

		return $flatten( $array, $shallow );
	}

	/**
	 * Returns a copy of the array with all instances of the values removed.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param array,mixed $values multiple, the value(s) to exclude
	 *
	 * @return array
	 */
	public static function without( $array, $values ) {
		if ( ! is_array( $values ) ) {
			$values = array_slice( func_get_args(), 1 );
		}

		return static::difference( $array, $values );
	}

	/**
	 * @see Underscore::uniq
	 */
	public static function unique( $array, $isSorted = false, callable $iterator = null, $context = null ) {
		return static::uniq( $array, $isSorted, $iterator, $context );
	}

	/**
	 * Produces a duplicate-free version of the array, using === to test object equality. If you know in advance that
	 * the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique
	 * items based on a transformation, pass an iterator function.
	 *
	 * WARNING: this function's cyclomatic complexity is (at least) quadratic ! using it with large arrays
	 * (> 1000 items) can be very slow and memory consuming.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param bool $isSorted optional, use a faster algorithm if the list is already sorted
	 * @param callable $iterator optional, the comparision function if needed
	 * @param object $context optional, if provided will become the context of $iterator
	 *
	 * @return array
	 */
	public static function uniq( $array, $isSorted = false, callable $iterator = null, $context = null ) {
		if ( empty( $array ) ) {
			return [];
		}

		// run a much faster algorithm if a transformation is not needed
		if ( is_array( $array ) && ! $iterator ) {
			return array_values( array_unique( $array ) );
		}

		$initial = $iterator ? static::map( $array, $iterator, $context ) : $array;
		$result  = [];

		static::each( $initial, function ( $value, $index ) use ( & $array, $isSorted, & $result ) {
			static $seen = [];

			if ( $isSorted ) {
				if ( $seen[0] !== $value ) {
					$result[] = static::get( $array, $index );
					$seen[0]  = $value;
				}
			} else {
				if ( ! in_array( $value, $seen, true ) ) {
					$result[] = static::get( $array, $index );
					$seen[]   = $value;
				}
			}
		} );

		return $result;
	}

	/**
	 * Computes the union of the passed-in arrays: the list of unique items, in order, that are present in one or more
	 * of the arrays.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array multiple, the arrays to join
	 *
	 * @return array
	 */
	public static function union() {
		return static::uniq( static::flatten( func_get_args(), true ) );
	}

	/**
	 * Computes the list of values that are the intersection of all the arrays. Each value in the result is present in
	 * each of the arrays.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array multiple, the arrays to intersect
	 *
	 * @return array
	 */
	public static function intersection( $array ) {
		if ( $array == null ) {
			return [];
		}

		$args       = func_get_args();
		$argsLength = func_num_args();

		if ( $argsLength == 1 ) {
			return $array;
		}

		if ( static::every( $args, 'is_array' ) ) {
			return array_values( call_user_func_array( 'array_intersect', $args ) );
		}

		$arrayLenght = static::size( $array );
		$result      = [];
		static::each( $array, function ( $value, $index ) use ( & $result, $args, $argsLength ) {
			if ( static::contains( $result, $value ) ) {
				return;
			}

			for ( $i = 1; $i < $argsLength; $i ++ ) {
				if ( ! static::contains( $args[ $i ], $value ) ) {
					break;
				}
			}

			if ( $i == $argsLength ) {
				$result[] = $value;
			}
		} );

		return $result;
	}

	/**
	 * Similar to without, but returns the values from array that are not present in the other arrays.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array multiple, the arrays to difference
	 *
	 * @return array
	 */
	public static function difference( $array ) {
		if ( empty( $array ) ) {
			return [];
		}

		if ( func_num_args() == 1 ) {
			return $array;
		}

		$args = func_get_args();

		if ( static::every( $args, 'is_array' ) ) {
			return array_values( call_user_func_array( 'array_diff', $args ) );
		}

		$rest = static::flatten( array_slice( $args, 1 ), true );

		return static::values( static::filter( $array, function ( $value ) use ( $rest ) {
			return ! static::contains( $rest, $value );
		} ) );
	}

	/**
	 * Merges together the values of each of the arrays with the values at the corresponding position. Useful when you
	 * have separate data sources that are coordinated through matching array indexes.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array multiple, the arrays to zip
	 *
	 * @return array
	 */
	public static function zip() {
		$arguments = func_get_args();
		$length    = func_num_args();
		$result    = [];
		for ( $i = 0; $i < $length; $i ++ ) {
			$result[ $i ] = static::pluck( $arguments, $i );
		}

		return $result;
	}

	/**
	 * Converts arrays into objects. Pass either a single list of [key, value] pairs, or a list of keys, and a list of
	 * values. If duplicate keys exist, the last value wins.
	 *
	 * @category Array Functions
	 *
	 * @param array $list the properties
	 * @param array $values optional, the values, if not provided each item of $list is used a pair
	 *
	 * @return object
	 */
	public static function obj( array $list, array $values = null ) {
		if ( ! $list ) {
			return (object) [];
		}

		$result = new \stdClass;
		$length = count( $list );
		$list   = array_values( $list );
		for ( $i = 0; $i < $length; $i ++ ) {
			$values
				? $result->$list[ $i ] = $values[ $i ]
				: $result->$list[ $i ][0] = $list[ $i ][1];
		}

		return $result;
	}

	/**
	 * Returns the index at which value can be found in the array, or -1 if value is not present in the array. This
	 * method uses array_search internally and is not optimized for long array binary search.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param mixed $item the value to look for
	 *
	 * @return mixed
	 */
	public static function indexOf( $array, $item ) {
		if ( is_array( $array ) ) {
			return ( $key = array_search( $item, $array ) ) !== false ? $key : - 1;
		}

		$found  = - 1;
		$search = $item;
		static::each( $array, function ( $item, $key ) use ( $search, & $found ) {
			if ( $item == $search ) {
				$found = $key;

				return static::BREAKER;
			}
		} );

		return $found;
	}

	/**
	 * Returns the index of the last occurrence of value in the array, or -1 if value is not present. This method uses
	 * array_keys internally and is not optimized for long array binary search.
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param mixed $item the value to look for
	 *
	 * @return mixed
	 */
	public static function lastIndexOf( $array, $item ) {
		if ( is_array( $array ) ) {
			return ( $keys = array_keys( $array, $item, false ) ) ? array_pop( $keys ) : - 1;
		}

		$found  = - 1;
		$search = $item;
		static::each( $array, function ( $item, $key ) use ( $search, & $found ) {
			if ( $item == $search ) {
				$found = $key;
			}
		} );

		return $found;
	}

	/**
	 * Uses a binary search to determine the index at which the value should be inserted into the list in order
	 * to maintain the list's sorted order. If an iterator is passed, it will be used to compute the sort ranking of
	 * each value, including the value you pass. Iterator may also be the string name of the property to sort by (eg.
	 * length).
	 *
	 * @category Array Functions
	 *
	 * @param traversable $array the list of items
	 * @param mixed $value the value to find the index for
	 * @param callable,scalar $iterator optional, the function by which a value is evaluated or a property's name
	 * @param object $context optional, if provided will become the context for $iterator
	 *
	 * @return int
	 */
	public static function sortedIndex( $array, $value, $iterator = null, $context = null ) {
		static::associate( $iterator, $context );

		if ( ! is_array( $array ) && ! $array instanceof \ArrayAccess ) {
			$array = static::toArray( $array );
		}

		$iterator = $iterator === null
			? function ( $item ) {
				return static::identity( $item );
			}
			: static::getLookupIterator( $iterator );

		$value = $iterator( $value );
		$low   = 0;
		$high  = static::size( $array );

		while ( $low < $high ) {
			$mid = ( $low + $high ) >> 1;
			$iterator( $array[ $mid ] ) < $value ? $low = $mid + 1 : $high = $mid;
		}

		return $low;
	}

	/**
	 * A function to create flexibly-numbered lists of integers, handy for each and map loops. start, if omitted,
	 * defaults to 0; step defaults to 1. Returns a list of integers from start to stop, incremented (or decremented)
	 * by step, exclusive. This method uses range internally.
	 *
	 * @category Array Functions
	 *
	 * @param int $start the starting index
	 * @param int $stop optional, the ending index
	 * @param int $step optional, the iteration step
	 *
	 * @return array
	 */
	public static function range( $start, $stop = null, $step = 1 ) {
		if ( func_num_args() <= 1 ) {
			$stop  = $start ?: 0;
			$start = 0;
		}

		return range( $start, $stop, $step );
	}

	/**
	 * Function (uh, ahem) Functions
	 * -----------------------------
	 */

	/**
	 * Wrap the first function inside of the wrapper function, passing it as the first argument. This allow the
	 * wrapper to execute code before and after the function runs, adjust the arguments and execute it conditionnaly.
	 * Arguments are passed along to the wrapper function.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param callable $function the function
	 *
	 * @return closure
	 */
	public static function wrap( callable $function, callable $wrapper ) {
		return function () use ( $function, $wrapper ) {
			$args = func_get_args();
			array_unshift( $args, $function );

			return call_user_func_array( $wrapper, $args );
		};
	}

	/**
	 * Returns a new negated version of the predicate function.
	 *
	 * @since 0.2.0
	 * @category Function (uh, ahem) Functions
	 *
	 * @param  callable $function the function
	 *
	 * @return closure
	 */
	public static function negate( callable $function ) {
		return function () use ( $function ) {
			return ! call_user_func_array( $function, func_get_args() );
		};
	}

	/**
	 * Returns the composition of a list of functions, where each function consumes the return value of the function
	 * that follows. In math terms, composing the functions f(), g(), and h() produces f(g(h())).
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param callable $functions multiple, the functions to compose
	 *
	 * @return closure
	 */
	public static function compose( $functions ) {
		if ( ! is_array( $functions ) ) {
			$functions = func_get_args();
		}

		return function () use ( $functions ) {
			$args = func_get_args();
			foreach ( $functions as $function ) {
				$args = call_user_func_array( $function, (array) $args );
			}

			return $args;
		};
	}

	/**
	 * Creates a version of the function that will only be run after first being called count times. Please note that
	 * the function shall not recieve parameters.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param int $count the number of times the $function shall be executed
	 * @param callable $function the function
	 *
	 * @return closure
	 */
	public static function after( $count, callable $function ) {
		return function () use ( & $count, $function ) {
			if ( -- $count > 0 ) {
				return;
			}

			return call_user_func_array( $function, func_get_args() );
		};
	}

	/**
	 * Creates a version of the function that can be called no more than count times. The result of the last function
	 * call is memoized and returned when count has been reached.
	 *
	 * @since 0.2.0
	 * @category Function (uh, ahem) Functions
	 *
	 * @param  int $count the number of times the $function shall be executed
	 * @param  callable $function the function
	 *
	 * @return closure
	 */
	public static function before( $count, callable $function ) {
		return function () use ( & $count, $function ) {
			static $memo;
			if ( -- $count > 0 ) {
				$memo = call_user_func_array( $function, func_get_args() );
			}

			return $memo;
		};
	}

	/**
	 * Creates a version of the function that can only be called one time. Repeated calls to the modified function
	 * will have no effect, returning the value from the original call. Useful for initialization functions, instead
	 * of having to set a boolean flag and then check it later.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param callable $function the function
	 *
	 * @return closure
	 */
	public static function once( callable $function ) {
		return static::before( 2, $function );
	}

	/**
	 * Partially apply a function by filling in any number of its arguments. Not all the arguments have to be present
	 * on the partial construction.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param callable $function the function
	 * @param array,mixed $arguments multiple, the arguments
	 *
	 * @return closure
	 */
	public static function partial( callable $function, $arguments ) {
		if ( ! is_array( $arguments ) ) {
			$arguments = array_slice( func_get_args(), 1 );
		}

		return function () use ( $function, $arguments ) {
			return call_user_func_array( $function, array_merge( func_get_args(), $arguments ) );
		};
	}

	/**
	 * Bind a function to an object, meaning that whenever the function is called, the value of $this will be the
	 * object. Optionally, pass arguments to the function to pre-fill them, also known as partial application.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param closure $function the function
	 * @param object $object the object to bind the closure to
	 *
	 * @throws InvalidArgumentException if $object is not an object
	 * @throws RuntimeException if the closure cannot be bound to $object (static closures)
	 *
	 * @return closure
	 */
	public static function bind( \Closure $function, $object ) {
		if ( ! is_object( $object ) ) {
			throw new \InvalidArgumentException( "not an object" );
		}

		// ~ here be dragons ~
		set_error_handler( function ( $errno ) use ( & $warning ) {
			$warning = $errno == E_WARNING;
		} );
		@$reset_error_stack; // intentionnal E_NOTICE with undefined variable
		$function = @\Closure::bind( $function, $object );
		restore_error_handler();

		// detect the 'Warning: Cannot bind an instance to a static closure' PHP error
		if ( $warning ) {
			throw new \RuntimeException( "cannot bind an instance to static closure" );
		}

		if ( func_num_args() <= 2 ) {
			return $function;
		}

		$args = func_get_args();

		return static::partial( $function, is_array( $args[2] ) ? $args[2] : array_slice( $args, 2 ) );
	}

	/**
	 * Bind a function to a class, meaning that whenever the function is called, the value of self or static will be
	 * the class. Optionally, pass arguments to the function to pre-fill them, also known as partial application.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param closure $function the function
	 * @param object,string $class the object or classname to bind the closure to
	 *
	 * @throws InvalidArgumentException if $class does not exists
	 * @throws RuntimeException if the closure cannot be bound to $class
	 *
	 * @return closure
	 */
	public static function bindClass( \Closure $function, $class ) {
		if ( ! class_exists( $class ) ) {
			throw new \InvalidArgumentException( "no such class $class" );
		}

		// assigning a class context for a closure is never an issue
		$function = \Closure::bind( $function, null, $class );

		if ( func_num_args() <= 2 ) {
			return $function;
		}

		$args = func_get_args();

		return static::partial( $function, is_array( $args[2] ) ? $args[2] : array_slice( $args, 2 ) );
	}

	/**
	 * Binds a number of methods on the object, specified by methodNames, to be run in the context of that object
	 * whenever they are invoked. Very handy for binding functions that are going to be used as event handlers, which
	 * would otherwise be invoked with a fairly useless this. methodNames are required. Keep in mind PHP doesn't
	 * allow to call a closure property value like a method, for instance $o->myClosure(), given $o is an instance of
	 * stdClass, won't work.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param object $object the object
	 * @param array,callable $methodNames multiple, the functions to attach
	 *
	 * @return object,string
	 */
	public static function bindAll( $object, $methodNames ) {
		if ( ! is_array( $methodNames ) ) {
			$methodNames = array_slice( func_get_args(), 1 );
		}

		foreach ( $methodNames as $methodName ) {
			if ( isset( $object->$methodName ) ) {
				$object->$methodName = static::bind( $object->$methodName, $object );
			}
		}

		return $object;
	}

	/**
	 * Memoizes a given function by caching the computed result. Useful for speeding up slow-running computations. If
	 * passed an optional hashFunction, it will be used to compute the hash key for storing the result, based on the
	 * arguments to the original function. The default hashFunction just uses the first argument to the memoized
	 * function as the key.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param callable $function the function to memoize
	 * @param callable $hashFunction optional, if provided will be used to hash $function's results
	 * @param array,ArrayAccess $cache optional, function's results cache
	 *
	 * @return closure
	 */
	public static function memoize( callable $function, callable $hashFunction = null, & $cache = null ) {
		$hashFunction = $hashFunction ?: function ( $arguments ) {
			return array_shift( $arguments );
		};

		if ( $cache && ! ( is_array( $cache ) || $cache instanceOf \ArrayAccess ) ) {
			throw new \InvalidArgumentException( "invalid cache type" );
		}

		$cache = $cache ?: [];

		return function () use ( $function, $hashFunction, & $cache ) {
			$args = func_get_args();
			$ckey = $hashFunction( $args );

			return array_key_exists( $ckey, $cache )
				? $cache[ $ckey ]
				: $cache[ $ckey ] = call_user_func_array( $function, $args );
		};
	}

	/**
	 * Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only
	 * actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events
	 * that occur faster than you can keep up with.
	 *
	 * @category Function (uh, ahem) Functions
	 *
	 * @param callable $function the function
	 * @param int $wait the time to wait between each call (in milliseconds)
	 *
	 * @return closure
	 */
	public static function throttle( callable $function, $wait ) {
		return function () use ( $function, $wait ) {
			static $pretime;

			$curtime = microtime( true );
			if ( ! $pretime || ( $curtime - $pretime ) >= ( $wait / 1000 ) ) {
				$pretime = $curtime;
				$function();
			}
		};
	}

	/**
	 * Call (execute) the given function, optionnaly bound to $context, with the given arguments and return its result.
	 *
	 * @since 0.2.0
	 * @category Function (uh, ahem) Functions
	 *
	 * @param callable $function the function
	 * @param mixed $arg ... optional, multiple, the function arguments
	 * @param object $context the function's context
	 *
	 * @return mixed
	 */
	public static function call( callable $function, $context = null ) {

		return static::apply( $function, $context, array_slice( func_get_args(), 2 ) );
	}

	/**
	 * Call (execute) the given function, optionnaly bound to $context, with the given argument list and return its
	 * result.
	 *
	 * @since 0.2.0
	 * @category Function (uh, ahem) Functions
	 *
	 * @param callable $function the function
	 * @param list $arguments the arguments
	 * @param object $context the function's context
	 *
	 * @return mixed
	 */
	public static function apply( callable $function, $context = null, $arguments = [] ) {
		static::associate( $function, $context );

		return call_user_func_array( $function, static::values( $arguments ) );
	}

	/**
	 * Object Functions
	 * ----------------
	 */

	/**
	 * Retrieve all the names of the object's properties.
	 *
	 * @category Object Functions
	 *
	 * @param traversable $object the list from which the keys are extracted
	 *
	 * @return array
	 */
	public static function keys( $object ) {
		if ( $object instanceof \stdClass ) {
			$object = (array) $object;
		}

		if ( is_array( $object ) ) {
			return array_keys( $object );
		}

		$result = [];
		static::each( $object, function ( $item, $key ) use ( & $result ) {
			$result[] = $key;
		} );

		return $result;
	}

	/**
	 * Return all of the values of the object's properties.
	 *
	 * @category Object Functions
	 *
	 * @param traversable $object the list from which the values are extracted
	 *
	 * @return array
	 */
	public static function values( $object ) {
		if ( $object instanceof \stdClass ) {
			$object = (array) $object;
		}

		if ( is_array( $object ) ) {
			return array_values( $object );
		}

		$result = [];
		static::each( $object, function ( $item ) use ( & $result ) {
			$result[] = $item;
		} );

		return $result;
	}

	/**
	 * Convert an object into a list of [key, value] pairs.
	 *
	 * @category Object Functions
	 *
	 * @param traversable $object the list of items to convert to pairs
	 *
	 * @return array
	 */
	public static function pairs( $object ) {
		$result = [];
		static::each( $object, function ( $item, $key ) use ( & $result ) {
			$result[] = [ $key, $item ];
		} );

		return $result;
	}

	/**
	 * Returns a copy of the object where the keys have become the values and the values the keys. For this to work,
	 * all of your object's values should be unique and string serializable.
	 *
	 * @category Object Functions
	 *
	 * @param traversable $object the object to invert
	 *
	 * @return object,array
	 */
	public static function invert( $object ) {
		if ( ! is_array( $object ) ) {
			$castDown = (boolean) $object = static::toArray( $object );
		}

		return ! empty( $castDown ) ? (object) array_flip( $object ) : array_flip( $object );
	}

	/**
	 * @see Underscore::functions
	 */
	public static function methods( $object ) {
		return static::functions( $object );
	}

	/**
	 * Returns a sorted list of the names of every method in an object — that is to say, the name of every function
	 * property of the object.
	 *
	 * @category Object Functions
	 *
	 * @param traversable,object $object the object to extract the functions from
	 *
	 * @return array
	 */
	public static function functions( $object ) {
		return ( is_array( $object ) || $object instanceOf \stdClass )
			? static::keys( static::filter( $object, function ( $item ) {
				return static::isFunction( $item );
			} ) )
			: static::sortBy( get_class_methods( $object ), function ( $name ) {
				return $name;
			} );
	}

	/**
	 * Copy all of the properties in the source objects over to the destination object, and return the destination
	 * object. It's in-order, so the last source will override properties of the same name in previous arguments.
	 *
	 * @category Object Functions
	 *
	 * @param object,array $destination the destination object
	 * @param object,array $sources multiple, the source objects
	 *
	 * @return object,array
	 */
	public static function extend( $destination, $sources ) {
		$sources = array_slice( func_get_args(), 1 );

		static::each( $sources, function ( $source ) use ( & $destination ) {
			static::each( $source, function ( $value, $name ) use ( & $destination ) {
				static::set( $destination, $name, $value );
			} );
		} );

		return $destination;
	}

	/**
	 * Returns a copy of the object, filtered to only have values for the whitelisted keys (or array of valid keys). If
	 * provided object is an object (in the broadest sense), a stdClass instance is returned, otherwise an array is
	 * returned.
	 *
	 * @category Object Functions
	 *
	 * @param traversable $object the object to pick properties on
	 * @param array,scalar $keys multiple, the keys to pick
	 *
	 * @return object,array
	 */
	public static function pick( $object, $keys ) {
		if ( ! is_array( $keys ) ) {
			$keys = array_slice( func_get_args(), 1 );
		}

		$result = is_object( $object )
			? new \stdClass
			: [];

		static::each( $keys, function ( $key ) use ( $object, & $result ) {
			if ( static::has( $object, $key ) ) {
				static::set( $result, $key, static::get( $object, $key ) );
			}
		} );

		return $result;
	}

	/**
	 * Return a copy of the object, filtered to omit the blacklisted keys (or array of keys). If provided object is an
	 * object (in the broadest sense), a stdClass instance is returned, otherwise an array is returned.
	 *
	 * @category Object Functions
	 *
	 * @param traversable $object the object to exclude keys from
	 * @param array,scalar $keys multiple, the keys to omit
	 *
	 * @return object,array
	 */
	public static function omit( $object, $keys ) {
		if ( ! is_array( $keys ) ) {
			$keys = array_slice( func_get_args(), 1 );
		}

		$result = is_object( $object )
			? new \stdClass
			: [];

		$keys = array_flip( static::toArray( $keys ) );
		static::each( $object, function ( $value, $name ) use ( $keys, & $result ) {
			if ( ! isset( $keys[ $name ] ) ) {
				static::set( $result, $name, $value );
			}
		} );

		return $result;
	}

	/**
	 * Fill in null properties in object with values from the defaults objects, and return the object. As soon as the
	 * property is filled, further defaults will have no effect.
	 *
	 * @category Object Functions
	 *
	 * @param traversable $object the object to fill
	 * @param traversable $defaults multiple, the objects or array that will fill object's missing keys
	 *
	 * @throws InvalidArgumentException if one of the $defaults object is not traversable
	 *
	 * @return object
	 */
	public static function defaults( $object, $defaults ) {
		$defaults = array_slice( func_get_args(), 1 );

		if ( $object instanceof \stdClass ) {
			$object   = (array) $object;
			$castDown = true;
		}

		$result  = $object;
		$isArray = is_array( $object );
		static::each( $defaults, function ( $default ) use ( & $result, $isArray ) {
			if ( ! static::isTraversable( $default ) ) {
				throw new \InvalidArgumentException( "cannot use " . static::typeOf( $default ) . " as default" );
			}

			if ( $isArray ) {
				return $result += static::toArray( $default );
			}

			static::each( $default, function ( $item, $index ) use ( & $result ) {
				if ( ! static::has( $result, $index ) ) {
					static::set( $result, $index, $item );
				}
			} );
		} );

		return ! empty( $castDown ) ? (object) $result : $result;
	}

	/**
	 * @see Underscore::duplicate
	 */
	public static function copy( $object ) {
		return static::duplicate( $object );
	}

	/**
	 * Create a shallow-copied clone of the object. Any nested objects or arrays will be copied by reference, not
	 * duplicated. This method is safe to use with arrays.
	 *
	 * @category Object Functions
	 *
	 * @param traversable $object the object to clone
	 *
	 * @return object,array
	 */
	public static function duplicate( $object ) {
		if ( is_object( $object ) ) {
			return clone $object;
		}

		return static::extend( [], $object );
	}

	/**
	 * Invokes interceptor with the object, and then returns object. The primary purpose of this method is to "tap
	 * into" a method chain, in order to perform operations on intermediate results within the chain.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 * @param callable $interceptor the function to call with the object as parameter
	 *
	 * @return mixed
	 */
	public static function tap( $object, callable $interceptor ) {
		$interceptor( $object );

		return $object;
	}

	/**
	 * Tells whether the object has a non null value for the given key. Gives priority to array's getters ($obj[$key]
	 * priorityze on $obj->$key). 'null' is equivalent to 'undefined'.
	 *
	 * @category Object Functions
	 *
	 * @param object,array $object the object
	 * @param scalar $key the key
	 *
	 * @return bool
	 */
	public static function has( $object, $key ) {
		return ( static::isArray( $object, true ) && array_key_exists( (string) $key, $object ) )
		       || ( static::isArray( $object ) && isset( $object[ $key ] ) )
		       || ( static::isObject( $object ) && property_exists( $object, $key ) );
	}

	/**
	 * Returns a function that will itself return the key property of any passed-in object.
	 *
	 * @since 0.2.0
	 * @category Object Functions
	 *
	 * @param string,int $key the key or offset to get
	 *
	 * @return closure
	 */
	public static function property( $key ) {
		return function ( $object ) use ( $key ) {
			return static::get( $object, $key );
		};
	}

	/**
	 * Returns a predicate function that will tell you if a passed in object contains all of the key/value properties
	 * present in properties.
	 *
	 * @since 0.2.0
	 * @category Object Functions
	 *
	 * @param traversable $properties the properties used by predicate
	 *
	 * @return closure
	 */
	public static function matches( $properties ) {
		$length = static::size( $properties );

		return function ( $object ) use ( $properties, $length ) {
			if ( $object === null ) {
				return ! $length;
			}

			$result = true;
			static::each( $properties, function ( $value, $key ) use ( $object, & $result ) {
				if ( static::get( $object, $key ) != $value ) {
					$result = false;

					return static::BREAKER;
				}
			} );

			return $result;
		};
	}

	/**
	 * Get the object's key value. If such keys doesn't exists, the default value is returned. If object is neither
	 * Array nor an Object, the object itself is returned.
	 *
	 * @category Object Functions
	 *
	 * @param object,array $object the object
	 * @param scalar $key the key
	 * @param mixed $default optional, the default value to return in case the key doesn't exists
	 *
	 * @return mixed
	 */
	public static function get( $object, $key, $default = null ) {
		if ( ! static::has( $object, $key ) ) {
			return $default;
		}

		if ( static::isArray( $object ) ) {
			return $object[ $key ];
		}

		if ( static::isObject( $object ) ) {
			return $object->$key;
		}

		return $object;
	}

	/**
	 * Set object's key value. If object is neither Array nor an Object, the object itself is returned.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object reference, the object
	 * @param scalar $key the key
	 * @param mixed $value the value to set
	 *
	 * @return mixed
	 */
	public static function set( & $object, $key, $value ) {
		if ( static::isArray( $object ) ) {
			$object[ $key ] = $value;
		}

		if ( static::isObject( $object ) ) {
			$object->$key = $value;
		}

		return $object;
	}

	/**
	 * Tells whether the object is of the given type, or class, or pseudo-type. You may pass several types at once
	 * (using an array of types or by passing several types as arguments), Underscore::is will return true if object
	 * matchs any of these.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 * @param string $types the types to test
	 *
	 * @return bool
	 */
	public static function is( $object, $types ) {
		if ( ! static::isArray( $types ) ) {
			$types = array_slice( func_get_args(), 1 );
		}

		$types = array_map( 'strtolower', $types );

		foreach ( $types as $type ) {
			if ( static::typeOf( $object, false ) == $type || $object instanceof $type ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Performs an optimized deep comparison between the two objects, to determine if they should be considered equal.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the first object
	 * @param mixed $other the second object
	 *
	 * @return bool
	 */
	public static function isEqual( $object, $other ) {
		return static::equal( $object, $other, [], [] );
	}

	/**
	 * Returns true if object contains no values (no enumerable own-properties). Works with scalars as well.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return object,array
	 */
	public static function isEmpty( $object ) {
		if ( empty( $object ) ) {
			return true;
		}

		return static::size( $object ) === 0;
	}

	/**
	 * Returns true if object is an array or usable like an array. If the optionnal native parameter is set to true, it
	 * will only return true if object is a native array.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 * @param bool $native optional, if true will no consider instances of ArrayAccess as arrays
	 *
	 * @return bool
	 */
	public static function isArray( $object, $native = false ) {
		return $native
			? is_array( $object )
			: is_array( $object ) || $object instanceof \ArrayAccess;
	}

	/**
	 * Returns true if value is an Object.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isObject( $object ) {
		return is_object( $object );
	}

	/**
	 * Returns true if object is a Function.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isFunction( $object ) {
		return is_callable( $object );
	}

	/**
	 * @see Underscore::isNumber
	 */
	public static function isNum( $object, $native = false ) {
		return static::isNumber( $object, $native );
	}

	/**
	 * @see Underscore::isNumber
	 */
	public static function isNumeric( $object, $native = false ) {
		return static::isNumber( $object, $native );
	}

	/**
	 * Returns true of object is a Number. If the optionnal native parameter is set to true, it will only return true
	 * if object is a native int or float.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object to pick properties on
	 * @param bool $native optional, if true will not consider SplType instances as numbers
	 *
	 * @return bool
	 */
	public static function isNumber( $object, $native = false ) {
		$result = $native
			? is_numeric( $object )
			: is_numeric( $object ) || $object instanceOf \SplInt || $object instanceOf \SplFloat;

		if ( $result && ! $object instanceOf \SplType ) {
			$result = ! is_nan( $object );
		}

		return $result;
	}

	/**
	 * @see Underscore::isInteger
	 */
	public static function isInt( $object, $native = false ) {
		return static::isInteger( $object, $native );
	}

	/**
	 * Returns true if the object is an integer. If the optional native parameter is set to true, it will only return
	 * true if object is a native int.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 * @param bool $native optional, if true will not consider instances of SplInt as integers
	 *
	 * @return bool
	 */
	public static function isInteger( $object, $native = false ) {
		return $native
			? is_int( $object )
			: is_int( $object ) || $object instanceof \SplInt;
	}

	/**
	 * Returns true if the object is a float. If the optional native parameter is set to true, it will only return true
	 * if object is a native float.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 * @param bool $native optional, if true will not consider instances of SplFloat as integers
	 *
	 * @return bool
	 */
	public static function isFloat( $object, $native = false ) {
		return $native
			? is_float( $object )
			: is_float( $object ) || $object instanceof \SplFloat;
	}

	/**
	 * Returns true if object is a String. If object is an object with a __toString method, it will be considered
	 * as a string as well. If the optionnal native parameter is set to true, it will only return true if object is a
	 * native string.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 * @param bool $native optional, if true will not consider object with toString or SplString as strings
	 *
	 * @return bool
	 */
	public static function isString( $object, $native = false ) {
		return $native
			? is_string( $object )
			: is_string( $object ) || $object instanceof \SplString || method_exists( $object, '__toString' );
	}

	/**
	 * Returns true if object is a DateTime instance. Everything the strtotime function can understand is also
	 * considered a date.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isDate( $object ) {
		return $object instanceof \DateTime || ( static::isString( $object ) && strtotime( (string) $object ) !== false );
	}

	/**
	 * Returns true if object is a valid regular expression (PCRE).
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isRegExp( $object ) {
		return static::isString( $object ) && ( @preg_match( (string) $object, null ) !== false );
	}

	/**
	 * Returns true if object is a finite number.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isFinite( $object ) {
		return is_finite( $object );
	}

	/**
	 * Returns true if object is NaN (Not a Number).
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isNaN( $object ) {
		return is_double( $object ) && is_nan( $object );
	}

	/**
	 * @see Underscore::isBoolean
	 */
	public static function isBool( $object, $native = false ) {
		return static::isBoolean();
	}

	/**
	 * Returns true if object is a Boolean. If the optionnal native parameter is set to true, it will only return true
	 * if object is a native boolean.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 * @param bool $native optional, if true will not consider instances of SplBool as integers
	 *
	 * @return bool
	 */
	public static function isBoolean( $object, $native = false ) {
		return $native
			? is_bool( $object )
			: is_bool( $object ) || $object instanceof \SplBool;
	}

	/**
	 * Returns true if object is Null.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isNull( $object ) {
		return is_null( $object );
	}

	/**
	 * Returns true if $object is a scalar. If the optionnal native parameter is set to true, it will only return true
	 * if object is a native scalar.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isScalar( $object, $native = false ) {
		return $native
			? is_scalar( $object )
			: is_scalar( $object ) || $object instanceof \SplType;
	}

	/**
	 * Returns true if the object can be traversed with a foreach loop.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isTraversable( $object ) {
		return static::is( $object, [ 'Traversable', 'stdClass', 'array' ] );
	}

	/**
	 * Returns true if the object is a resource (like a file handle returned by fopen).
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 *
	 * @return bool
	 */
	public static function isResource( $object ) {
		return is_resource( $object );
	}

	/**
	 * Type constants
	 */
	const TYPE_NULL = "null";
	const TYPE_BOOLEAN = "boolean";
	const TYPE_INTEGER = "integer";
	const TYPE_FLOAT = "float";
	const TYPE_NAN = "nan";
	const TYPE_STRING = "string";
	const TYPE_ARRAY = "array";
	const TYPE_OBJECT = "object";
	const TYPE_RESOURCE = "ressouce";
	const TYPE_UNKNOWN = "unknown";

	/**
	 * @see Underscore::typeOf
	 */
	public static function getType( $object, $class = true ) {
		return static::typeOf( $object, $class );
	}

	/**
	 * Gets the class of given object or its native type. This function aggregates most of the is* functions and can
	 * be seen as a more preceise version of PHP native function gettype. The class parameters lets you know the
	 * exact type of the object, if set to false 'object' is returned for objects. Otherwise will return one of the
	 * Underscore::TYPE_* constants.
	 *
	 * @category Object Functions
	 *
	 * @param mixed $object the object
	 * @param bool $class optional, if true will return the exact class of $object instead of TYPE_OBJECT
	 *
	 * @return string
	 */
	public static function typeOf( $object, $class = true ) {
		switch ( true ) {
			case static::isNull( $object ):
				return static::TYPE_NULL;
			case static::isBoolean( $object ):
				return static::TYPE_BOOLEAN;
			case static::isInteger( $object ):
				return static::TYPE_INTEGER;
			case static::isNaN( $object ):
				return static::TYPE_NAN;
			case static::isFloat( $object ):
				return static::TYPE_FLOAT;
			case static::isString( $object ):
				return static::TYPE_STRING;
			case static::isArray( $object ):
				return static::TYPE_ARRAY;
			case static::isResource( $object ):
				return static::TYPE_RESOURCE;
			case static::isObject( $object ):
				return $class ? get_class( $object ) : static::TYPE_OBJECT;
			default:
				return static::TYPE_UNKNOWN;
		}
	}

	/**
	 * Utility Functions
	 * -----------------
	 */

	/**
	 * Returns the same value that is used as the argument. In math: f(x) = x. This function looks useless, but is used
	 * throughout Underscore as a default iterator.
	 *
	 * @category Utility Functions
	 *
	 * @param mixed $value the value to return
	 *
	 * @return mixed
	 */
	public static function identity( $value ) {
		return $value;
	}

	/**
	 * Creates a function that returns the same value that is used as the argument of _::constant.
	 *
	 * @since 0.2.0
	 * @category Utility Functions
	 *
	 * @param mixed $value the value
	 *
	 * @return mixed
	 */
	public static function constant( $value ) {
		return function () use ( $value ) {
			return $value;
		};
	}

	/**
	 * Returns undefined irrespective of the arguments passed to it. Useful as the default for optional callback
	 * arguments.
	 *
	 * @since 0.2.0
	 * @category Utility Functions
	 *
	 * @return void
	 */
	public static function noop() {
		return;
	}

	/**
	 * Invokes the given iterator function n times. Each invocation of iterator is called with an index argument.
	 * Produces an array of the returned values.
	 *
	 * @category Utility Functions
	 *
	 * @param int $n the number of time $iterator will be run
	 * @param callable $iterator the iterator function
	 * @param object $context optional, if provided will become the context for $iterator
	 *
	 * @return array
	 */
	public static function times( $n, callable $iterator, $context = null ) {
		static::associate( $iterator, $context );

		$n     = max( 0, $n );
		$accum = [];
		for ( $i = 0; $i < $n; $i ++ ) {
			$accum[] = $iterator( $i );
		}

		return $accum;
	}

	/**
	 * Returns a random integer between min and max, inclusive. If you only pass one argument, it will return a number
	 * between 0 and that number.
	 *
	 * @category Utility Functions
	 *
	 * @param int $min the lower bound (or the max if $max is null, the min being 0 then)
	 * @param int $max optional the upper bound
	 *
	 * @return int
	 */
	public static function random( $min, $max = null ) {
		if ( $max === null ) {
			list( $min, $max ) = [ 0, $min ];
		}

		return mt_rand( $min, $max );
	}

	/**
	 * Allows you to extend Underscore with your own utility functions. Pass a hash of array('name' => function)
	 * definitions to have your functions added to the Underscore library, as well as the OOP wrapper.
	 *
	 * @category Utility Functions
	 *
	 * @param array $functions an collection of functions to add to the Underscore class
	 *
	 * @return void
	 */
	public static function mixin( array $functions ) {
		static::$_userFunctions = $functions + static::$_userFunctions;
	}

	/**
	 * Returns callable version of any Underscore method (event the user defined ones).
	 *
	 * @category Utility Functions
	 *
	 * @param string $method multiple, the Underscore's method name(s)
	 *
	 * @return callable,array
	 */
	public static function provide( $method ) {
		$class = get_called_class();

		return func_num_args() == 1
			? [ $class, $method ]
			: static::map( func_get_args(), function ( $method ) use ( $class ) {
				return [ $class, $method ];
			} );
	}

	/**
	 * Generate a pseudo-unique id.
	 *
	 * @category Utility Functions
	 *
	 * @param string $prefix optional, a prefix for the id
	 *
	 * @return string
	 */
	public static function uniqueId( $prefix = "" ) {
		return uniqid( $prefix );
	}

	/**
	 * Escapes a string for insertion into HTML, replacing &, <, >, ", ', and / characters.
	 *
	 * @category Utility Functions
	 *
	 * @param string $string the string to escape
	 *
	 * @return string
	 */
	public static function escape( $string ) {
		return htmlentities( (string) $string );
	}

	/**
	 * The opposite of escape, replaces &amp;, &lt;, &gt;, &quot;, &#x27;, and &#x2F; with their unescaped
	 * counterparts.
	 *
	 * @category Utility Functions
	 *
	 * @param string $string the string to unescape
	 *
	 * @return string
	 */
	public static function unescape( $string ) {
		return html_entity_decode( (string) $string );
	}

	/**
	 * If the value of the named property is a function then invoke it with the object as context; otherwise, return
	 * it.
	 *
	 * @category Utility Functions
	 *
	 * @param object,array $object the object or array
	 * @param scalar $property the property to get
	 *
	 * @return mixed
	 */
	public static function result( $object, $property ) {
		$value = static::get( $object, $property );

		if ( static::isFunction( $value ) ) {
			static::associate( $value, $object );

			return $value();
		}

		return $value;
	}

	/**
	 * The equivalent of the finally keywork (available since PHP 5.5).
	 *
	 * @since 0.2.0
	 * @category Utility Functions
	 *
	 * @param callable $function a function
	 * @param callable $finally another function that will *always* be executed after $function
	 * @param object $context the functions context
	 *
	 * @return mixed
	 */
	public static function lastly( callable $function, callable $finally, $context = null ) {
		static::associate( $function, $context );
		static::associate( $finally, $context );

		try {
			$result = $function();
		} catch ( \Exception $exception ) { /* noop */
		}
		$finally();

		if ( isset( $exception ) ) {
			throw $exception;
		}

		return $result;
	}

	/**
	 * Returns an integer timestamp for the current time.
	 *
	 * @return int
	 */
	public static function now() {
		return time();
	}

	/**
	 * By default, Underscore uses ERB-style template delimiters, change the following template settings to
	 * use alternative delimiters.
	 */
	public static $templateSettings = [
		'evaluate'    => '/<%([\s\S]+?)%>/',
		'interpolate' => '/<%=([\s\S]+?)%>/',
		'escape'      => '/<%-([\s\S]+?)%>/',
	];

	/**
	 * When customizing templateSettings, if you don't want to define an interpolation, evaluation or escaping
	 * regex, we need one that is guaranteed not to match.
	 */
	protected static $_noMatch = "/(.)^/";

	/**
	 * Compiles PHP templates into functions that can be evaluated for rendering. Useful for rendering complicated
	 * bits of HTML from JSON data sources. Template functions can both interpolate variables, using <%= ... %>, as
	 * well as execute arbitrary PHP code, with <% ... %>. If you wish to interpolate a value, and have it be
	 * HTML-escaped, use <%- ... %> When you evaluate a template function, pass in a data object that has properties
	 * corresponding to the template's free variables. If you're writing a one-off, you can pass the data object as
	 * the second parameter to template in order to render immediately instead of returning a template function. The
	 * settings argument should be a hash containing any Underscore::$templateSettings that should be overridden.
	 *
	 * If ERB-style delimiters aren't your cup of tea, you can change Underscore's template settings to use different
	 * symbols to set off interpolated code. Define an interpolate regex to match expressions that should be
	 * interpolated verbatim, an escape regex to match expressions that should be inserted after being HTML escaped,
	 * and an evaluate regex to match expressions that should be evaluated without insertion into the resulting string.
	 * You may define or omit any combination of the three.
	 *
	 * @category Utility Functions
	 *
	 * @param string $templateString the template buffer
	 * @param array,object $data optional, if provided will compute the template using this array as variables
	 * @param array $settings optional, il provided will override default matchers (use with care)
	 *
	 * @return closure,string
	 */
	public static function template( $templateString, $data = [], $settings = [] ) {
		$settings = static::defaults( [], $settings, static::$templateSettings, [
			'evaluate'    => static::$_noMatch,
			'interpolate' => static::$_noMatch,
			'escape'      => static::$_noMatch,
		] );

		$class = get_called_class();

		$pattern = '~' . implode( '|', static::map(
				static::pick( $settings, 'escape', 'interpolate', 'evaluate' ),
				function ( $subpattern, $name ) {
					return "(?<$name>" . trim( $subpattern, $subpattern[0] ) . ")";
				}
			) ) . '|$~';

		$templateString = preg_replace_callback( $pattern, function ( $match ) use ( $class ) {
			if ( ! empty( $match['escape'] ) ) {
				return sprintf( '<?php echo %s::escape(%s) ?>', $class, trim( $match[2] ) );
			}

			if ( ! empty( $match['interpolate'] ) ) {
				return sprintf( '<?php echo %s ?>', trim( $match[4] ) );
			}

			if ( ! empty( $match['evaluate'] ) ) {
				return sprintf( '<?php %s ?>', trim( $match[6] ) );
			}
		}, $templateString );


		return $data ? self::template_function( static::toArray( $data ), $templateString ) : self::template_function();
	}

	public static function template_function( $data = [], $templateString = '' ) {
		extract( $data ); ob_start();
		eval( '?>' . $templateString );
		return ob_get_clean();
	}

	/**
	 * Chaining
	 * --------
	 */

	/**
	 * Returns a wrapped object. Calling methods on this object will continue to return wrapped objects until value
	 * is used. Calling chain will cause all future method calls to return wrapped objects. When you've finished the
	 * computation, use value to retrieve the final value.
	 *
	 * @category Chaining
	 *
	 * @param mixed $object the chaining initial state
	 *
	 * @return Bridge
	 */
	public static function chain( $object ) {
		return new Bridge( $object, new static );
	}

	/**
	 * Class Forgery
	 * -------------
	 */

	/**
	 * @see Underscore::forge
	 */
	public static function strategy( $classname ) {
		return static::forge( $classname );
	}

	/**
	 * Create new mixins on runtime. The implementation is based on Bob Weinand's idea of Scala traits implementation
	 * in PHP (see it here https://gist.github.com/bwoebi/7319798). This method decomposes the $classname to create
	 * a new class, using '\with' as a separator for traits.
	 *
	 * @since 0.2.0
	 * @category Class Forgery
	 *
	 * @param string $classname the class to forge
	 *
	 * @throws RuntimeException if class or traits are not availabled
	 *
	 * @return boolean
	 */
	public static function forge( $classname ) {
		if ( class_exists( $classname ) ) {
			return true;
		}

		if ( strpos( $classname, '\\with' ) === false ) {
			return false;
		}

		list( $namespace, $class ) = str_split( $classname, strrpos( $classname, '\\' ) );
		$class = substr( $class, 1 );

		$traits = explode( '\\with', $classname );
		$base   = '\\' . trim( array_shift( $traits ), '\\' );

		if ( ! class_exists( $base, true ) ) {
			throw new \RuntimeException( "class $base does not exists" );
		}

		if ( empty( $traits ) ) {
			return false;
		}

		$use = '';
		foreach ( $traits as $trait ) {
			if ( ! trait_exists( $trait, true ) ) {
				throw new \RuntimeException( "trait $trait does not exists" );
			}
			$use .= "use $trait; ";
		}

		// eval is now officially your friend!
		return @eval( "namespace $namespace { class $class extends $base { $use } }" ) !== false;
	}

	/**
	 * Internal Functions
	 * ------------------
	 */

	/**
	 * User defined functions internal hashmap.
	 */
	protected static $_userFunctions = [];

	/**
	 * Allows you to call the user defined functions registered with Underscore::mixin
	 *
	 * @category Internal Functions
	 *
	 * @param string $method the method to call
	 * @param array $arguments the method's arguments
	 *
	 * @return mixed
	 */
	public static function __callStatic( $method, array $arguments ) {
		if ( ! isset( static::$_userFunctions[ $method ] ) ) {
			throw new \BadMethodCallException( "no such method $method" );
		}

		return call_user_func_array( static::$_userFunctions[ $method ], $arguments );
	}

	/**
	 * Allows object calls toward user defined static methods
	 *
	 * @category Internal Functions
	 *
	 * @param string $method the method to call
	 * @param array $arguments the method's arguments
	 *
	 * @return mixed
	 */
	public function __call( $method, array $arguments ) {
		return static::__callStatic( $method, $arguments );
	}

	/**
	 * Safely Associates the given function with the given context.
	 *
	 * @category Internal Functions
	 *
	 * @param mixed $function reference, the function to associate
	 * @param object $context the function's context
	 *
	 * @return void
	 */
	protected static function associate( & $function, $context ) {
		if ( $context && $function instanceof \Closure ) {
			if ( ! $function = $function->bindTo( $context ) ) {
				throw new \UnexpectedValueException( "unable to associate closure to context" );
			}
		}
	}

	/**
	 * Returns an iterator that matches a list item against properties.
	 *
	 * @category Internal Functions
	 *
	 * @param array $properties the properties to filter on
	 *
	 * @return closure
	 */
	protected static function getListfilter( $properties ) {
		return function ( $item ) use ( $properties ) {
			foreach ( $properties as $property => $value ) {
				if ( static::get( $item, $property ) != $value ) {
					return false;
				}
			}

			return true;
		};
	}

	/**
	 * An internal function to generate lookup iterators.
	 *
	 * @category Internal Functions
	 *
	 * @param mixed $value the value to iterate with
	 *
	 * @return mixed,closure
	 */
	protected static function getLookupIterator( $value ) {
		return is_callable( $value ) ? $value : function ( $object ) use ( $value ) {
			return static::get( $object, $value );
		};
	}

	/**
	 * Tells if two objects are equals.
	 *
	 * @category Internal Functions
	 *
	 * @param mixed $a the first value
	 * @param mixed $b the second $value
	 * @param array $aStack the first recursion stack
	 * @param array $bStack the second recursion stack
	 *
	 * @return bool
	 */
	protected static function equal( $a, $b, $aStack, $bStack ) {
		// Unwrap any wrapped object.
		$a instanceof self && $a = $a->_wrapped;
		$b instanceof self && $b = $b->_wrapped;

		if ( $a === null && $b === null ) {
			return true;
		}

		// Two resources are always considered different since there is no way to compare them
		if ( is_resource( $a ) && is_resource( $b ) ) {
			return false;
		}

		// Perform classic scalar comparison.
		if ( is_scalar( $a ) && is_scalar( $b ) ) {
			return $a == $b;
		}

		// Assume equality for cyclic structures. The algorithm for detecting cyclic structures is adapted from
		// ES 5.1 section 15.12.3, abstract operation JO.
		$length = count( $aStack );
		while ( $length -- ) {
			if ( $aStack[ $length ] == $a ) {
				return $bStack[ $length ] == $b;
			}
		}

		// Add the first object to the stack of traversed objects.
		$aStack[] = $a;
		$bStack[] = $b;

		$result = true;
		$size   = 0;

		if ( static::isTraversable( $a ) && static::isTraversable( $b ) ) {
			// Try to deep compare sequences.
			static::each( $a, function ( $item, $key ) use ( & $a, & $b, & $aStack, & $bStack, & $size, & $result ) {
				$size ++;

				$result = static::has( $b, $key ) && static::equal(
						static::get( $a, $key ),
						static::get( $b, $key ),
						$aStack,
						$bStack
					);

				if ( ! $result ) {
					return static::BREAKER;
				}
			} );

			if ( $size != static::size( $b ) ) {
				$result = false;
			}
		} else {
			// Last hope comparison.
			$result = $a == $b;
		}

		// Remove the first object from the stack of traversed objects.
		array_pop( $aStack );
		array_pop( $aStack );

		return $result;
	}
}
