package de.wwwu.awolf.presenter.util;

import java.security.SecureRandom;
import java.util.Collections;
import java.util.List;

/**
 * Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
 *
 * @Author: Armin Wolf
 * @Email: a_wolf28@uni-muenster.de
 * @Date: 29.06.2017.
 */
public class FastElementSelector {

    /**
     * Liefert das k-te Element aus der Eingabeliste zurück in Theta(n) Zeit.
     *
     * @param a Eingabeliste
     * @param i Rang des gewünschten Elements
     * @return das Element
     */
    public static Double randomizedSelect(List<Double> a, double i) {

        int start = 0;
        int end = a.size() - 1;

        if (i >= end + 1) {
            return a.get(end);
        }

        while (true) {
            if (start >= end) {
                return a.get(start);
            }
            int q = randomizedPartition(a, start, end);
            int k = q - start + 1;

            if (i == k) {
                return a.get(q);
            } else {
                if (i < k) {
                    end = q - 1;
                } else {
                    start = q + 1;
                    i = i - k;
                }
            }
        }
    }

    /**
     * Hilfsmethode
     *
     * @param a     Eingabeliste
     * @param start Startindex
     * @param end   Index des letzten Elements
     * @return Pivotelement
     */
    private static int randomizedPartition(List<Double> a, int start, int end) {
        int i = 0;
        SecureRandom random = new SecureRandom();

        //alternative:  ThreadLocalRandom.current()
        if (start < end) {
            i = start + random.nextInt(end - start);
        } else {
            i = end + random.nextInt(start - end);
        }

        Collections.swap(a, end, i);
        return partition(a, start, end);
    }

    /**
     * Hilfsmethode
     *
     * @param a     Eingabeliste
     * @param start Startindex
     * @param end   Index des letzten Elements
     * @return Pivotelement
     */
    private static int partition(List<Double> a, int start, int end) {
        Double x = a.get(end);
        int i = start - 1;
        for (int j = start; j <= end - 1; j++) {
            if (a.get(j) <= x) {
                i++;
                Collections.swap(a, i, j);
            }
        }
        Collections.swap(a, i + 1, end);
        return i + 1;
    }
}
