<?php
	function sql_cross($table_left, $table_right)
	{
		$table_result = [];
		array_map
		(
			function ($row_left) use (&$table_left,&$table_right,&$table_result)
			{
				array_map
				(
					function ($row_right) use (&$table_left,&$table_right,&$table_result,&$row_left)
					{
						$row_result = [];
						foreach ($row_left as $key_left => $value_left) $row_result[$key_left] = $value_left;
						foreach ($row_right as $key_right => $value_right) $row_result[$key_right] = $value_right;
						array_push($table_result, $row_result);
					},
					$table_right
				);
			},
			$table_left
		);
		return $table_result;
	}
	
	function sql_cross_all($tables_source)
	{
		$n = count($tables_source);
		if ($n == 1)
		{
			return $tables_source[0];
		}
		else
		{
			return sql_cross(sql_cross_all(array_slice($tables_source, $n-1)), $tables_source[$n-1]);
		}
	}
	
	function sql_project($table_source, $columns)
	{
		return (
			array_map
			(
				function ($row_in) use (&$columns)
				{
					$row_out = [];
					foreach ($row_in as $key_in => $value_in)
					{
						if (array_search($key_in, $columns) !== false)
						{
							$row_out[$key_in] = $value_in;
						}
					}
					return $row_out;
				},
				$table_source
			)
		);
	}
	
	function sql_order($table_source, $order)
	{
		usort
		(
			$table_source,
			$order
		);
		return $table_source;
	}
	
	function sql_condense($table_source, $columns_from, $columns_to, $condensators)
	{
		return (
			array_map
			(
				function ($row_in) use (&$columns_from,&$columns_to,&$condensators)
				{
					$row_out = [];
					$values = [];
					foreach ($row_in as $column => $value)
					{
						if (array_search($column, $columns_from) === false)
						{
							$row_out[$column] = $row_in[$column];
						}
						else
						{
							$values[$column] = $value;
						}
					}
					foreach ($columns_to as $column)
					{
						$row_out[$column] = (array_key_exists($column, $condensators) ? $condensators[$column]($values) : null);
					}
					return $row_out;
				},
				$table_source
			)
		);
	}
	
	function sql_delete($table_source, $columns)
	{
		return sql_condense($table_source, $columns, [], []);
	}
	
	function sql_add($table_source, $columns, $assigners = [])
	{
		return sql_condense($table_source, [], $columns, $assigners);
	}
	
	function sql_rename($table_source, $column_from, $column_to)
	{
		return sql_condense($table_source, [$column_from], [$column_to], [$column_to => function ($values) use (&$column_from) {return $values[$column_from];}]);
	}
	
	function sql_select($table_source, $predicate)
	{
		return array_filter($table_source, $predicate);
	}
	
	function sql_groups($table_source, $column, $extraction = null)
	{
		if ($extraction == null) $extraction = (function ($column, $row) {return $row[$column];});
		$groups = [];
		array_map
		(
			function ($row_source) use (&$table_source,&$column,&$extraction,&$groups)
			{
				$value = $extraction($column, $row_source);
				$group = null;
				$index = null;
				// foreach ($groups as $group_)
				for ($index_ = 0; $index_ < count($groups); $index_ += 1)
				{
					$group_ = $groups[$index_];
					if ($group_["value"] == $value)
					{
						$group = $group_;
						$index = $index_;
						break;
					}
				}
				if ($group == null)
				{
					$group = ["value" => $value, "members" => []];
					$index = count($groups);
					array_push($groups, $group);
				}
				// array_push($group["members"], $row_source);
				array_push($groups[$index]["members"], $row_source);
			},
			$table_source
		);
		return $groups;
	}
	
	function sql_groupify($table_source, $column, $aggregators)
	{
		$groups = sql_groups($table_source, $column);
		$table_result = array_map
		(
			function ($group) use (&$column,&$aggregators)
			{
				$row_result = [];
				$row_result[$column] = $group["value"];
				foreach ($aggregators as $aggregator_key => $aggregator_value)
				{
					$args = array_map(function ($member) use (&$aggregator_key) {return $member[$aggregator_key];}, $group["members"]);
					$row_result[$aggregator_key] = $aggregator_value($args);
				}
				return $row_result;
			},
			$groups
		);
		return $table_result;
	}
	
 ?>