package net.ranides.assira.math;

import net.ranides.assira.collection.lists.IntArrayList;
import net.ranides.assira.collection.lists.IntList;
import org.junit.Ignore;
import org.junit.Test;

import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Random;

import static org.junit.Assert.*;

@Ignore("manual slow")
public class RandomUtilsTest {

    @Test
    public void testStreamPerformance() {
        Random random = new SecureRandom();

        for(int i=0; i<5;i++) {
            timeUniqueStream(random, 100_000, 0, 1_000_000);
            timeUniqueStream(random, 100_000, 0, 200_000);
            timeUniqueStream(random, 100_000, 0, 111_000);
            System.out.println();
        }
        assertTrue(true);
    }

    private void timeUniqueStream(Random random, int count, int start, int end) {
        long dt = System.currentTimeMillis();
        int a = 0;
        for(int i=0; i<10; i++) {
            int[] values = uniqueFromStream(random, count, start, end);
            a += values[0];
        }

        dt = System.currentTimeMillis() - dt;
        System.out.printf("%d time = %.0f%% %d%n", a%10, (100.0*count)/(end-start),dt);
    }

    @Test
    public void testDoubleP() {
        Random rng = new Random();
        for(int i=0; i<50; i++) {
            System.out.println(Arrays.toString(uniqueDoubleP(rng,5, 0, 10)));
        }
    }

    @Test
    public void testKnuth() {
        Random rng = new Random();
        for(int i=0; i<50; i++) {
            System.out.println(RandomUtils.uniqueInts(rng,5, 0, 10));
        }
    }

    @Test
    public void testStream() {
        Random rng = new Random();
        for(int i=0; i<50; i++) {
            System.out.println(Arrays.toString(uniqueFromStream(rng, 10, 5, 1000)));
        }
    }

    public static int[] uniqueDoubleP(Random rng, int count, int start, int end) {

        IntList result = new IntArrayList(count);
        int remaining = end - start;
        for (int i = start; i < end && count > 0; i++) {
            double probability = rng.nextDouble();
            if (probability < ((double) count) / (double) remaining) {
                count--;
                result.push(i);
            }
            remaining--;
        }
        return result.toIntArray();
    }

    private static int[] uniqueFromStream(Random random, int count, int start, int end) {
        return random.ints(start, end).distinct().limit(count).toArray();
    }


}