code: package dotnet54.util.math.doubles;
  
  public class TimeSeriesVectorOps {
  
      public static final double threshold = 1e-10;
  
      public static double l1Norm(double[][] series, int[] dimensions) {
          double total = 0;
          double element;
          for (int i = 0; i < series[0].length; i++) {
              for (int dimension : dimensions) {
                  element = series[dimension][i];
                  total += (element <= 0.0D) ? 0.0D - element : element;  //Math.abs()
              }
          }
          return total;
      }
  
      public static double l2Norm(double[][] series, int[] dimensions) {
          double total = 0;
          double element;
          for (int i = 0; i < series[0].length; i++) {
              for (int dimension : dimensions) {
                  element = series[dimension][i];
                  total += element * element;
              }
          }
          return Math.sqrt(total);
      }
  
      public static double l2NormSquared(double[][] series, int[] dimensions) {
          double total = 0;
          double element;
          for (int i = 0; i < series[0].length; i++) {
              for (int dimension : dimensions) {
                  element = series[dimension][i];
                  total += element * element;
              }
          }
          return total;
      }
  
      public static double lpNorm(double[][] series, int p, int[] dimensions) {
          double total = 0;
          double element;
          for (int i = 0; i < series[0].length; i++) {
              for (int dimension : dimensions) {
                  element = series[dimension][i];
                  total += Math.pow(element, p);
              }
          }
          return  Math.pow(total, (double) 1/p);
      }
  
      public static double l1Distance(double[][] series1, double[][] series2, int[] dimensions) {
          double total = 0;
          double diff;
          for (int i = 0; i < series1[0].length; i++) {
              for (int dimension : dimensions) {
                  diff = series1[dimension][i] - series2[dimension][i];
                  total += (diff <= 0.0D) ? 0.0D - diff : diff;  //Math.abs()
              }
          }
          return total;
      }
  
      public static double l2Distance(double[][] series, double[][] series2, int[] dimensions) {
          double total = 0;
          double diff;
          for (int i = 0; i < series[0].length; i++) {
              for (int dimension : dimensions) {
                  diff = series[dimension][i] - series2[dimension][i];
                  total +=  diff * diff;
              }
          }
          return Math.sqrt(total);
      }
  
      public static double l2DistanceSquared(double[][] series1, double[][] series2, int[] dimensions) {
          double total = 0;
          double diff;
          for (int i = 0; i < series1[0].length; i++) {
              for (int dimension : dimensions) {
                  diff = series1[dimension][i] - series2[dimension][i];
                  total +=  diff * diff;
              }
          }
          return total;
      }
  
      public static double lpDistance(double[][] series1, double[][] series2, int p, int[] dimensions) {
          double total = 0;
          double pow;
          for (int i = 0; i < series1[0].length; i++) {
              for (int dimension : dimensions) {
                  pow = Math.pow(series1[dimension][i] - series2[dimension][i], p);
                  total += (pow <= 0.0D) ? 0.0D - pow : pow;  //Math.abs()
              }
          }
          return Math.pow(total,  1.0D /p);
      }
  
      //TODO CHECK
      public static int l1Similarity(double[][] series1, double[][] series2, double epsilon, int[] dimensions) {
          double total = 0;
          double diff;
          for (int i = 0; i < series1.length; i++) {
              for (int dimension : dimensions) {
                  diff = series1[dimension][i] - series2[dimension][i];
                  total += (diff <= 0.0D) ? 0.0D - diff : diff;  //Math.abs()
              }
          }
          return total <= dimensions.length * epsilon ? 1: 0;
      }
  
      //TODO CHECK
      public static int l2Similarity(double[][] series1, double[][] series2, double epsilon, int[] dimensions) {
          double total = 0;
          double diff;
          for (int i = 0; i < series1.length; i++) {
              for (int dimension : dimensions) {
                  diff = series1[dimension][i] - series2[dimension][i];
                  total += diff * diff;
              }
          }
          // if epsilon < 1?
          return total <= dimensions.length * epsilon * epsilon ? 1: 0;
      }
  
      public static double[] add(double[][] series1, int i, double[][] series2, int j, int[] dimensions) {
          double[] result = new double[series1.length];
          for (int dimension : dimensions) {
              result[i] = series1[dimension][i] + series2[dimension][i];
          }
          return result;
      }
  
      public static double[] subtract(double[][] series1, int i, double[][] series2, int j, int[] dimensions) {
          double[] result = new double[series1.length];
          for (int dimension : dimensions) {
              result[dimension] = series1[dimension][i] - series2[dimension][j];
          }
          return result;
      }
  
      public static double dot(double[][] series1, double[][] series2, int[] dimensions) {
          double total = 0;
          for (int i = 0; i < series1[0].length; i++) {
              for (int dimension : dimensions) {
                  total +=  series1[dimension][i] * series2[dimension][i];
              }
          }
          return total;
      }
  
  
      public static double project(double[][] series1, int point, double[] direction, int[] dimensions) {
          double total = 0;
          for (int dimension : dimensions) {
              total +=  series1[dimension][point] * direction[dimension];
          }
          return total;
      }
  
      //TODO CHECK
      public static boolean isBetween(double[][] series1, double[][] series2, int left, int mid, int right, int[] dimensions) {
          double[] directionVector = subtract(series1, left, series2, right, dimensions);
          double[] unitVector = Vectors.div(directionVector, Vectors.l2Norm(directionVector));
          double midOnLeftProjection = project(series1, mid, unitVector, dimensions);
          double midOnRightProjection = project(series2, mid, unitVector, dimensions);
          if (midOnLeftProjection > 0 && midOnRightProjection < 0){
              return true;
          }else{
              return false;
          }
  
      }
  
  }
  