package net.ranides.assira.math;

import net.ranides.assira.collection.lists.IntArrayList;
import net.ranides.assira.collection.lists.IntList;

import java.util.List;
import java.util.Random;

/**
 * Helper methods for randomizing data.
 */
public class RandomUtils {

    /**
     * Reimplementation of
     * <a href="https://tfetimes.com/wp-content/uploads/2015/04/ProgrammingPearls2nd.pdf">Knuth's algorithm</a>.
     * It generates count distinct numbers from range [begin,end). <br>Complexity: {@code O(count)}.
     *
     * It uses default {@code Random} to get random bits.
     *
     * @param count count
     * @param begin begin
     * @param end end
     * @return numbers
     */
    public static IntList uniqueInts(int count, int begin, int end) {
        return uniqueInts(new Random(), count, begin, end);
    }

    /**
     * Reimplementation of
     * <a href="https://tfetimes.com/wp-content/uploads/2015/04/ProgrammingPearls2nd.pdf">Knuth's algorithm</a>.
     * It generates count distinct numbers from range [begin,end). <br>Complexity: {@code O(count)}.
     *
     * @param rng source of random bits
     * @param count count
     * @param begin begin
     * @param end end
     * @return numbers
     */
    public static IntList uniqueInts(Random rng, int count, int begin, int end) {
        int n = end - begin;
        IntList result = new IntArrayList(count);
        for(int i=0; i<n; i++) {
            if( Long.remainderUnsigned(rng.nextLong(), n-i) < count) {
                result.push(begin + i);
                count--;
            }
        }
        return result;
    }

    /**
     * Choices random element from array.
     *
     * @param rng source of random bits
     * @param values values
     * @param <T> value type
     * @return value
     */
    public static <T> T element(Random rng, T[] values) {
        return values[rng.nextInt(values.length)];
    }

    /**
     * Choices random element from list.
     *
     * @param rng source of random bits
     * @param values values
     * @param <T> value type
     * @return value
     */
    public static <T> T element(Random rng, List<T> values) {
        return values.get(rng.nextInt(values.size()));
    }


}
