module BackPropagation_Char;

/*
Programmed by Jeon Hee Won
2007. 5. 22
*/

private import BackPropagation_common;
private import std.c.stdlib;
private import std.c.time;
private import std.stdio;

class BackPropagation_Training {

public : // connection의 weight
	Weight InputToHidden[PAIR_ELEMENT][HIDDEN_COUNT]; //Input에서 Hidden의 weight
	Weight HiddenToOutput[HIDDEN_COUNT][PAIR_COUNT]; //Hidden에서 Output의 weight
	Weight Input_bias[HIDDEN_COUNT]; //Input bias
	Weight Hidden_bias[PAIR_COUNT];  //Hidden bias
	
	//활성화 함수
	double delegate(double) Activation_func;
	double delegate(double) Activation_func_Diff;

	void processing(){
		int count = 0;

		step_0();
		step_1();
		step_2();

		while(count < LEARNING_COUNT){

			/* Feedforward	*/
			step_3(count%6);
			step_4();
			step_5();

			/* Backpropagation of error	*/

			step_6();
			step_7();
			step_8();

			count++;
		}
	}

	this(Tanh tangent){
		Activation_func = &tangent.TanH;
		Activation_func_Diff = &tangent.TanH_differential;
	}

	this(Bianry_Sigmoid sigmoid){
		Activation_func = &sigmoid.Bianry_Sigmoid;
		Activation_func_Diff = &sigmoid.Binary_sigmoid_differential;
	}

private:
	Neuron Input[PAIR_ELEMENT]; // 입력층 48개
	Neuron Hidden[HIDDEN_COUNT]; // 은닉층 40개
	Neuron Output[PAIR_COUNT]; // 출력층 6개

	uint Target[PAIR_COUNT];

	//Initialize
	void step_0(){

		srand(cast(uint) time(null));
		
		/* InputToHidden의 weight를 초기화[0.5 ~ -0.5] */
		for(int i=0; i < PAIR_ELEMENT; i++)
			for(int j=0; j < HIDDEN_COUNT; j++)
				InputToHidden[i][j].Wold = (rand()%1000)/1000.0 - 0.5;

		/* HiddenToOutput의 weight를 초기화[0.5 ~ -0.5] */
		for(int i=0; i < HIDDEN_COUNT; i++)
			for(int j=0; j < PAIR_COUNT; j++)
				HiddenToOutput[i][j].Wold = (rand()%1000)/1000.0 - 0.5;

		/* 입력층의 bias의 weight를 초기화[-1.0 ~ 0.0]	*/
		for(int i=0; i < HIDDEN_COUNT; i++)
			Input_bias[i].Wold = (rand()%1000)/1000.0 - 1.0;

		/* 은닉층의 bias의 weight를 초기화[-1.0 ~ 0.0]	*/
		for(int i=0; i < PAIR_COUNT; i++)
			Hidden_bias[i].Wold = (rand()%1000)/1000.0 - 1.0;
	}

	void step_1(){
		//while stopping condition us false Do Step 2-9
	}

	void step_2(){
		//For each training pair do steps 3-8
	}

	// FeedForword
	// count값은 데이터의 인덱스 정보.
	void step_3(int count){
		/*
		Each input unit receives input signal xi and broadcasts this sinal
		to all units in the layer above(the hidden unis)
		*/
		//training set 입력
		int i = 0;
		uint[] rowDataIn = PAIR_DATA[count];
		foreach(uint uiData ; rowDataIn){
			Input[i].input = Input[i].output = uiData;
			i++;
		}
		
		uint[] rowDataOut = PAIR_TARGET[count];
		//writefln(rowDataOut);
		i = 0;
		/* 기대하는 출력값을 넣어줌 */
		foreach(uint uiData ; rowDataOut){
			Target[i] = uiData;
			//writefln("%d target",Target[i]);
			i++;
		}
	}

	void step_4(){

		/* 
			Each hidden unit sums its weighted input signals,
		*/
		/*
			applies its activation function to compute its output signal, 
			and sends this signal to all units in the layer above (output units)
		*/

		/*히든 레이어의 인풋값을 가지고 계산을 한다.
		1. 인풋값을 각 히든레이어로 가는 값의 old weight값을 가지고 계산한다.
		2. 각 인풋 레이어로의 weight 모든 값을 계산해서 이 결과를 input에 넣고
		3. input값을 시그모이드 함수 처리를 해서 output에 넣는다.
		4. hidden layer 완료..
		*/
		for(int j = 0; j < HIDDEN_COUNT; j++)
		{
			double sum = 0;
			sum = Input_bias[j].Wold;
			for(int i = 0; i < PAIR_ELEMENT; i++)
				sum += Input[i].output * InputToHidden[i][j].Wold;
			Hidden[j].input = sum;
			Hidden[j].output = Activation_func(Hidden[j].input);
		}
	}

	void step_5(){
		/*
			Each output unit sums its weighted input signals,
			and applies its activation function to compute its output signals
		*/
		/* 
		이것 setp4와 비슷하다.
		*/
		for(int j=0; j < PAIR_COUNT; j++)
		{
			double sum = 0;
			sum = Hidden_bias[j].Wold;
			for(int i=0; i < HIDDEN_COUNT; i++)
				sum += Hidden[i].output * HiddenToOutput[i][j].Wold;

			Output[j].input = sum;
			Output[j].output = Activation_func(Output[j].input);
		}
	}
	// FeedForword end.

	//error backpropagation
	void step_6(){

		for(int j=0; j < PAIR_COUNT; j++){
			Output[j].delta = (Target[j] - Output[j].output) * Activation_func_Diff(Output[j].input);
			Hidden_bias[j].delta = LEARNING_RATE * Output[j].delta;
			for(int i=0; i < HIDDEN_COUNT; i++)
				HiddenToOutput[i][j].delta = LEARNING_RATE * Output[j].delta * Hidden[i].output;
		}
	}

	void step_7(){
			
		/*	은닉층의 BackPropagation 합을 구함		*/
		for(int i = 0; i < HIDDEN_COUNT; i++){
			Hidden[i].BP_delta_sum = 0;
			for(int j=0; j < PAIR_COUNT; j++)
				Hidden[i].BP_delta_sum += Output[j].delta * HiddenToOutput[i][j].Wold;
		}

		
		/* 은닉층의 delta를 구함	*/
		for(int i = 0; i < HIDDEN_COUNT; i++)
			Hidden[i].delta = Hidden[i].BP_delta_sum * Activation_func_Diff(Hidden[i].input);


		/*	입력층의 delta를 구함	*/
		for(int i = 0; i < PAIR_ELEMENT; i++)
			for(int j = 0; j < HIDDEN_COUNT; j++)
				InputToHidden[i][j].delta = LEARNING_RATE * Hidden[j].delta * Input[i].output;

		/*	입력층 bias의 delta를 구함		*/
		for(int i = 0; i < HIDDEN_COUNT; i++)
			Input_bias[i].delta = LEARNING_RATE * Hidden[i].delta;
	}

	void step_8(){

		/* 은닉층의 새로운 weight값을 구함	 - hidden */
		for(int j = 0; j < PAIR_COUNT; j++)
			for(int i=0; i < HIDDEN_COUNT; i++)
				HiddenToOutput[i][j].Wnew = HiddenToOutput[i][j].Wold + HiddenToOutput[i][j].delta;

		/* 은닉층의 새로운 weight값을 구함	 - HBias */
		for(int i = 0; i < PAIR_COUNT; i++)
			Hidden_bias[i].Wnew = Hidden_bias[i].Wold + Hidden_bias[i].delta;

		/* 입력층의 새로운 weight값을 구함	 - input */
		for(int j = 0; j < HIDDEN_COUNT; j++)
			for(int i = 0; i < PAIR_ELEMENT; i++)
				InputToHidden[i][j].Wnew = InputToHidden[i][j].Wold + InputToHidden[i][j].delta;

		/* 입력층의 새로운 weight값을 구함	 - IBias */
		for(int i = 0;i < HIDDEN_COUNT;i++)
			Input_bias[i].Wnew = Input_bias[i].Wold + Input_bias[i].delta;


		/* 새로운 값으로 업데이트	*/
		/* 은닉층의 새로운 weight값을 구함	 - hidden */
		for(int j = 0; j < PAIR_COUNT; j++)
			for(int i = 0; i < HIDDEN_COUNT; i++)
				HiddenToOutput[i][j].Wold = HiddenToOutput[i][j].Wnew;

		/* 은닉층의 새로운 weight값을 구함	 - HBias */
		for(int i = 0; i < PAIR_COUNT; i++)
			Hidden_bias[i].Wold = Hidden_bias[i].Wnew;

		/* 입력층의 새로운 weight값을 구함	 - input */
		for(int j = 0; j < HIDDEN_COUNT; j++)
			for(int i = 0; i < PAIR_ELEMENT; i++)
				 InputToHidden[i][j].Wold = InputToHidden[i][j].Wnew;

		/* 입력층의 새로운 weight값을 구함	 - IBias */
		for(int i = 0;i < HIDDEN_COUNT; i++)
			Input_bias[i].Wold = Input_bias[i].Wnew;

		
	}

}




//테스트 클래스
//feed forword 부분과 거의 동일하다.
class BackPropagation_Test{

public:
	//활성화 함수
	double delegate(double) Activation_func;
	//double delegate(double) Activation_func_Diff;

	this(BackPropagation_Training BPT){
		//학습값 복사
		this.InputToHidden[][] = BPT.InputToHidden;
		this.HiddenToOutput[][] = BPT.HiddenToOutput;
		this.Input_bias[][] = BPT.Input_bias;
		this.Hidden_bias[][] = BPT.Hidden_bias;
		//활성화 함수 복사
                this.Activation_func = BPT.Activation_func;
		//this.Activation_func_Diff = BPT.Activation_func_Diff;
	}
	~this(){ }

private:

	Neuron Input[PAIR_ELEMENT]; // 입력층 48개
	Neuron Hidden[HIDDEN_COUNT]; // 은닉층 40개
	Neuron Output[PAIR_COUNT]; // 출력층 6개
	
	Weight InputToHidden[PAIR_ELEMENT][HIDDEN_COUNT]; 
	Weight HiddenToOutput[HIDDEN_COUNT][PAIR_COUNT];
	Weight Input_bias[HIDDEN_COUNT]; 
	Weight Hidden_bias[PAIR_COUNT]; 
        
	void step_0(){
		//initialize
	}

	void step_1(){
		/* While stopping ocndition is false, do step2-9 */
	}

	void step_2(){
		/* For each training pair, do steps3-8 */
	}

	void step_3(int count){
		int i = 0;
		uint[] rowDataIn = RECO_DATA[count];
		foreach(uint uiData ; rowDataIn){
			Input[i].input = Input[i].output = uiData;
			i++;
		}
	}

	void step_4(){
		for(int j = 0; j < HIDDEN_COUNT; j++)
		{
			double sum = 0;
			sum = Input_bias[j].Wold;
			for(int i = 0; i < PAIR_ELEMENT; i++)
				sum += Input[i].output * InputToHidden[i][j].Wold;
			Hidden[j].input = sum;
			Hidden[j].output = Activation_func(Hidden[j].input);
		}
	}

	void step_5(){
		for(int j=0; j < PAIR_COUNT; j++)
		{
			double sum = 0;
			sum = Hidden_bias[j].Wold;
			for(int i=0; i < HIDDEN_COUNT; i++)
				sum += Hidden[i].output * HiddenToOutput[i][j].Wold;

			Output[j].input = sum;
			Output[j].output = Activation_func(Output[j].input);
		}
	}

public:
	char evaluation(int count){
		double max = -1;
		int index = 0;

		step_0();
		step_1();
		step_2();
		step_3(count);
		step_4();
		step_5();
		for(int i = 0; i < PAIR_COUNT;i++ ){
			writef("%.6f\t", Output[i].output);
			if(Output[i].output > max){
				max = Output[i].output;
				index = i;
			}
		}

		switch(index){
		case 0 :
			return '1';
		case 1 :
			return '9';
		case 2 :
			return '5';
		case 3 :
			return '/';
		case 4 :
			return '7';
		case 5 :
			return '0';
		}
	}
}



/*
void main(){
	BackPropagation BP = new BackPropagation;

	BP.processing();
}
*/
