/*************************************************************************
 *  Kompilacja:  javac Point2D.java
 *  Wykonanie:    java Point2D N
 *  Zależności: StdOut.java
 *
 *  Niezmienny typ danych dla punktów w przestrzeni.
 *
 *  Klient testowy tworzy N losowych punktów w jednostce kwadratowej i
 *  oblicza minimalną odległość między dwoma z nich (przez atak siłowy).
 *
 *  % java Point2D 100
 *  Odległość minimalna = 0.006187844627988278
 *
 *************************************************************************/

import java.util.Arrays;
import java.util.Comparator;

public class Point2D implements Comparable<Point2D> {
    public static final Comparator<Point2D> X_ORDER = new ByXComparator();
    public static final Comparator<Point2D> Y_ORDER = new ByYComparator();

    public final Comparator<Point2D> DISTANCE_TO_ORDER = new DistanceToOrder();

    private final double x;    // Współrzędna x
    private final double y;    // Współrzędna y

    public Point2D(double x, double y) { this.x = x; this.y = y; }

    // Zwraca współrzędną x punktu
    public double x() { return x; }

    // Zwraca współrzędną y punktu
    public double y() { return y; }

    // Zwracanie promienia dla punktu według współrzędny biegunowych
    public double r() { return Math.sqrt(x*x + y*y); }

    // Zwracanie kąta dla punktu według współrzędnych biegunowych
    // (między -pi/2 a pi/2)
    public double theta() { return Math.atan2(y, x); }

    // Zwracanie odległości euklidesowej między danym punktem a punktem that
    public double distanceTo(Point2D that) {
       double dx = this.x - that.x;
       double dy = this.y - that.y;
       return Math.sqrt(dx*dx + dy*dy);
    }

    // Zwracanie kwadratu odległości między danym punktem a punktem that
    public double distanceSquaredTo(Point2D that) {
       double dx = this.x - that.x;
       double dy = this.y - that.y;
       return dx*dx + dy*dy;
    }

    // Porównywanie według współrzędnej y; równe elementy są porównywane według współrzędnej x
    public int compareTo(Point2D that) {
        if (this.y < that.y) return -1;
        if (this.y > that.y) return +1;
        if (this.x < that.x) return -1;
        if (this.x > that.x) return +1;
        return 0;
    }

    // Porównywanie punktów według współrzędnej x; równe elementy porównywane są według współrzędnej y
    private static class ByXComparator implements Comparator<Point2D> {
        public int compare(Point2D p, Point2D q) {
           if (p.x < q.x) return -1;
           if (p.x > q.x) return +1;
           if (p.y < q.y) return -1;
           if (p.y > q.y) return +1;
           return 0;
        }
    }

    // Porównywanie punktów według współrzędnej y; równe elementy porównywane są według współrzędnej x
    private static class ByYComparator implements Comparator<Point2D> {
        public int compare(Point2D p, Point2D q) {
           if (p.y < q.y) return -1;
           if (p.y > q.y) return +1;
           if (p.x < q.x) return -1;
           if (p.x > q.x) return +1;
           return 0;
        }
    }
 
    // Porównywanie punktów według odległości o danego punktu
    private class DistanceToOrder implements Comparator<Point2D> {
        public int compare(Point2D p, Point2D q) {
            double dist1 = distanceSquaredTo(p);
            double dist2 = distanceSquaredTo(q);
            if      (dist1 < dist2) return -1;
            else if (dist1 > dist2) return +1;
            else                    return  0;
        }
    }


    // Czy dany punkt jest równy y?
    public boolean equals(Object y) {
        if (y == this) return true;
        if (y == null) return false;
        if (y.getClass() != this.getClass()) return false;
        Point2D that = (Point2D) y;
        return this.x == that.x && this.y == that.y;
    }

    // Przekształcanie na łańcuch znaków
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    // Wyświetlanie za pomocą StdDraw
    public void draw() {
        StdDraw.point(x, y);
    }

    // Wyświetlanie linii z danego punktu p do q za pomocą StdDraw
    public void drawTo(Point2D that) {
        StdDraw.line(this.x, this.y, that.x, that.y);
    }


    // Generowanie N losowych punktów w jednostce kwadratowej i wyznaczanie 
    // odległości między parą najbliższych punktów
    public static void main(String[] args) {
        int N = Integer.parseInt(args[0]);

        // Generowanie N losowych punktów w jednostce kwadratowej
        Point2D[] points = new Point2D[N];
        for (int i = 0; i < N; i++) {
            double x = StdRandom.random();
            double y = StdRandom.random();
            points[i] = new Point2D(x, y);
        }

        // Wyznaczanie przez atak siłowy odległości między parą najbliższych punktów 
        // (można użyć metody distanceSquaredTo() w celu poprawy wydajności)
        double min = Double.POSITIVE_INFINITY;
        for (int i = 0; i < N; i++) {
            for (int j = i+1; j < N; j++) {
                double dist = points[i].distanceTo(points[j]);
                if (dist < min) min = dist;
            }
        }

        StdOut.println("Minimalna odległość = " + min);
    }
}
