package perceptron;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Brian
 */
public class Perceptron {

	double alpha; // Learning rate
	double[] W; // Weight vector
	List<TrainingSample> D = new ArrayList<>(); // List containing training samples

	public Perceptron(String trainingDataLocation) throws FileNotFoundException, IOException {
		alpha = 0.1; // Initialize learning rate
		D = IO.getTrainingData(trainingDataLocation); // read in training samples
		W = new double[D.get(0).x.size()]; // weight vector is the same size as the feature vectors
		
		// Print initial weights:
		System.out.print("Initial weights: ");
		IO.printVector(W);

		// Train:
		long start = System.currentTimeMillis();
		W = train(D, W);
		long end = System.currentTimeMillis();
		System.out.println("Training time: " + (end - start) + " milliseconds");
	}

	/**
	 * @param D
	 * @param W
	 * @return the final weights once the perceptron has been trained
	 */
	private double[] train(List<TrainingSample> D, double[] W) {
		double error;
		int error_count;
		int iteration_count = 0;
		do {
			iteration_count++;
			error_count = 0;
			for(int j = 0; j < D.size(); j++) {
				TrainingSample curSample = D.get(j);

				int y_j = dotProduct(curSample.x, W) > 0.5 ? 1 : 0;
				error = curSample.d - y_j;

				if(error != 0) {
					error_count++;
					for(int i = 0; i < W.length; i++) {
						double delta_w = alpha * (D.get(j).d - y_j) * curSample.x.get(i);
						W[i] += delta_w;
					}
				}
			}
		} while(error_count != 0);
		System.out.println("No. of iterations: " + iteration_count);
		return W;
	}

	/**
	 * @param x
	 * @param w
	 * @return dot product of feature vectors x and weight vector w
	 */
	private double dotProduct(List<Integer> x, double[] w) {
		double dp = 0;
		for(int i = 0; i < x.size(); i++) {
			dp += x.get(i) * w[i];
		}

		return dp;
	}

	/**
	 * @param x
	 * @return an int representing the class chosen by the perceptron
	 */
	public int classify(List<Integer> x) {
		double y = dotProduct(x, W);
		
		return y > 0 ? 1 : 0;
	}
}
