package dotnet54.util.math.doubles;

public class VectorsWithFilter {

    public static final double threshold = 1e-10;

    public static double l1Norm(double[] vector, int[] indices) {
        double total = 0;
        double element;
        for (int i : indices) {
            element = vector[i];
            total += (element <= 0.0D) ? 0.0D - element : element;  //Math.abs()
        }
        return total;
    }

    public static double l1Norm(double[][] matrix, int[] dimensions) {
        double total = 0;
        double element;
        for (int i = 0; i < matrix[0].length; i++) {
            for (int dimension : dimensions) {
                element = matrix[dimension][i];
                total += (element <= 0.0D) ? 0.0D - element : element;  //Math.abs()
            }
        }
        return total;
    }

    public static double l2Norm(double[] vector, int[] indices) {
        double total = 0;
        double element;
        for (int i : indices) {
            element = vector[i];
            total += element * element;
        }
        return Math.sqrt(total);
    }

    public static double l2Norm(double[][] matrix, int[] dimensions) {
        double total = 0;
        double element;
        for (int i = 0; i < matrix[0].length; i++) {
            for (int dimension : dimensions) {
                element = matrix[dimension][i];
                total += element * element;
            }
        }
        return Math.sqrt(total);
    }

    public static double l2NormSquared(double[] vector, int[] indices) {
        double total = 0;
        double element;
        for (int i : indices) {
            element = vector[i];
            total += element * element;
        }
        return total;
    }

    public static double l2NormSquared(double[][] matrix, int[] dimensions) {
        double total = 0;
        double element;
        for (int i = 0; i < matrix[0].length; i++) {
            for (int dimension : dimensions) {
                element = matrix[dimension][i];
                total += element * element;
            }
        }
        return total;
    }

    public static double lpNorm(double[] vector, int p, int[] indices) {
        double total = 0;
        double element;
        for (int i : indices) {
            element = vector[i];
            total += Math.pow(element, p);
        }
        return  Math.pow(total, (double) 1/p);
    }

    public static double lpNorm(double[][] matrix, int p, int[] dimensions) {
        double total = 0;
        double element;
        for (int i = 0; i < matrix[0].length; i++) {
            for (int dimension : dimensions) {
                element = matrix[dimension][i];
                total += Math.pow(element, p);
            }
        }
        return  Math.pow(total, (double) 1/p);
    }

    public static double l1Distance(double[] vector1, double[] vector2, int[] indices) {
        double total = 0;
        double diff;
        for (int i = 0; i < indices.length; i++) {
            diff = vector1[indices[i]] - vector2[indices[i]];
            total += (diff <= 0.0D) ? 0.0D - diff : diff;  //Math.abs()
        }
        return total;
    }

    public static double l1Distance(double[][] matrix1, double[][] matrix2, int[] dimensions) {
        double total = 0;
        double diff;
        for (int i = 0; i < matrix1[0].length; i++) {
            for (int dimension : dimensions) {
                diff = matrix1[dimension][i] - matrix2[dimension][i];
                total += (diff <= 0.0D) ? 0.0D - diff : diff;  //Math.abs()
            }
        }
        return total;
    }

    public static double l2Distance(double[] vector1, double[] vector2, int[] indices) {
        double total = 0;
        double diff;
        for (int i = 0; i < indices.length; i++) {
            diff = vector1[indices[i]] - vector2[indices[i]];
            total +=  diff * diff;
        }
        return Math.sqrt(total);
    }

    public static double l2Distance(double[][] matrix1, double[][] matrix2, int[] dimensions) {
        double total = 0;
        double diff;
        for (int i = 0; i < matrix1[0].length; i++) {
            for (int dimension : dimensions) {
                diff = matrix1[dimension][i] - matrix2[dimension][i];
                total +=  diff * diff;
            }
        }
        return Math.sqrt(total);
    }

    public static double l2DistanceSquared(double[] vector1, double[] vector2, int[] indices) {
        double total = 0;
        double diff;
        for (int i = 0; i < indices.length; i++) {
            diff = vector1[indices[i]] - vector2[indices[i]];
            total +=  diff * diff;
        }
        return total;
    }

    public static double l2DistanceSquared(double[][] matrix1, double[][] matrix2, int[] dimensions) {
        double total = 0;
        double diff;
        for (int i = 0; i < matrix1[0].length; i++) {
            for (int dimension : dimensions) {
                diff = matrix1[dimension][i] - matrix2[dimension][i];
                total +=  diff * diff;
            }
        }
        return total;
    }

    public static double lpDistance(double[] vector1, double[] vector2, int p, int[] indices) {
        double total = 0;
        double pow;
        for (int i = 0; i < indices.length; i++) {
            pow =  Math.pow(vector1[indices[i]] - vector2[indices[i]], p);
            total += (pow <= 0.0D) ? 0.0D - pow : pow;  //Math.abs()
        }
        return Math.pow(total,  1.0D /p);
    }

    public static double lpDistance(double[][] matrix1, double[][] matrix2, int p, int[] dimensions) {
        double total = 0;
        double pow;
        for (int i = 0; i < matrix1[0].length; i++) {
            for (int dimension : dimensions) {
                pow = Math.pow(matrix1[dimension][i] - matrix2[dimension][i], p);
                total += (pow <= 0.0D) ? 0.0D - pow : pow;  //Math.abs()
            }
        }
        return Math.pow(total,  1.0D /p);
    }

    public static double dot(double[] vector1, double[] vector2, int[] indices) {
        double total = 0;
        for (int i = 0; i < indices.length; i++) {
            total +=  vector1[indices[i]] * vector2[indices[i]];
        }
        return total;
    }

    public static double dot(double[][] vector1, double[][] vector2, int[] dimensions) {
        double total = 0;
        for (int i = 0; i < vector1[0].length; i++) {
            for (int dimension : dimensions) {
                total +=  vector1[dimension][i] * vector2[dimension][i];
            }
        }
        return total;
    }

    public static double[] add(double[][] vector1, double[][] vector2, int[] dimensions) {
        double total = 0;
        double[] result = new double[vector1.length];
        for (int i = 0; i < vector1.length; i++) {
            for (int dimension : dimensions) {
                result[i] = vector1[dimension][i] + vector2[dimension][i];
            }
        }
        return result;
    }

    public static double[] subtract(double[][] vector1, double[][] vector2, int[] dimensions) {
        double total = 0;
        double[] result = new double[vector1.length];
        for (int i = 0; i < vector1.length; i++) {
            for (int dimension : dimensions) {
                result[i] = vector1[dimension][i] - vector2[dimension][i];
            }
        }
        return result;
    }

    //TODO CHECK
    public static int l1Similarity(double[] vector1, double[] vector2, double epsilon, int[] indices) {
        double total = 0;
        double diff;
        for (int i = 0; i < indices.length; i++) {
            diff = vector1[indices[i]] - vector2[indices[i]];
            total += (diff <= 0.0D) ? 0.0D - diff : diff;  //Math.abs()
        }
        return total <= indices.length * epsilon ? 1: 0;
    }

    //TODO CHECK
    public static int l2Similarity(double[] vector1, double[] vector2, double epsilon, int[] indices) {
        double total = 0;
        double diff;
        for (int i = 0; i < indices.length; i++) {
            diff = vector1[indices[i]] - vector2[indices[i]];
            total +=  diff * diff;
        }
        // if epsilon < 1?
        return total <= indices.length * epsilon * epsilon ? 1: 0;
    }

    //DEV
    public static int isBetween(double left, double mid, double right) {
        if (left <= mid && mid <= right){
            return 1;
        }else{
            return 0;
        }
    }

//    public static int isBetween(double[][] left, double[][] mid, double[][] right, int[] indices) {
//        double[] leftToRight = subtract(left, right, indices);
//        double midProjection = dot(mid, leftToRight, indices);
//
//
//    }

}
