package yi.math;

import yi.util.io.*;

/**
 * Utility method on vectors
 */

public class VectorUtil {

  /**
   * Return the Euclidean distance of two double typed vectors
   * @param value1
   * @param value2
   * @return
   */
  public static double EuclideanDistance(double[] value1, double[] value2) {
    double sum = 0;
    for (int i=0; i<value1.length; i++) {
      double d = value1[i] - value2[i];
      sum += d * d;
      if (d==Double.NaN || sum==Double.NaN) {
        System.err.println("value1[i] = " + value1[i] + "value2[i] = " + value2[i]);
      }
    }
    return Math.sqrt(sum);
  }

  public static double squaredEuclideanDistance(double[] value1, double[] value2) {
    double sum = 0;
    for (int i=0; i<value1.length; i++) {
      double d = value1[i] - value2[i];
      sum += d * d;
      if (d==Double.NaN || sum==Double.NaN) {
        System.err.println("value1[i] = " + value1[i] + "value2[i] = " + value2[i]);
      }
    }
    return sum;
  }

  /**
   * Return the Euclidean distance of two int typed vectors
   * @param value1
   * @param value2
   * @return
   */
  public static double EuclideanDistance(int[] value1, int[] value2) {
    double sum = 0;
    for (int i=0; i<value1.length; i++) {
      double d = value1[i] - value2[i];
      sum += d * d;
      if (d==Double.NaN || sum==Double.NaN) {
        System.err.println("value1[i] = " + value1[i] + "value2[i] = " + value2[i]);
      }
    }
    return Math.sqrt(sum);
  }
  /**
   * Return the length of a double vector
   * @param value
   * @return
   */
  public static double lenth(double[] value) {
    double sum = 0;
    for (int i=0; i<value.length; i++) {
      sum += value[i] * value[i];
    }
    return Math.sqrt(sum);
  }


  /**
   * Return whether two double typed vectors are equal
   * @param value1
   * @param value2
   * @return
   */
  public static boolean equal(double[] value1, double[] value2) {
    for (int i=0; i<value1.length; i++) {
      if (value1[i] != value2[i]) return false;
    }
    return true;
  }

  public static double dotProduct(double[] v1, double[] v2) {
    double product = 0;
    for (int i=0; i<v1.length; i++) product += v1[i] * v2[i];
    return product;
  }

  /**
   * Return the scalar projection of a point p to the a vector v
   * @param v
   * @param p
   * @return
   */
  public static double scalarProjection (double[] v, double[] p) {
    return dotProduct(v, p) / lenth(v);
  }

  /**
   * Return the scalar projection of a point p to the a vector v (connecting
   * from v1 to v2
   * @param v
   * @param p
   * @return
   */
  public static double scalarProjection (double[] l1, double[] l2, double[] p) {
    // move the original point to l1
    double[] v = new double[l1.length];
    double[] p_shifted = new double[p.length];
    for (int i=0; i<l1.length; i++) {
      v[i] = l2[i] - l1[i];
      p_shifted[i] = p[i] - l1[i];
    }
    return dotProduct(v, p_shifted) / lenth(v);
  }

  public static double distance(double[] p1, double[] p2) {
    double sum = 0;
    for (int i=0; i<p1.length; i++) {
      double d = p1[i] - p2[i];
      sum += d * d;
    }
    return Math.sqrt(sum);
  }

  /**
   * the distance between a point p and a line (l1 to l2)
   * @param l1
   * @param l2
   * @param p
   * @return
   */
  public static double distance(double[] l1, double[] l2, double[] p) {
    double scalar_proj = scalarProjection (l1, l2, p);
    double dist1 = distance(l1, p);
    double dist_square = dist1 * dist1 - scalar_proj * scalar_proj;
    if (dist_square <0 ) {
      if (dist_square > -1e-5)
        dist_square = 0;
        else {
          System.err.println("Wrong calculation of distance from point ");
         OutputResult.print(System.err, p);
         System.err.println("to line: ");
         OutputResult.print(System.err, l1);
         OutputResult.print(System.err, l2);
        }
    }
    return Math.sqrt(dist_square);
  }

}