<?

class network {

	public $layers = [];

	public function __construct($layer_count=3, $neuron_count=3) {
		$index = 0;
		$layers = $this->layers;
		while($index < $layer_count) {
			$layer = [];
			$neuron_index = 0;
			if($index == ($layer_count-1)) {
				$neuron_count = 2;
			}
			while($neuron_index < $neuron_count) {
				$neuron = new neuron($this, $neuron_count);
				$layer[] = $neuron;
				$neuron_index = $neuron_index + 1;
			}
			$layers[] = $layer;
			$index = $index + 1;
		}
	}

	public function compute_output($input_vector, $adjust_from_error_targets=false) {
		$input_vector_value = $input_vector;
		$layers = $this->layers;
		$layers = [...$layers];

		/*if($adjust_from_error_targets !== false) {
			$layers = $object->reverse($layers);
		}*/

		if($adjust_from_error_targets !== false) {

			$input_vector_value = $input_vector;
			/*$layers = [...$layers];*/
			$layers = $object->reverse($layers);
			foreach($layers as $layer_key => $layer) {
				$set_output_vector = [];
				foreach($layer as $neuron) {
					$output_value = $neuron->compute_output($input_vector_value);
					if($layer_key == 0) {
						$neuron->adjust_from_error_last($input_vector_value, $output_value, $adjust_from_error_targets);
					} else {
						$neuron->adjust_from_error($input_vector_value, $output_value, $layers[$layer_key-1]);
					}
					$set_output_vector[] = $output_value;
				}
			}
			$input_vector_value = $set_output_vector;
			$layers = $object->reverse($layers);
		}
		
		$input_vector_value = $input_vector;

		foreach($layers as $layer_key => $layer) {
			$set_output_vector = [];
			foreach($layer as $neuron) {
				$output_value = $neuron->compute_output($input_vector_value);
				$set_output_vector[] = $output_value;
			}
			$input_vector_value = $set_output_vector;
		}

		return $input_vector_value;
	}
}

class neuron {

	public $weights;

	public function __construct($network, $weight_count=3) {
		$index = 0;
		$this->weights = [];
		$weights = $this->weights;
		while($index < $weight_count) {
			$weights[] = $math->random_int(100) / 1000;
			$index = $index + 1;
		}
		$object->log('constructed neuron');
	}

	public function adjust_from_error_last($inputs, $output_value, $target=NULL) {
		$error = ($target - $output_value) * $output_value * (1 - $output_value);

		$this->error = $error;


		foreach($this->weights as $key => $weight) {
			$this->weights[$key] = $weight + ($object->learning_rate * $error * $inputs[$key]);
		}
		return $error;
	}

	public $error = 0;

	public function adjust_from_error($inputs, $output_value, $previous_layer_output=NULL) {
		$sum = 0;
		foreach($this->weights as $key => $weight) {
			$sum = $sum + $weight * ($previous_layer_output[$key]->error);
		}

		$error = $output_value * (1 - $output_value) * $sum;

		$this->error = $error;

		foreach($this->weights as $key => $weight) {
			$this->weights[$key] = $weight + ($object->learning_rate * $error * $inputs[$key]);
		}
		return $error;
	}

	public function compute_output($input_vector) {
		$output_value = 0;
		foreach($input_vector as $key => $value) {
			$output_value = $output_value + ($this->weights[$key] * $value);
		}
		return $output_value;
	}

	public $b = 3;

	public function activation_function($output_value) {
		return (1 / (1 + ($math->exp((0-$this->b) * $output_value))));
	}
}

?>