package dotnet54.measures.multivariate;

import dotnet54.measures.Measure;
import dotnet54.tscore.data.Dataset;

import java.util.Random;

public class Euclidean extends MultivariateMeasure {

	protected boolean useSquaredEuclidean;

	public Euclidean(Measure measureName, boolean dependentDimensions, int[] dimensionsToUse) {
		super(measureName, dependentDimensions, dimensionsToUse);
		this.useSquaredEuclidean = true;
	}

	public Euclidean(Measure measureName, boolean dependentDimensions, int[] dimensionsToUse, boolean useSquaredEuclidean) {
		super(measureName, dependentDimensions, dimensionsToUse);
		this.useSquaredEuclidean = useSquaredEuclidean;
	}

	//TODO NOTE total <= bsf, if bsf <= 0, < operator will cause problems when early abandoning since total = 0 initially
	@Override
	public double distanceIndep(double[][] series1, double[][] series2, int dimension, double bsf) {
		double[] vector1 = series1[dimension];
		double[] vector2 = series2[dimension];
		double total = 0;
		int minLength = Math.min(vector1.length, vector2.length);
		for (int i = 0; i < minLength & total <= bsf; i++){
			// L2 inside distance, as in dtw, we use parameter pForIndependentDims only to combine dimensions and not inside the measure
			total += (vector1[i] - vector2[i]) * (vector1[i] - vector2[i]);
			// for any Lp-Norm -- DEV ONLY, we always use l2 inside the distance
//			total += Math.pow(Math.abs(vector1[i] - vector2[i]), pForIndependentDims);
		}
		if (useSquaredEuclidean){
			return total;
		}else{
			return Math.sqrt(total);
		}
	}

	// this version assumes that the input arrays store data as series[numDimensions][length]
	@Override
	public double distanceDep(double[][] series1, double[][] series2, double bsf){
		double total = 0;
		int minLength = Math.min(series1[0].length, series2[0].length);
		for (int i = 0; i < minLength & total <= bsf; i++){
			for (int dimension : dimensionsToUse) {
				// L2 inside distance, as in dtw, we use parameter pForDependentDims only to combine dimensions and not inside the measure
				total += (series1[dimension][i] - series2[dimension][i]) * (series1[dimension][i] - series2[dimension][i]);
				// for any Lp-Norm -- DEV ONLY, we always use l2 inside the distance
//				total += Math.pow(Math.abs(series1[dimension][i] - series2[dimension][i]), pForDependentDims);
			}
			// the sqrt of total at the end of summing each dimension cancels out because each point needs to be squared
			// skipped here: sqrt(total)^2
		}
		if (useSquaredEuclidean){
			return total;
		}else{
			return Math.sqrt(total);
		}
	}

	@Override
	public double distanceNaive(double[] series1, double[] series2){
		double total = 0;
		int minLength = Math.min(series1.length, series2.length);
		for (int i = 0; i < minLength; i++){
			total += (series1[i] - series2[i]) * (series1[i] - series2[i]);
		}
		return Math.sqrt(total);
	}

	@Override
	public void setRandomParams(Dataset trainData, Random rand){
		//pass
	}

	@Override
	public void initParamsByID(Dataset trainData, Random rand) {
		//pass
	}

	@Override
	public void setParamsByID(int paramID, int seriesLength, Random rand) {
		//pass
	}

	@Override
	public void setRandomParamByID(Dataset trainData, Random rand) {

	}

	@Override
	public String toString(){
		return "Euclidean[sqEuc="+useSquaredEuclidean+","+super.toString()+",]";
	}

//	// this version assumes that the input arrays store data as series[numDimensions][length]
//	public double distance_dep_transposed(double[][] s, double[][] t, double bsf, int[] dimensionsToUse){
//		double total = 0;
//		int minLength = Math.min(s[0].length, t[0].length);
//		for (int i = 0; i < minLength & total <= bsf; i++){
//			for (int dimension : dimensionsToUse) {
//				total += (s[i][dimension] - t[i][dimension]) * (s[i][dimension] - t[i][dimension]);
//			}
//		}
////		return Math.sqrt(total);
//		return total;
//	}


	//REMOVE
	// this version assumes that the input arrays store data as series[length][numDimensions]
//	public synchronized double distance_indep_transposed(double[][] s, double[][] t, double bsf, int[] dimensionsToUse){
//		double total = 0;
//		int minLength = Math.min(s.length, t.length);
//		for (int dimension : dimensionsToUse) {
//			for (int i = 0; i < minLength & total <= bsf; i++){
//				total += (s[i][dimension] - t[i][dimension]) * (s[i][dimension] - t[i][dimension]);
//			}
//		}
////		return Math.sqrt(total);
//		return total;
//	}

}
