package DDMT.Nodes;

/*
 * DataPoint.java
 *
 * Created on 21:46, August 1st, 2004
 */

/** Data point class.
 * @author Kun Liu
 * @version 1.1
 */

import java.lang.*;
import java.io.*;
import java.math.*;
import java.util.*;

public class DataPoint
{
    // data point array
    public double[] DataArray;

    // cluster membership, i.e. 1, 2, 3, ..., k, default is -1
    public int membership;

    /** Constructor
     *  @param DataArray data point array
     */
    public DataPoint(double [] DataArray)
    {
        this.DataArray = new double [DataArray.length];
        System.arraycopy(DataArray, 0, this.DataArray, 0, DataArray.length);
        this.membership = -1;
    }
    
    public DataPoint(int size)
    {
        DataArray = new double[size];
    }
    public DataPoint(DataPoint dp)
    {
        double tmparray [] = dp.getDataArray();
        this.DataArray = new double [tmparray.length];
        System.arraycopy(tmparray, 0, this.DataArray, 0, tmparray.length);
        this.membership = dp.getMembership();
    }

    /** Setup the membership of the point*/
    public void setMembership(int Membership){
        this.membership = Membership;
    }

    /** Get a COPY of the data point array */
    public double [] getDataArray(){

              double tmparray [] = new double[this.DataArray.length];
              System.arraycopy(this.DataArray, 0, tmparray, 0, this.DataArray.length);
        return tmparray;
    }

    /** Get the membership number of the point*/
    public int getMembership(){
        return this.membership;
    }

    /** Print data point**/
    public void print(){

              for (int i=0;i<this.DataArray.length;i++){
                      System.out.print(this.DataArray[i]+" ");
              }
              System.out.print(this.membership+" \n");

    }

    /**Compute the Euclidean distance between two data points**/
    public static double euclideanDist(DataPoint p1, DataPoint p2){
        double [] p1array = p1.getDataArray();
        double [] p2array = p2.getDataArray();
        if(p1array.length != p2array.length){
            System.out.println("Dimension not match!");
            return 0;
        }
        double dist = 0;
        for (int i=0;i<p1array.length;i++){
            dist = dist + (p1array[i]-p2array[i])*(p1array[i]-p2array[i]);
        }
        dist =  Math.sqrt(dist);
        return dist;

    }

    /** Compute the sum of two data points, and return a new data point**/
    public static DataPoint sum(DataPoint p1, DataPoint p2){
        double [] p1array = p1.getDataArray();
        double [] p2array = p2.getDataArray();
        if(p1array.length != p2array.length){
            System.out.println("Dimension not match!");
            return null;
        }
        for (int i=0; i<p1array.length;i++){
            p1array[i] = p1array[i] + p2array[i];
        }
        DataPoint tmp = new DataPoint(p1array);
        return tmp;

    }

        /** Compute the sum of one data points with all constants, and return a new data point**/
    public static DataPoint sum(DataPoint p1, double scalar){

        double [] p1array = p1.getDataArray();

        for (int i=0; i<p1array.length;i++){
            p1array[i] = p1array[i] + scalar;
        }

        DataPoint tmp = new DataPoint(p1array);

        return tmp;

    }
    /** Compute the difference of two data points, and return a new point**/
    public static DataPoint minus(DataPoint p1, DataPoint p2){
        double [] p1array = p1.getDataArray();
        double [] p2array = p2.getDataArray();
        if(p1array.length != p2array.length){
            System.out.println("Dimension not match!");
            return null;
        }
        for (int i=0; i<p1array.length;i++){
            p1array[i] = p1array[i] - p2array[i];
        }
        DataPoint tmp = new DataPoint(p1array);
        return tmp;

    }

    /** Divide a data point by a scalar, and return a new point **/
    public static DataPoint divideScalar(DataPoint p1, double scalar){
        double [] p1array = p1.getDataArray();
        for (int i=0; i<p1array.length;i++){
            p1array[i] = p1array[i]/scalar ;
        }
        DataPoint tmp = new DataPoint(p1array);
        return tmp;
    }

    /** Multiply a data point by a scalar, and return a new point **/
    public static DataPoint multiplyScalar(DataPoint p1, double scalar){
        double [] p1array = p1.getDataArray();
        for (int i=0; i<p1array.length;i++){
            p1array[i] = p1array[i]*scalar ;
        }
        DataPoint tmp = new DataPoint(p1array);
        return tmp;
    }


    /** Norm square of a data point **/
    public static double normSquare(DataPoint p1){
        double [] p1array = p1.getDataArray();
        double square = 0;

        for (int i=0; i<p1array.length;i++){
            square += p1array[i]*p1array[i];
        }
        return square;
    }


    /** Main function -- for test*/
    public static void main(String args[]){

        double [] p1 = {1.1, 2.2, 3.3, 4.4};
        double [] p2 = {5.5, 6.6, 7.7, 8.8};

        // instantiate DataPoint class
        DataPoint dp1 = new DataPoint(p1);
        DataPoint dp2 = new DataPoint(p2);
        // print data points
        dp1.print();
        dp2.print();

        ////////////////
        // this block shows that arraycopy does not make a real copy of the object
        // changing the membership of the point from one array also changing that from the other array
        dp1.setMembership(1);
        dp2.setMembership(2);
        DataPoint [] dparray1= {dp1,dp2};
        DataPoint [] dparray2 = new DataPoint[dparray1.length];

        System.arraycopy(dparray1, 0, dparray2, 0, dparray1.length);

        System.out.println("dparray1[0] membership:" +dparray1[0].getMembership());
        System.out.println("dparray1[1] membership:" +dparray1[1].getMembership());
        System.out.println("dparray2[0] membership:" +dparray2[0].getMembership());
        System.out.println("dparray2[1] membership:" +dparray2[1].getMembership());

        dparray2[0].setMembership(3);
        dparray2[1].setMembership(4);

        System.out.println("dparray1[0] membership:" +dparray1[0].getMembership());
        System.out.println("dparray1[1] membership:" +dparray1[1].getMembership());
        System.out.println("dparray2[0] membership:" +dparray2[0].getMembership());
        System.out.println("dparray2[1] membership:" +dparray2[1].getMembership());


        ////////////////

        // Euclidean distance
        double dist = DataPoint.euclideanDist(dp1,dp2);
        System.out.println("Euclidean dist is: " + dist);

        // sum
        DataPoint sum = DataPoint.sum(dp1,dp2);
        System.out.print("Sum of two data points: " );
        sum.print();

        // minus
        DataPoint minus = DataPoint.minus(dp1,dp2);
        System.out.print("Minus of two data points: ");
        minus.print();

        // divide
        DataPoint divideScalar = DataPoint.divideScalar(dp1,2);
        System.out.print("Divide scalar (dp1) by 2: ");
        divideScalar.print();

        // multiply
        DataPoint multiplyScalar = DataPoint.multiplyScalar(dp2,3);
        System.out.print("Multiply scalar (dp2) by 3: ");
        multiplyScalar.print();

        // norm square
        double square1 = DataPoint.normSquare(dp1);
        double square2 = DataPoint.normSquare(dp2);
        System.out.println("norm square 1: " + square1);
        System.out.println("norm square 2: " + square2);

        // print data points again to see if they have changed
        dp1.print();
        dp2.print();

        // test copy constructor
        dp2 = new DataPoint(dp1);
        dp1.print();
        dp2.print();
        dp1.setMembership(5);
        System.out.println("dp1 membership:" +dp1.getMembership());
        dp2.setMembership(6);
        System.out.println("dp1 membership:" +dp1.getMembership());
        System.out.println("dp2 membership:" +dp2.getMembership());

    }
}
